// --------------------------------------
// Lua_Kirby
// Kirby's main abilities
// --------------------------------------
// BUGS:
// Crusher immunity doesn't work right... (Fixed maybe?)
// Tutorial controls (this seems unfixable without lame hacks...)
// --------------------------------------
local kirbydebug = false
local sucklength = 10
local suck1time = TICRATE*18/35
local suck2time = TICRATE*10/35
local suck1time4 = TICRATE*18/35
local suck2time4 = TICRATE*10/35
local suck1timed = TICRATE*32/35
local suck2timed = TICRATE*10/35
local deathtimer = TICRATE
local roulettespins = 59
local spinstoslow = 40
local slowdownadd = 1
local escapelimit = 6

local duckbutton = BT_CUSTOM2
local tossbutton = BT_TOSSFLAG

rawset(_G, "AF_NOFLOAT", 1)		// Disable floating
rawset(_G, "AF_NODUCK", 2)		// Disable ducking
rawset(_G, "AF_NOJUMP", 4)		// Disable jumping
rawset(_G, "AF_NOSWIM", 8)		// Disable swimming
rawset(_G, "AF_NODROP", 16)		// Disable dropping the ability
rawset(_G, "AF_NOLOSE", 32)		// Disable losing this ability from damage or death
rawset(_G, "AF_ALLOWSUCK", 64)	// Allows Kirby to suck
rawset(_G, "AF_WALKSWIM", 128)	// Use walking for swimming always

local kirbykillsflickies = false	// If false, flickies are added to Kirby's inhaled object table, otherwise they die

local fatspritetable = {}
fatspritetable[SPR2_STND] = SPR2_FSTN
fatspritetable[SPR2_WALK] = SPR2_FWLK
fatspritetable[SPR2_RUN_] = SPR2_FWLK
fatspritetable[SPR2_PAIN] = SPR2_FOWW
fatspritetable[SPR2_SPNG] = SPR2_FSPR
fatspritetable[SPR2_FALL] = SPR2_FFAL
fatspritetable[SPR2_RIDE] = SPR2_FRID
local fatrestoretable = {}
fatrestoretable[SPR2_FSTN] = SPR2_STND
fatrestoretable[SPR2_FWLK] = SPR2_WALK
//fatrestoretable[SPR2_FRUN] = SPR2_RUN_
fatrestoretable[SPR2_FOWW] = SPR2_PAIN
fatrestoretable[SPR2_FSPR] = SPR2_SPNG
fatrestoretable[SPR2_FFAL] = SPR2_FALL
fatrestoretable[SPR2_FRID] = SPR2_RIDE

// Pointers to ability functions
rawset(_G, "numabilities", 16)
local abilities = {}
abilities[0] = K_FireBall
abilities[1] = K_Ice
abilities[2] = K_Stone
abilities[3] = K_Bomb
abilities[4] = K_Spark
abilities[5] = K_Needle
abilities[6] = K_Tornado
abilities[7] = K_Wheel
abilities[8] = K_Ball
abilities[9] = K_Sword
abilities[10] = K_Hammer
abilities[11] = K_Mike		// Yes, these are all mike
abilities[12] = K_Mike		// Each one is a different usage count left
abilities[13] = K_Mike
abilities[14] = K_Sleep
abilities[15] = K_UFO
rawset(_G, "abilityicons", {})

// Same as before, but for secret abilities!
rawset(_G, "numsecretabilities", 9)
local secretabilities = {}
// ----------------------------------
// Orange Juices
secretabilities[0] = K_QPShu
secretabilities[1] = K_Suguri
secretabilities[2] = K_Idol
// ----------------------------------
secretabilities[3] = K_Gun
secretabilities[4] = K_OwTheEdge
secretabilities[5] = K_OhNo
secretabilities[6] = K_Crash
secretabilities[7] = K_Yahoo
secretabilities[8] = K_Legacy
rawset(_G, "secretabilityicons", {})

// Custom abilities!
rawset(_G, "K_AddAbility", function(abilityfunction, secret, iconname)
	if not(secret)
		abilities[numabilities] = abilityfunction
		numabilities = $1 + 1
		abilityicons[numabilities] = iconname
		return numabilities
	else
		secretabilities[numsecretabilities] = abilityfunction
		secretabilityicons[numsecretabilities] = iconname
		numsecretabilities = $1 + 1
		return numsecretabilities
	end
end)

local maxfloattime = TICRATE*4
local ablflashtime = TICRATE*7/10

// Dash sparkles
local sparklecolors = {}
sparklecolors[0] = SKINCOLOR_PINK
sparklecolors[1] = SKINCOLOR_MOSS
sparklecolors[2] = SKINCOLOR_AZURE

local function RandomOff(num)
	return (P_RandomByte()%num)-(num/2)
end
local function SpawnSparkles(mobj, spriteheight, star)
	local stype = MT_BALLSPARKLE
//	if(star)
//		stype = MT_KIRBY_STARPARTICLE
//	end
	local zoff = 8*mobj.scale*P_MobjFlip(mobj)
	local sparkle = P_SpawnMobj(mobj.x+(RandomOff(30)*mobj.scale), mobj.y+(RandomOff(30)*mobj.scale), mobj.z+(mobj.height/2)+(RandomOff(spriteheight)*mobj.scale)+zoff, stype)
	sparkle.scale = mobj.scale
	if(mobj.eflags & MFE_VERTICALFLIP)
		sparkle.eflags = $1|MFE_VERTICALFLIP
	end
	if(star)
		if(P_RandomByte()&1)					// 50%
			sparkle.color = SKINCOLOR_GOLD
		elseif(P_RandomByte()&1)				// 25%
			sparkle.color = SKINCOLOR_SUNSET
		else									// 25%
			sparkle.color = SKINCOLOR_YELLOW
		end
	else
		sparkle.color = sparklecolors[P_RandomByte()%3]
	end
end

// Fixes mobj after kirby suck
local function fixMobj(mobj)
	mobj.flags = mobj.oldflags
end

local function resetKirbyVars(player, dontResetAbility)
	if(player.kvars == nil)
		player.kvars = {}
	end
	player.kvars.prevcmd = player.cmd.buttons
	player.kvars.ducking = false
	player.kvars.floating = false
	player.kvars.floatingtimer = 0
	player.kvars.usedjumps = 0
	if not(dontResetAbility)
		or(player.kvars.ability == nil)
		player.kvars.ability = 0
		player.kvars.secretability = false		// If true, use the secret ability list rather than the normal one
	end
	if(player.kvars.abljingletime == nil)
		player.kvars.abljingletime = 0			// Handled by the abilities themselves, does not reset
	end
	player.kvars.ablstate = 0
	player.kvars.ablvar1 = 0
	player.kvars.ablvar2 = 0
	player.kvars.ablvar3 = 0
	player.kvars.suckstate = 0
	player.kvars.sucktimer = 0				// Used for the sound effect
	player.kvars.inhalesleft = 0			// Number of objects left to inhale
	player.kvars.suckedmultiple = false		// If true, then spit a big star!
	player.kvars.numsuck = 0				// Number of sucks each frame. If this reaches the number of suck objects, then Kirby is forced to not suck!
	player.kvars.toability = 0				// Ability to be gained from the inhaled object
	player.kvars.tosecret = 0				// If ability is secret
	player.kvars.badtime = 0				// Counts down to 0. Used for the hud
	player.kvars.eattime = 0				// Ditto
	player.kvars.itemtime = 0				// Monitors
	player.kvars.itsnouse = 0				// ITS NO USE (also counts down)
	player.kvars.flashtime = 0
	player.kvars.rouletteability = 0
	player.kvars.roulette = 0
	player.kvars.roulettespeed = 0
	player.kvars.specialroulette = false	// If true, current ability is a secret one!
	player.kvars.deathtimer = 0
	player.kvars.fastswimtime = 0
	player.kvars.prevfoward = player.cmd.forwardmove
	player.kvars.prevground = true
	player.kvars.viewztween = 0
	player.kvars.prevsuper = 0
	player.kvars.prevcarry = player.powers[pw_carry]
	// FAT HACK
	player.kvars.fatwalkframe = 0
	
	if not(player.kvars.numsuckobjs == nil)
		local snum = 0
		while(snum < player.kvars.numsuckobjs)
			if(player.kvars.suckobjs[snum] and player.kvars.suckobjs[snum].valid)
				fixMobj(player.kvars.suckobjs[snum])
				player.kvars.suckobjs[snum].flags2 = $1 & !MF2_DONTDRAW
				player.kvars.suckobjs[snum].issucked = 0
				player.kvars.suckobjs[snum].beingsucked = false
				player.kvars.suckobjs[snum].kirbyescapenum = nil
				player.kvars.suckobjs[snum].kirbyescapeprev = nil
			end
			player.kvars.suckobjs[snum] = nil
			snum = $1 + 1
		end
	end
	player.kvars.suckobjs = {}				// Objects we've sucked (mostly players)
	player.kvars.numsuckobjs = 0
	
	// Monitor contents :eyes:
	if not(player.kvars.numsuckitems == nil)
		local snum = 0
		while(snum < player.kvars.numsuckitems)
			player.kvars.suckitems[snum] = nil
			snum = $1 + 1
		end
	end
	player.kvars.suckitems = {}
	player.kvars.numsuckitems = 0
	
	// RNG when getting an ability
	player.kvars.abilityrandom = P_RandomByte()
	
	player.kvars.industdevil = false
	player.kvars.inmud = false
	player.kvars.squish = false
	
	// Transform
	player.kvars.superstars = {}
	player.kvars.supertranstime = 0
	player.kvars.supertransspeed = 0
	player.kvars.supertransradius = FRACUNIT*30
	player.kvars.supertransflash = 0
end
local CheckMud

// Reset on spawn
addHook("PlayerSpawn", function(player)
	resetKirbyVars(player, true)
end)

local function buttonPressed(player, button)
	if(player.cmd.buttons & button)
		and not(player.kvars.prevcmd & button)
		return true
	end
	return false
end

local function lockControl(player)
	if(player.powers[pw_nocontrol] <= 2)
		player.powers[pw_nocontrol] = 2
	end
	player.pflags = $1|PF_STASIS
end

local function spitEatenMobjs(player, spittype)
	local snum = 0
	while(snum < player.kvars.numsuckobjs)
		if(player.kvars.suckobjs[snum] and player.kvars.suckobjs[snum].valid)
		//	P_TeleportMove(player.kvars.suckobjs[snum], player.mo.x, player.mo.y, player.mo.z)
			if(spittype == 0)
				player.kvars.suckobjs[snum].momx = 0
				player.kvars.suckobjs[snum].momy = 0
				player.kvars.suckobjs[snum].momz = 0
			elseif(spittype == 1)
				P_InstaThrust(player.kvars.suckobjs[snum], player.drawangle, FixedMul(mobjinfo[MT_KIRBYSPITSTAR].speed, player.mo.scale))
				player.kvars.suckobjs[snum].momz = P_MobjFlip(player.kvars.suckobjs[snum])
				if(player.kvars.suckobjs[snum].type >= MT_FLICKY_01 and player.kvars.suckobjs[snum].type <= MT_SEED)
					player.kvars.suckobjs[snum].momx = $1 / 2
					player.kvars.suckobjs[snum].momy = $1 / 2
				end
			elseif(spittype == 2)
				P_InstaThrust(player.kvars.suckobjs[snum], player.drawangle, -5*player.mo.scale)
				player.kvars.suckobjs[snum].momz = 5*player.mo.scale*P_MobjFlip(player.mo)
			end
			P_TeleportMove(player.kvars.suckobjs[snum], player.mo.x, player.mo.y, player.mo.z)
			P_TryMove(player.kvars.suckobjs[snum], player.mo.x+player.kvars.suckobjs[snum].momx, player.mo.y+player.kvars.suckobjs[snum].momy, true)
			// Visible!
			player.kvars.suckobjs[snum].flags2 = $1 & !MF2_DONTDRAW
			player.kvars.suckobjs[snum].issucked = 0
			if(player.kvars.suckobjs[snum].type == MT_PLAYER)
				player.kvars.suckobjs[snum].state = S_PLAY_PAIN
				player.kvars.suckobjs[snum].player.drawangle = player.drawangle
			else
				player.kvars.suckobjs[snum].angle = player.drawangle
			end
			fixMobj(player.kvars.suckobjs[snum])
			player.kvars.suckobjs[snum].beingsucked = false
			// AA
			player.kvars.suckobjs[snum].kirbyescapenum = nil
			player.kvars.suckobjs[snum].kirbyescapeprev = nil
			player.kvars.suckobjs[snum] = nil
		end
		snum = $1 + 1
	end
	player.kvars.numsuckobjs = 0
	// Piggybacking off of spitEatenMobjs becaue I'm lazy
	if(spittype == 2)
		snum = 0
		while(snum < player.kvars.numsuckitems)
			local btyp = player.kvars.suckitems[snum]
			local item = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z+(player.mo.height/2)-FixedMul(mobjinfo[btyp].height/2, player.mo.scale), btyp)
			if(player.mo.eflags & MFE_VERTICALFLIP)
				item.eflags = $1|MFE_VERTICALFLIP
				item.flags2 = $1|MF2_OBJECTFLIP
			end
			item.scale = player.mo.scale
			item.target = player.mo
			player.kvars.suckitems[snum] = nil
			if(player.kvars.badtime)
				player.kvars.itemtime = player.kvars.badtime
			elseif(player.kvars.itsnouse)
				player.kvars.itemtime = player.kvars.itsnouse
			end
			player.kvars.badtime = 0
			player.kvars.itsnouse = 0
			snum = $1 + 1
		end
		player.kvars.numsuckitems = 0
	else
		snum = 0
		while(snum < player.kvars.numsuckitems)
			player.kvars.suckitems[snum] = nil
			snum = $1 + 1
		end
		player.kvars.numsuckitems = 0
	end
end

local function oopsAllFlickies(player)
	local snum = 0
	while(snum < player.kvars.numsuckobjs)
		if(player.kvars.suckobjs[snum] and player.kvars.suckobjs[snum].valid)
			and not(player.kvars.suckobjs[snum].type >= MT_FLICKY_01 and player.kvars.suckobjs[snum].type <= MT_SEED)
			return false
		end
		snum = $1 + 1
	end
	return true
end

local function tickRoulette(player, reset, force)
	player.kvars.abilityrandom = P_RandomByte()
	if not(reset)
		or(force)
		player.kvars.roulette = $1 - 1
		if(roulettespins - player.kvars.roulette > spinstoslow)
			player.kvars.roulettespeed = (((roulettespins - player.kvars.roulette)-spinstoslow)*slowdownadd)+1
		else
			player.kvars.roulettespeed = 1
		end
		
		if(player.kvars.roulette == 0)
			or(force)
			// Give ability
			player.kvars.ability = player.kvars.rouletteability
			player.kvars.secretability = player.kvars.specialroulette
			player.kvars.ablstate = 0
			player.kvars.ablvar1 = 0
			player.kvars.ablvar2 = 0
			player.kvars.ablvar3 = 0
			player.kvars.suckstate = 0
			spitEatenMobjs(player, 2)
			S_StartSound(player.mo, sfx_ablg)
			player.kvars.eattime = (TICRATE*3/2)-2
			player.kvars.badtime = 0
			player.kvars.itemtime = 0
			player.kvars.itsnouse = 0
			player.kvars.flashtime = ablflashtime
			player.kvars.abljingletime = 0
			player.mo.state = S_KIRBY_TRANSFORM1
			player.mo.frame = 0
			return
		end
		S_StartSound(nil, sfx_menu1, player)
	else
		player.kvars.roulette = roulettespins
		player.kvars.roulettespeed = 1
	end
	
	// Set random ability
	local oldability = player.kvars.rouletteability
	local oldsecret = player.kvars.specialroulette
	local aaaaaa = false
	if(P_RandomByte() == 0)				// 1 in 256 chance to get a secret ability
		player.kvars.specialroulette = true
		while((oldsecret)and(oldability == player.kvars.rouletteability))
			or not(aaaaaa)
			player.kvars.rouletteability = (P_RandomByte()%numsecretabilities)+1
			aaaaaa = true
		end
	else
		player.kvars.specialroulette = false
		while(not(oldsecret)and(oldability == player.kvars.rouletteability))
			or not(aaaaaa)
			player.kvars.rouletteability = (P_RandomByte()%(numabilities-2))+1
			if(player.kvars.rouletteability >= 13)
				player.kvars.rouletteability = $1 + 2
			end
			aaaaaa = true
		end
	end
end

// Debug command
if(kirbydebug)
	COM_AddCommand("ability", function(player, ability, secret)
		if(multiplayer)
			return
		end
		if(player.kvars == nil)
			return
		end
		if(player.mo and player.mo.valid)
			and not(player.mo.skin == "kirby")
			return
		end
		local abil = tonumber(ability)
		if(abil == nil)
			return
		end
		if(abil < -1)
			return
		end
		if(secret)
			if(abil > numsecretabilities)
				return
			end
		else
			if(abil > numabilities)
				return
			end
		end
		if(abil == -1)	// Roulette
			player.kvars.rouletteability = -1	// Start roulette
		else
			player.kvars.ability = abil
		end
		if(secret)
			player.kvars.secretability = true
		else
			player.kvars.secretability = false
		end
		player.kvars.ablstate = 0
		player.kvars.ablvar1 = 0
		player.kvars.ablvar2 = 0
		player.kvars.ablvar3 = 0
		player.kvars.suckstate = 0
		spitEatenMobjs(player, 2)
		S_StartSound(player.mo, sfx_ablg)
		player.kvars.eattime = TICRATE*3/2
		player.kvars.badtime = 0
		player.kvars.itemtime = 0
		player.kvars.itsnouse = 0
		player.kvars.flashtime = ablflashtime
		player.kvars.abljingletime = 0
		player.kvars.abilityrandom = P_RandomByte()
	end)
end

local function callAbilityFunc(player, forvalue, destroy, late)
	if(player.powers[pw_carry] == CR_NIGHTSMODE)
		player.kvars.ablstate = 0
		player.kvars.ablvar1 = 0
		player.kvars.ablvar2 = 0
		player.kvars.ablvar3 = 0
		player.kvars.suckstate = 0
		return 0
	end
	if not(player.kvars.ability)
		return 0
	end
	if(player.kvars.secretability)
		return secretabilities[player.kvars.ability-1](forvalue, destroy, late, player)
	end
	return abilities[player.kvars.ability-1](forvalue, destroy, late, player)
end

local function squishedCheck(player)
	local height = FixedMul(player.height, player.mo.scale)
	if(player.mo.ceilingz - player.mo.floorz < FixedMul(height, player.mo.scale))
		return true
	end
	return false
end

local function discardAbility(player)
	callAbilityFunc(player, false, true, false)
	S_StartSound(player.mo, sfx_ablt)
	local star = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_KIRBYSTARDROP)
	star.scale = player.mo.scale
	if(player.mo.eflags & MFE_VERTICALFLIP)
		P_TeleportMove(star, player.mo.x, player.mo.y, player.mo.z+player.mo.height-star.height)
		star.eflags = $1|MFE_VERTICALFLIP
		star.flags2 = $1|MF2_OBJECTFLIP
	end
	star.abilitytogive = player.kvars.ability
	star.secret = player.kvars.secretability
	P_InstaThrust(star, player.mo.angle, -10*star.scale)
	P_SetObjectMomZ(star, 10*FRACUNIT)
	player.kvars.ability = 0
	if not(player.powers[pw_invulnerability])
		and not(player.powers[pw_sneakers])
		P_RestoreMusic(player)
	end
	player.kvars.eattime = TICRATE*3/2
	player.kvars.badtime = 0
	player.kvars.itemtime = 0
	player.kvars.itsnouse = 0
	player.mo.color = player.skincolor
	player.kvars.ablstate = 0	// Failsafe
	player.kvars.ablvar1 = 0
	player.kvars.ablvar2 = 0
	player.kvars.ablvar3 = 0
	player.kvars.suckstate = 0
	spitEatenMobjs(player, 2)
end

addHook("PlayerThink", function(player)
	if(player.mo and player.mo.valid)
		and(player.mo.skin == "kirby")
		// Crusher immunity!... hopefully!
		if(squishedCheck(player))
			or(player.kvars.roulette)
			player.mo.flags = $1 & !MF_SHOOTABLE
			player.kvars.squish = true
		elseif(player.kvars.squish)
			player.kvars.squish = false
			player.mo.flags = $1|MF_SHOOTABLE
		end
		if(player.powers[pw_super])
			or(player.mo.state >= S_PLAY_NIGHTS_TRANS1 and player.mo.state <= S_PLAY_NIGHTS_TRANS5)
			player.mo.color = player.skincolor	// We don't want to super flash
		end
	end
end)

addHook("ThinkFrame", function()
	for player in players.iterate
		// Init Kirby's variables (actually already done, but eh)
		if(player.kvars == nil)
			or not(player.mo and player.mo.valid and(player.mo.skin == "kirby"))
			resetKirbyVars(player, false)
		end
		
		// Not Kirby?
		if not(player.mo and player.mo.valid and(player.mo.skin == "kirby"))
			continue
		end
		
		if(player.mo and player.mo.valid and player.mo.issucked)
			continue
		end
		
		// 2D variable
		local twod = false
		if(player.mo.flags2 & MF2_TWOD)
			or(twodlevel)
			or(player.mo.currentaxis)
			twod = true
		end
		
		player.normalspeed = skins[player.mo.skin].normalspeed
		player.thrustfactor = skins[player.mo.skin].thrustfactor
		player.charflags = skins[player.mo.skin].flags
		
		// Important
		if(P_PlayerInPain(player) or player.mo.health <= 0)
			and(player.kvars.flashtime)
			player.kvars.flashtime = 0
		end
		player.kvars.inmud = CheckMud(player)	// Check mud
		
		// --------------------------------
		// Death!
		// --------------------------------
		if(player.deadtimer)
			and(leveltime & 1)
			and(player.deadtimer <= 70*TICRATE/35)
			player.deadtimer = $1 - 1
		end
		if(player.kvars.deathtimer)
			player.kvars.deathtimer = $1 - 1
			player.mo.momx = 0
			player.mo.momy = 0
			// Copied directly from source, this will be 100% accurate!
			if(player.mo.movedir == DMG_DROWNED)
				P_SetObjectMomZ(player.mo, FRACUNIT / 2) // slower fall from drowning
			else
				P_SetObjectMomZ(player.mo, 2 * FRACUNIT / 3)
			end
			if not(player.kvars.deathtimer)
				player.mo.state = S_KIRBY_DEATHTUMBLE
				S_StartSound(player.mo, sfx_kdiet)
				P_SetObjectMomZ(player.mo, 15*FRACUNIT)
			end
		end
		
		// Can't do anything else if we're dead!
		if not(player.mo.health)
			continue
		end
		
		local oldbutt = player.cmd.buttons
		if(player.powers[pw_super])
			player.charflags = $1|SF_SUPER
		elseif(player.cmd.buttons & BT_TOSSFLAG)
			and(P_SuperReady(player))
			and not(player.kvars.prevcmd & BT_USE)	// A
			and not(player.kvars.ability)
			player.cmd.buttons = $1 & !BT_USE	// Allow transform
			player.charflags = $1|SF_SUPER
		else
			player.charflags = $1 & !SF_SUPER
		end
		
		local starship = false
		if(player.powers[pw_super])
			starship = true
		end
		
		player.jumpfactor = skins[player.mo.skin].jumpfactor
		player.height = skins[player.mo.skin].height
		player.shieldscale = skins[player.mo.skin].shieldscale
		player.charability = skins[player.mo.skin].ability
		if(player.powers[pw_carry] == CR_NIGHTSMODE)
			player.mo.height = FixedMul(player.height, player.mo.scale)
		end
		if(player.mo.eflags & MFE_UNDERWATER)
			and not(starship)	// Starship can't swim
			and not(player.kvars.inmud)
			player.jumpfactor = 0
		end
		
		// Super transformation animation
		if(player.mo.state >= S_PLAY_SUPER_TRANS1 and player.mo.state <= S_PLAY_SUPER_TRANS6)
			local star = 0
			local starangle = FixedAngle(player.kvars.supertranstime)
			local radius = FixedMul(player.mo.scale, player.kvars.supertransradius)
			while(star < 7)
				local tox = player.mo.x+FixedMul(cos(player.mo.angle+starangle), radius)
				local toy = player.mo.y+FixedMul(sin(player.mo.angle+starangle), radius)
				local toz = player.mo.z+(player.mo.height/2)-(FixedMul(mobjinfo[MT_KIRBY_STARSHIPSTAR].height, player.mo.scale)/2)
				if not(player.kvars.superstars[star] and player.kvars.superstars[star].valid)
					player.kvars.superstars[star] = P_SpawnMobj(tox, toy, toz, MT_KIRBY_STARSHIPSTAR)
				else
					P_TeleportMove(player.kvars.superstars[star], tox, toy, toz)
				end
				player.kvars.superstars[star].scale = player.mo.scale
				if(player.mo.eflags & MFE_VERTICALFLIP)
					player.kvars.superstars[star].eflags = $1|MFE_VERTICALFLIP
				else
					player.kvars.superstars[star].eflags = $1 & !MFE_VERTICALFLIP
				end
				player.kvars.superstars[star].frame = star+1
				P_SpawnGhostMobj(player.kvars.superstars[star])
				star = $1 + 1
				starangle = $1 + FixedAngle(360*FRACUNIT/7)
			end
			player.kvars.supertransspeed = $1 + (FRACUNIT*3/2)
			player.kvars.supertranstime = $1 + player.kvars.supertransspeed
			if(player.kvars.supertransspeed > FRACUNIT*37)
				player.kvars.supertransradius = $1 - (FRACUNIT*3)
				if(player.kvars.supertransradius < 0)
					player.kvars.supertransradius = 0
				end
			end
		else
			player.kvars.supertranstime = 0
			player.kvars.supertransspeed = 0
			player.kvars.supertransradius = FRACUNIT*30
			local star = 0
			while(star < 7)
				if(player.kvars.superstars[star] and player.kvars.superstars[star].valid)
					P_RemoveMobj(player.kvars.superstars[star])
					player.kvars.superstars[star] = nil
				end
				star = $1 + 1
			end
		end
		
		if(player.mo.state == S_PLAY_SUPER_TRANS6)
			and(player.mo.tics <= 1)
			player.kvars.supertransflash = TICRATE/3
		end
		
		// --------------------------------
		// Abilities!
		// --------------------------------
		if(player.gotflag)
			and(player.kvars.ability)
			discardAbility(player)
			if(P_IsObjectOnGround(player.mo))
				if(player.mo.momx or player.mo.momy)
					player.mo.state = S_PLAY_WALK
				else
					player.mo.state = S_PLAY_STND
				end
			else
				player.mo.state = S_PLAY_FALL
			end
		end
		
		local cansuck = false
		if(callAbilityFunc(player, true, false, false) & AF_ALLOWSUCK)
			cansuck = true
		end
		
		if(player.powers[pw_super])		// Starship!
			player.mo.color = player.skincolor	// We don't want to super flash
			player.charflags = $1|SF_NOSKID
			player.mo.colorized = false
			
			if not(player.mo.state >= S_PLAY_SUPER_TRANS1 and player.mo.state <= S_PLAY_SUPER_TRANS6)
				player.height = $1 * 2	// Normal player height
				player.mo.state = S_KIRBY_STARSHIP
				if(player.kvars.supertransflash)
					player.mo.color = SKINCOLOR_SUPERSILVER1
					player.mo.colorized = true
				end
				
				if not(P_IsObjectOnGround(player.mo))	// Low gravity
					and not(player.cmd.buttons & BT_CUSTOM2)	// Duck button = more intense gravity
					player.mo.momz = $1 - P_GetMobjGravity(player.mo)/3
				end
				
				// Schuut
				if(player.cmd.buttons & BT_USE)
					if not(leveltime%3)
						S_StartSound(player.mo, sfx_bshoot)
						P_SPMAngle(player.mo, MT_KIRBYSPITSTAR, player.mo.angle, 0)
					end
					if not(leveltime%6)
						if(player.mo.health > 1)
							player.mo.health = $1 - 1
							player.health = $1 - 1
						end
						local shot1 = P_SPMAngle(player.mo, MT_KIRBYSPITSTAR, player.mo.angle+FixedAngle(FRACUNIT*10), 0)
						local shot2 = P_SPMAngle(player.mo, MT_KIRBYSPITSTAR, player.mo.angle-FixedAngle(FRACUNIT*10), 0)
						local shot3 = P_SPMAngle(player.mo, MT_KIRBYSPITSTAR, player.mo.angle+FixedAngle(FRACUNIT*20), 0)
						local shot4 = P_SPMAngle(player.mo, MT_KIRBYSPITSTAR, player.mo.angle-FixedAngle(FRACUNIT*20), 0)
						if(shot1)
							shot1.fuse = TICRATE/3
						end
						if(shot2)
							shot2.fuse = TICRATE/3
						end
						if(shot3)
							shot3.fuse = TICRATE/6
						end
						if(shot4)
							shot4.fuse = TICRATE/6
						end
					end
				end
			end
		else
			if(player.mo.state == S_KIRBY_STARSHIP)
				player.mo.state = S_PLAY_WALK
			end
			if(player.kvars.ability)
				if(player.mo.state == S_KIRBY_SUCK or player.mo.state == S_KIRBY_SUCKMOVE)
					player.mo.state = S_PLAY_STND
				end
				if(buttonPressed(player, tossbutton))
					and not(callAbilityFunc(player, true, false, false) & AF_NODROP)
					and not(player.kvars.suckstate)
					and not(player.kvars.flashtime)
					and not(player.powers[pw_carry] == CR_NIGHTSMODE)
					// Discard ability!
					discardAbility(player)
				elseif not(player.kvars.flashtime)		// Not while transforming!
					callAbilityFunc(player, false, false, false)
				end
			elseif(player.mo.skin == "kirby")
				cansuck = true
			end
		end
		
		// --------------------------------
		// Sucking!
		// --------------------------------
		if(cansuck)
			player.kvars.numsuck = 0
			
			local wassuck = player.kvars.suckstate
			
			if not(player.kvars.suckstate)
				and(buttonPressed(player, BT_USE))
				and not(player.pflags & PF_STASIS)
				and not(player.powers[pw_nocontrol])
				and not(player.exiting)
				and not(P_PlayerInPain(player))
				and not(player.kvars.floating)
				and not(player.kvars.ducking)
				and not(player.powers[pw_carry] or player.kvars.prevcarry)
				and not(player.gotflag)
			//	and not(player.kvars.inmud)
				player.kvars.suckstate = 1
				player.pflags = $1|PF_NOJUMPDAMAGE	// For jumping from chains
			end
			
			if(player.kvars.suckstate == 1)		// Sucking
				or(player.kvars.suckstate == 2)	// Sucking (Locked)
				player.jumpfactor = 0
				player.drawangle = player.mo.angle
				// Animation
				if(R_PointToDist2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy))
					and(P_IsObjectOnGround(player.mo))
					if not(player.mo.state == S_KIRBY_SUCKMOVE)
						player.mo.state = S_KIRBY_SUCKMOVE
					end
				else
					player.mo.state = S_KIRBY_SUCK
				end
				player.normalspeed = $1 / 3		// 1/3 speed! (Kirby can still move, like Kirby 64)
				// Let go of the button?
				if(player.kvars.suckstate == 1)
					and(not(player.cmd.buttons & BT_USE) or(player.exiting) or(P_PlayerInPain(player)) or(player.powers[pw_carry] or player.kvars.prevcarry) or(player.kvars.floating) or(player.gotflag) /*or(player.kvars.inmud)*/)
					player.kvars.suckstate = 0
				else
					// Continue otherwise
					
					// Suck sound
					local suck1timeu = suck1time4
					local suck2timeu = suck2time4
					local suck1 = sfx_4suck
					local suck2 = sfx_4suckl
					if not(player.kvars.sucktimer)
						S_StartSound(player.mo, suck1)
					elseif(player.kvars.sucktimer == suck1timeu)
						S_StartSound(player.mo, suck2)
					end
					player.kvars.sucktimer = $1 + 1
					if(player.kvars.sucktimer >= suck1timeu+suck2timeu-1)
						player.kvars.sucktimer = suck1timeu
					end
					
					// Suck objects
					local zoff = 0
					if not(wassuck)
						local snum = 1
						while(snum < sucklength)
							local xoff = FixedMul(cos(player.drawangle), player.mo.radius*snum)
							local yoff = FixedMul(sin(player.drawangle), player.mo.radius*snum)
							local suck = P_SpawnMobj(player.mo.x+xoff, player.mo.y+yoff, player.mo.z+zoff, MT_KIRBYSUCKS)
							suck.sucktime = snum
							suck.target = player.mo
							suck.flags2 = $1|MF2_DONTDRAW
							suck.scale = player.mo.scale
							snum = $1 + 1
						end
					end
					local xoff = FixedMul(cos(player.drawangle), player.mo.radius*sucklength)
					local yoff = FixedMul(sin(player.drawangle), player.mo.radius*sucklength)
					local suck = P_SpawnMobj(player.mo.x+xoff, player.mo.y+yoff, player.mo.z+zoff, MT_KIRBYSUCKS)
					suck.sucktime = sucklength
					suck.target = player.mo
					suck.flags2 = $1|MF2_DONTDRAW
					suck.scale = player.mo.scale
				end
			elseif(player.kvars.suckstate == 3)	// Something inhaled!
				if(player.mo.state >= S_KIRBY_SUCK and player.mo.state <= S_KIRBY_SUCKMOVE)
					player.mo.state = S_PLAY_STND
				end
				
				player.shieldscale = FRACUNIT*80/100
				
				// Pressing down
				if((player.cmd.buttons & duckbutton) or (twod and player.cmd.forwardmove < -25 and not player.mo.currentaxis))
					and(P_IsObjectOnGround(player.mo))
					and not(player.exiting)
					and not(P_PlayerInPain(player))
					and(player.mo.health)
					and not(player.kvars.floating)
					player.mo.state = S_KIRBY_CONSUME
					player.kvars.suckstate = 0
					player.kvars.suckedmultiple = false
					player.kvars.eattime = TICRATE*3/2
					player.kvars.badtime = 0
					player.kvars.itemtime = 0
					player.kvars.itsnouse = 0
					if not(player.mo.skin == "kirby")
						or(player.kvars.ability)
						S_StartSound(player.mo, sfx_eat2)
						player.kvars.eattime = 0
					elseif(player.kvars.toability)
						if(player.kvars.toability == -1)
							player.kvars.rouletteability = -1	// Start roulette
							player.kvars.eattime = 0
						elseif(player.kvars.toability == 1000 and player.kvars.tosecret)	// Secret "Normal" ability (doesn't actually do anything)
							player.kvars.tosecret = false
							player.kvars.ability = 0
						elseif(player.kvars.toability == 1001 and player.kvars.tosecret)	// Secret ITS NO USE
							player.kvars.itsnouse = player.kvars.eattime
							player.kvars.ability = 0
							S_StartSound(player.mo, sfx_nouse)
						else
							player.kvars.ability = player.kvars.toability
						end
						
						player.kvars.ablvar1 = 0
						player.kvars.ablvar2 = 0
						player.kvars.ablvar3 = 0
						if(player.kvars.tosecret)
							player.kvars.secretability = true
						else
							player.kvars.secretability = false
						end
						S_StartSound(player.mo, sfx_ablg)
						player.kvars.abilityrandom = P_RandomByte()
						player.kvars.flashtime = ablflashtime
						player.kvars.abljingletime = 0
					else
						S_StartSound(player.mo, sfx_eat2)
						player.kvars.badtime = player.kvars.eattime
					end
					spitEatenMobjs(player, 2)
				// Spitting
				elseif(buttonPressed(player, BT_USE))
					and not(P_PlayerInPain(player))
					and not(player.exiting)
					player.kvars.suckstate = 0
					player.drawangle = player.mo.angle
					if(player.kvars.numsuckobjs)
						and not(oopsAllFlickies(player))
						spitEatenMobjs(player, 1)
						if(player.kvars.numsuckobjs > 1)
							S_StartSound(player.mo, sfx_spitl)
						else
							S_StartSound(player.mo, sfx_spit)
						end
					else
						spitEatenMobjs(player, 1)
						local spitstar = P_SPMAngle(player.mo, MT_KIRBYSPITSTAR, player.drawangle, 0)
						if(player.kvars.suckedmultiple)
							S_StartSound(player.mo, sfx_spitl)
							if(spitstar and spitstar.valid)
								spitstar.kirbymulti = true
							end
						else
							S_StartSound(player.mo, sfx_spit)
						end
					end
					player.mo.state = S_KIRBY_SPIT
					player.kvars.suckedmultiple = false
				end
			else
				// No succ
				if(player.mo.state >= S_KIRBY_SUCK and player.mo.state <= S_KIRBY_SUCKMOVE)
					player.mo.state = S_PLAY_STND
				end
			end
			
			if not(player.kvars.suckstate)
				or(player.kvars.suckstate >= 3)
				while(S_SoundPlaying(player.mo, sfx_suck))
					or(S_SoundPlaying(player.mo, sfx_suckl))
					or(S_SoundPlaying(player.mo, sfx_4suck))
					or(S_SoundPlaying(player.mo, sfx_4suckl))
					or(S_SoundPlaying(player.mo, sfx_dsuck))
					or(S_SoundPlaying(player.mo, sfx_dsuckl))
					S_StopSound(player.mo)
				end
				player.kvars.sucktimer = 0
				if not(player.kvars.suckstate)
					player.kvars.toability = 0
					player.kvars.tosecret = false
				end
			end
		end
		
		if(player.mo.state == S_KIRBY_SPIT)
			player.drawangle = player.mo.angle
		end
		
		// --------------------------------
		// Ducking!
		// --------------------------------
		if((((player.cmd.buttons & duckbutton) or (twod and player.cmd.forwardmove < -25 and not player.mo.currentaxis)) and not(player.exiting)) or player.mo.state == S_KIRBY_SLIDE or squishedCheck(player))
			and(P_IsObjectOnGround(player.mo))
			and not(P_PlayerInPain(player))
			and not(callAbilityFunc(player, true, false, false) & AF_NODUCK)
			and not(player.kvars.suckstate >= 1)	// Not sucking!
			and not(player.kvars.floating)
			and not(player.mo.state == S_KIRBY_CONSUME)
			and not(player.kvars.flashtime)
			and not(starship)	// Not when using starship!
			and not(player.powers[pw_carry] or player.kvars.prevcarry)
			and not(player.kvars.inmud)
			and not(player.mo.eflags & MFE_GOOWATER)
			player.jumpfactor = 0
			player.height = $1 / 6
			// Start ducking
			if not(player.kvars.ducking)
				S_StartSound(player.mo, sfx_kflat)
			end
			player.kvars.ducking = true
			if not(player.mo.state == S_KIRBY_SLIDE)
				player.mo.state = S_KIRBY_DUCK
			end
			
			// Can't move when ducking!
			if(player.mo.currentaxis)
				lockControl(player)
			end
			player.normalspeed = 0
			player.thrustfactor = 0
			
			// Crusher immunity!... hopefully!
			if(squishedCheck(player))
				or(player.kvars.roulette)
				player.mo.flags = $1 & !MF_SHOOTABLE
			else
				player.mo.flags = $1|MF_SHOOTABLE
			end
			
			// Sliding!
			if(buttonPressed(player, BT_JUMP))
				and not(player.mo.state == S_KIRBY_SLIDE)
				// Slide!
				P_Thrust(player.mo, player.drawangle, 20*player.mo.scale)
				player.mo.state = S_KIRBY_SLIDE
				player.mo.savedstate = S_KIRBY_SLIDE
				S_StartSound(player.mo, sfx_kbrake)
			end
			// End the slide
			if(R_PointToDist2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy) < 3*player.mo.scale)
				and(player.mo.state == S_KIRBY_SLIDE)
				player.mo.state = S_KIRBY_DUCK
			end
			// Hit things when sliding!
			if(player.mo.state == S_KIRBY_SLIDE)
				player.pflags = ($1|PF_JUMPED) & !PF_NOJUMPDAMAGE
			else
				player.pflags = ($1|PF_NOJUMPDAMAGE) & !PF_JUMPED
			end
			// Slide dust
			if(player.mo.state == S_KIRBY_SLIDE)
				and not(leveltime % 3)
				local particle = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_SPINDUST)
				particle.scale = player.mo.scale
				if(player.mo.eflags & MFE_VERTICALFLIP)
					P_TeleportMove(particle, player.mo.x, player.mo.y, player.mo.z+player.mo.height-particle.scale)
				end
				particle.momz = (((P_RandomByte()*player.mo.scale)/255)+player.mo.scale)*P_MobjFlip(player.mo)
				
				local rundir = R_PointToAngle2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy)+ANGLE_90
				P_InstaThrust(particle, rundir, (((P_RandomByte()*player.mo.scale*2)/255)-player.mo.scale)*16)
				
				local goodmomx = player.mo.momx-player.cmomx
				local goodmomy = player.mo.momy-player.cmomy
				particle.momx = $1 + player.cmomx + (goodmomx*2/3)
				particle.momy = $1 + player.cmomy + (goodmomy*2/3)
				
				P_TeleportMove(particle, particle.x-particle.momx, particle.y-particle.momy, particle.z+particle.momz)
				
				if(P_MobjFlip(player.mo) < 0)
					particle.eflags = $1|MFE_VERTICALFLIP
					particle.flags2 = $1|MF2_OBJECTFLIP
				end
				
				if(player.mo.eflags & MFE_UNDERWATER)
					or(player.mo.eflags & MFE_TOUCHWATER)
					particle.state = S_SPINDUST_BUBBLE1
				end
				
				particle.tics = 6
			end
		else
			if(player.kvars.ducking)
				player.mo.flags = $1|MF_SHOOTABLE
				player.pflags = ($1|PF_NOJUMPDAMAGE) & !PF_JUMPED
				if(player.mo.state == S_KIRBY_DUCK)
					if(player.mo.momx-player.cmomx or player.mo.momy-player.cmomy)
						player.mo.state = S_PLAY_WALK
					else
						player.mo.state = S_PLAY_STND
					end
				end
			end
			player.kvars.ducking = false
		end
		
		// Kinda useless at this point...
		if(callAbilityFunc(player, true, false, false) & AF_NOJUMP)
			player.jumpfactor = 0
		end
		
		// --------------------------------
		// Floating
		// --------------------------------
		if not(player.kvars.floating)
			and((buttonPressed(player, BT_JUMP) and not(P_IsObjectOnGround(player.mo) or player.kvars.prevground)) or (twod and (player.cmd.forwardmove > 25 and player.kvars.prevforward <= 25)))
			and(player.kvars.floatingtimer)
			and not(player.mo.eflags & MFE_UNDERWATER)
			and not(player.kvars.inmud)
			and not(player.pflags & PF_STASIS)
			and not(player.powers[pw_nocontrol])
			and not(player.exiting)
			and not(P_PlayerInPain(player))
			and not(player.powers[pw_carry] or player.kvars.prevcarry)
			and not(callAbilityFunc(player, true, false, false) & AF_NOFLOAT)
			and not(player.kvars.suckstate)	// Not sucking!
			and not(player.kvars.ducking)
			and not(player.kvars.flashtime)
			and not(starship)	// Not when using starship!
			and not(player.gotflag)	// Not when you have the flag!
			player.kvars.floating = true
			S_StartSound(player.mo, sfx_kfloat)
			player.mo.state = S_KIRBY_FLOATSTART
			player.pflags = $1|PF_NOJUMPDAMAGE	// For jumping from chains
		end
		
		// Force exit floating!
		if(P_PlayerInPain(player))
			or(player.powers[pw_carry] or player.kvars.prevcarry)
			or(starship)	// Not when using starship!
			or(player.kvars.inmud)
			if(player.kvars.floating)
				if(player.mo.state >= S_KIRBY_FLOATSTART and player.mo.state <= S_KIRBY_FLOATEND)
					if(P_IsObjectOnGround(player.mo))
						player.mo.state = S_PLAY_STND
					else
						player.mo.state = S_PLAY_FALL
					end
				end
				if((player.powers[pw_carry]) or player.kvars.prevcarry or player.kvars.inmud)
					and not(player.powers[pw_carry] == CR_PTERABYTE)
					// Puff air because why not
					S_StartSound(player.mo, sfx_exhale)
					local puff = P_SPMAngle(player.mo, MT_KIRBYAIRPUFF, player.drawangle, 0)
					if(puff)
						local off = player.mo.height
						if(P_MobjFlip(player.mo) < 0)
							off = 0
						end
						puff.scale = $1 * 3/2
						P_TeleportMove(puff, player.mo.x+(puff.momx*2), player.mo.y+(puff.momy*2), player.mo.z+off-(puff.height/3))
						puff.momx = $1 + player.mo.momx
						puff.momy = $1 + player.mo.momy
					end
				end
				player.kvars.floating = false
			end
		end
		
		// Mid-float
		if(player.kvars.floating)
			player.kvars.floatingtimer = $1 - 1
			
			player.jumpfactor = 0
			player.shieldscale = FRACUNIT*80/100
			
			if(player.gotflag)
				and(player.kvars.floatingtimer > (TICRATE*2/3)-1)
				player.kvars.floatingtimer = (TICRATE*2/3)-1
			end
			
			local floatuse = S_KIRBY_FLOAT
			if(player.kvars.floatingtimer < TICRATE*2/3)
				floatuse = S_KIRBY_FLOATTIRE
			end
			
			// Animation
			if not(player.mo.state == S_KIRBY_FLOATSTART or player.mo.state == floatuse)
				or(P_IsObjectOnGround(player.mo))
				player.mo.state = floatuse
			end
			
			// Gravity reduction
			if not(P_IsObjectOnGround(player.mo))
				player.mo.momz = $1 - (P_GetMobjGravity(player.mo)/2)
			end
			if(player.mo.momz*P_MobjFlip(player.mo) < -5*player.mo.scale)	// Terminal velocity
				P_SetObjectMomZ(player.mo, -5*FRACUNIT, false)
			end
			// Can't float for too long!
			if(player.kvars.floatingtimer < TICRATE*2/3)
				player.mo.momx = $1 *7/8
				player.mo.momy = $1 *7/8
				player.mo.momz = $1 *4/5
				if(buttonPressed(player, BT_USE))
					player.kvars.floatingtimer = 0
				end
			elseif not(player.pflags & PF_STASIS)
				and not(player.powers[pw_nocontrol])
				and not(player.exiting)
				// Floating!
				if(player.cmd.buttons & BT_JUMP)
					or(twod and player.cmd.forwardmove > 25)
					if(player.mo.tics > 2)
						and(player.mo.state == S_KIRBY_FLOAT)
						player.mo.tics = 2	// Speed up animation
					end
					// Ascend!
					if(player.mo.momz*P_MobjFlip(player.mo) < 5*player.mo.scale)
						P_SetObjectMomZ(player.mo, FRACUNIT, true)
						if(player.mo.momz*P_MobjFlip(player.mo) > 5*player.mo.scale)
							P_SetObjectMomZ(player.mo, 5*FRACUNIT, false)
						end
					end
				elseif(player.mo.frame & 1)	// Stupid thing to restore old animation from 2.1`
					and(player.mo.state == S_KIRBY_FLOAT)
					if(player.mo.tics > 4)
						player.mo.tics = 4	// Speed up animation
					end
				end
			end
			// Exit the float
			if(buttonPressed(player, BT_USE))
				or not(player.kvars.floatingtimer)
				or(P_IsObjectOnGround(player.mo))	// This wasn't a thing in 2.1, but there's an animation bug I really dont want to look into... this is fine enough
				player.mo.state = S_KIRBY_FLOATEND
				player.kvars.floating = false
				S_StartSound(player.mo, sfx_exhale)
				player.mo.momx = $1 *1/2
				player.mo.momy = $1 *1/2
				player.mo.momz = $1 *1/2
				// Spawn the air puff
				local puff = P_SPMAngle(player.mo, MT_KIRBYAIRPUFF, player.drawangle, 0)
				if(puff)
					local off = player.mo.height
					if(P_MobjFlip(player.mo) < 0)
						off = 0
					end
					puff.scale = $1 * 3/2
					P_TeleportMove(puff, player.mo.x+(puff.momx*2), player.mo.y+(puff.momy*2), player.mo.z+off-(puff.height/3))
					puff.momx = $1 + player.mo.momx
					puff.momy = $1 + player.mo.momy
				end
			end
		end
		
		// Reset floating time
		if(P_IsObjectOnGround(player.mo))
			or(player.powers[pw_carry] or player.kvars.prevcarry)
			or(player.mo.eflags & MFE_UNDERWATER)
			or(player.kvars.inmud)
			player.kvars.floatingtimer = maxfloattime
			player.kvars.usedjumps = 0
		end
		
		// --------------------------------
		// Swimming!
		// --------------------------------
		if(player.mo.eflags & MFE_UNDERWATER)
			and not(starship)	// Not when using starship!
			and not(player.kvars.flashtime)		// Don't bother
			and not(player.kvars.inmud)			// Don't swim in mud!
			if not(player.kvars.floating)
				if not(player.kvars.ducking)
					and not(P_PlayerInPain(player))
					and not(player.powers[pw_carry] or player.kvars.prevcarry)
					and not(callAbilityFunc(player, true, false, false) & AF_NOSWIM)
					and not(player.kvars.suckstate >= 1 and player.kvars.suckstate <= 2)	// Not sucking!
					if(buttonPressed(player, BT_JUMP) or (((player.cmd.buttons & BT_JUMP) or (twod and player.cmd.forwardmove > 25)) and player.kvars.fastswimtime <= 1))
						and not(player.pflags & PF_JUMPSTASIS)
						and not(player.powers[pw_nocontrol])
						and not(player.exiting)
						player.kvars.fastswimtime = TICRATE*2/3
						player.mo.momz = 5*player.mo.scale*P_MobjFlip(player.mo)
					end
					
					// Animation
					if not(player.mo.state == S_PLAY_GASP)
						and not(player.mo.state == S_KIRBY_SPIT)	// Bad
						local swimanim = S_KIRBY_SWIM
						
						if(player.kvars.suckstate == 3)
							or(callAbilityFunc(player, true, false, false) & AF_WALKSWIM)
							swimanim = S_PLAY_WALK
						end
						
						if not(player.mo.state == swimanim)
							if not(P_IsObjectOnGround(player.mo))
								player.mo.state = swimanim
							end
						else
							if(player.kvars.fastswimtime)
								player.kvars.fastswimtime = $1 - 1
								if(player.mo.tics > 2)
									player.mo.tics = 2
								end
							end
						end
						
						if not(player.kvars.fastswimtime)
							and not(player.cmd.forwardmove)
							and not(player.cmd.sidemove)
							and not(P_IsObjectOnGround(player.mo))
							player.mo.state = S_PLAY_FALL
						end
						
						if(player.kvars.suckstate >= 1 and player.kvars.suckstate <= 2)
							and not(P_IsObjectOnGround(player.mo))
							player.mo.state = S_KIRBY_SUCK	// Should be suck
						end
					end
				else
					player.kvars.fastswimtime = 0
				end
			else
				player.mo.momz = 2*player.mo.scale*P_MobjFlip(player.mo)
			end
		else
			player.kvars.fastswimtime = 0
			if(player.mo.state == S_KIRBY_SWIM)
				player.mo.state = S_PLAY_FALL
			end
		end
		
		// --------------------------------
		// MISC
		// --------------------------------
		// Falling animation
		if not(P_IsObjectOnGround(player.mo))
			and((player.mo.state >= S_PLAY_STND and player.mo.state <= S_PLAY_RUN) or player.mo.state == S_KIRBY_DUCK or player.mo.state == S_KIRBY_SLIDE)
			and not(player.powers[pw_carry] or player.kvars.prevcarry)	// We got minecarts in 2.2
			and not(player.mo.eflags & MFE_UNDERWATER)	// Fat uses this!
			and not(player.kvars.inmud)
			player.mo.state = S_PLAY_FALL
		end
		
		// Running particles
		if(P_IsObjectOnGround(player.mo))
			and(player.mo.state == S_PLAY_RUN)
			and not(starship)	// Not when using starship!
			if not(leveltime % 3)
				local particle = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_SPINDUST)
				particle.scale = player.mo.scale
				if(player.mo.eflags & MFE_VERTICALFLIP)
					P_TeleportMove(particle, player.mo.x, player.mo.y, player.mo.z+player.mo.height-particle.scale)
				end
				particle.momz = (((P_RandomByte()*player.mo.scale)/255)+player.mo.scale)*P_MobjFlip(player.mo)
				
				local rundir = R_PointToAngle2(player.cmomx, player.cmomy, player.mo.momx, player.mo.momy)+ANGLE_90
				P_InstaThrust(particle, rundir, (((P_RandomByte()*player.mo.scale*2)/255)-player.mo.scale)*16)
				
				local goodmomx = player.mo.momx-player.cmomx
				local goodmomy = player.mo.momy-player.cmomy
				particle.momx = $1 + player.cmomx + (goodmomx*2/3)
				particle.momy = $1 + player.cmomy + (goodmomy*2/3)
				
				P_TeleportMove(particle, particle.x-particle.momx, particle.y-particle.momy, particle.z+particle.momz)
				
				if(P_MobjFlip(player.mo) < 0)
					particle.eflags = $1|MFE_VERTICALFLIP
					particle.flags2 = $1|MF2_OBJECTFLIP
				end
				
				if(player.mo.eflags & MFE_UNDERWATER)
					or(player.mo.eflags & MFE_TOUCHWATER)
					particle.state = S_SPINDUST_BUBBLE1
				end
				
				particle.scale = player.mo.scale
			end
			
			// Speed up animation
			player.mo.tics = 1
		end
		
		// Infinite breath! (maybe remove?)
	//	player.powers[pw_underwater] = 0
		
		// HUD timers
		if(player.kvars.badtime)
			player.kvars.badtime = $1 - 1
		end
		if(player.kvars.eattime)
			player.kvars.eattime = $1 - 1
		end
		if(player.kvars.itemtime)
			player.kvars.itemtime = $1 - 1
		end
		if(player.kvars.itsnouse)
			player.kvars.itsnouse = $1 - 1
		end
		if(player.kvars.supertransflash)
			player.kvars.supertransflash = $1 - 1
		end
		if(player.kvars.roulettespeed)
			and(player.kvars.roulette)
			player.kvars.roulettespeed = $1 - 1
			if(player.cmd.buttons & BT_JUMP)
				and not(player.kvars.prevcmd & BT_JUMP)
				and not(player.kvars.rouletteability == -1)
				tickRoulette(player, false, true)
				player.kvars.roulette = 0
				player.kvars.roulettespeed = 0
			elseif not(player.kvars.roulettespeed)
				tickRoulette(player, false, false)
			end
		end
		if(player.kvars.flashtime)
			player.kvars.flashtime = $1 - 1
			if(player.kvars.roulette)
				if(player.kvars.flashtime == 0)
					player.kvars.flashtime = 1
				end
			end
			player.mo.momx = 0
			player.mo.momy = 0
			player.mo.momz = 0
			if(player.powers[pw_nocontrol] < 2)
				player.powers[pw_nocontrol] = 2
			end
			if not(player.mo.state == S_KIRBY_CONSUME)
				and not(player.mo.state >= S_KIRBY_TRANSFORM1 and player.mo.state <= S_KIRBY_TRANSFORM2)
				player.mo.state = S_KIRBY_TRANSFORM1
				player.mo.frame = 0
			end
			if(player.mo.state >= S_KIRBY_TRANSFORM1 and player.mo.state <= S_KIRBY_TRANSFORM2)
				SpawnSparkles(player.mo, 30, false)
				if(player.kvars.rouletteability == -1)
					player.kvars.rouletteability = 0
					tickRoulette(player, true, false)
				end
			end
		elseif(player.mo.state >= S_KIRBY_TRANSFORM1 and player.mo.state <= S_KIRBY_TRANSFORM2)
			player.mo.state = S_PLAY_STND
			player.kvars.rouletteability = 0
		end
		
		// No shield abilities!
		player.pflags = $1|PF_THOKKED
		
		// No spinning!
	//	if(player.mo.state == S_PLAY_ROLL)
	//		player.mo.state = S_PLAY_PAIN
	//	end
		
		// Color stuff
		if(P_PlayerInPain(player))
			local color = SKINCOLOR_ORANGE
			if(player.skincolor == SKINCOLOR_ORANGE)
				color = SKINCOLOR_RUST
			end
			if(leveltime & 1)
				player.mo.color = color
				player.mo.colorized = true
			else
				player.mo.color = player.skincolor
				player.mo.colorized = false
			end
		elseif not(mariomode and player.powers[pw_invulnerability])
			and not(starship)
			player.mo.color = player.skincolor
			player.mo.colorized = false
			if(player.powers[pw_shield] & SH_FIREFLOWER)
				player.mo.color = SKINCOLOR_WHITE
			end
			
			if(player.mo.skin == "kirby")
				and(player.kvars.suckstate == 3)
				and(player.kvars.toability)
				and not(player.kvars.ability)
				and not(leveltime%(TICRATE/4))
				if(player.skincolor == SKINCOLOR_ORANGE)
					player.mo.color = SKINCOLOR_YELLOW
					player.mo.colorized = true
				else
					player.mo.color = SKINCOLOR_ORANGE	// Flash so we know there's an ability!
					player.mo.colorized = true
				end
			end
		end
		
		// Become phat kirby
		if(player.kvars.suckstate == 3)
			and(player.mo.sprite == SPR_PLAY)
			if(fatspritetable[player.mo.sprite2])
				player.mo.sprite2 = fatspritetable[$1]
			end
			if(player.mo.sprite2 == SPR2_FWLK)
				if(player.mo.tics == 1)	// Might break md2s...
					player.kvars.fatwalkframe = ($1 + 1)%8
				end
				player.mo.frame = player.kvars.fatwalkframe
			else
				player.kvars.fatwalkframe = 0
			end
		elseif(fatrestoretable[player.mo.sprite2])
			player.mo.sprite2 = fatrestoretable[player.mo.sprite2]
		end
		
		// Starship
		if(starship)
			and not(player.kvars.prevsuper)
			player.kvars.eattime = TICRATE*3/2
			player.kvars.badtime = 0
			player.kvars.itemtime = 0
			player.kvars.itsnouse = 0
		end
		
		// Sucked objects
		if(player.kvars.numsuckobjs)
			player.mo.rollangle = 0
		end
		
		local actualsucks = 0
		local snum = 0
		while(snum < player.kvars.numsuckobjs)
			if(player.kvars.suckobjs[snum] and player.kvars.suckobjs[snum].valid)
				P_TeleportMove(player.kvars.suckobjs[snum], player.mo.x, player.mo.y, player.mo.z)
				if not(player.kvars.suckobjs[snum].player and player.kvars.suckobjs[snum].player.valid)
					player.kvars.suckobjs[snum].angle = player.mo.angle
				else
					// Player stuff
					local otherplayer = player.kvars.suckobjs[snum].player
					if not(otherplayer.powers[pw_nocontrol] > 2)
						otherplayer.powers[pw_nocontrol] = 2
					end
				end
				player.kvars.suckobjs[snum].momx = 0
				player.kvars.suckobjs[snum].momy = 0
				player.kvars.suckobjs[snum].momz = 0
				// Invisible!
				player.kvars.suckobjs[snum].flags2 = $1|MF2_DONTDRAW
				player.kvars.suckobjs[snum].issucked = 2	// So the player knows they're eaten (for compatability with characters like ZFG and Sora)
				// Escape stuff
				if(player.kvars.suckobjs[snum].player and player.kvars.suckobjs[snum].player.valid)
					if(player.kvars.suckobjs[snum].player.weapondelay < 2)
						player.kvars.suckobjs[snum].player.weapondelay = 2
					end
					
					if(player.kvars.suckobjs[snum].kirbyescapenum == nil)
						player.kvars.suckobjs[snum].kirbyescapenum = 0
					end
					if(player.kvars.suckobjs[snum].kirbyescapeprev == nil)
						player.kvars.suckobjs[snum].kirbyescapeprev = false
					end
					
					// Add to the counter
					if(player.kvars.suckobjs[snum].player.cmd.buttons & BT_JUMP)
						and not(player.kvars.suckobjs[snum].kirbyescapeprev)
						player.kvars.suckobjs[snum].kirbyescapenum = $1 + 1
						S_StartSound(player.mo, sfx_kflat)
						local rf = 1
						if(P_RandomByte()&1)
							rf = -1
						end
					//	player.mo.rollangle = (ANGLE_22h/2*rf)+rf
						player.mo.momx = $1 + (P_RandomByte()-127)*FRACUNIT/127
						player.mo.momy = $1 + (P_RandomByte()-127)*FRACUNIT/127
						if(P_IsObjectOnGround(player.mo))
							player.mo.momz = $1 + P_RandomByte()*FRACUNIT*3/256
						end
					end
					
					if(player.kvars.suckobjs[snum].player.cmd.buttons & BT_JUMP)
						player.kvars.suckobjs[snum].kirbyescapeprev = true
					else
						player.kvars.suckobjs[snum].kirbyescapeprev = false
					end
					
					// Escape!
					if(player.kvars.suckobjs[snum].kirbyescapenum >= escapelimit)
						player.kvars.suckobjs[snum].kirbyescapenum = nil
						player.kvars.suckobjs[snum].kirbyescapeprev = nil
						// Set flags
						player.kvars.suckobjs[snum].flags2 = $1 & !MF2_DONTDRAW
						player.kvars.suckobjs[snum].issucked = 0
						player.kvars.suckobjs[snum].state = S_PLAY_PAIN
						fixMobj(player.kvars.suckobjs[snum])
						player.kvars.suckobjs[snum].beingsucked = false
						// Set escape stuff aaa
						if not(player.kvars.suckobjs[snum].player.powers[pw_nocontrol] > 5)
							player.kvars.suckobjs[snum].player.powers[pw_nocontrol] = 5
						end
						P_TeleportMove(player.kvars.suckobjs[snum], player.mo.x, player.mo.y, player.mo.z+(10*P_MobjFlip(player.mo)))
						player.kvars.suckobjs[snum].momz = 5*player.kvars.suckobjs[snum].scale*P_MobjFlip(player.kvars.suckobjs[snum])
						P_InstaThrust(player.kvars.suckobjs[snum], player.kvars.suckobjs[snum].angle, -8*player.kvars.suckobjs[snum].scale)
						// Remove from list
						player.kvars.suckobjs[snum] = nil
					end
				end
				
				if(player.kvars.suckobjs[snum] and player.kvars.suckobjs[snum].valid)
					actualsucks = $1 + 1
				end
			end
			snum = $1 + 1
		end
		
		// Everyone escaped
		if(player.kvars.numsuckobjs)
			and not(actualsucks)
			player.kvars.numsuckobjs = 0
			player.kvars.suckstate = 0
			player.kvars.suckedmultiple = false
			player.mo.state = S_PLAY_PAIN
			P_InstaThrust(player.mo, player.drawangle, -5*player.mo.scale)
			player.mo.momz = 5*player.mo.scale*P_MobjFlip(player.mo)
			P_TeleportMove(player.mo, player.mo.x, player.mo.y, player.mo.z+P_MobjFlip(player.mo))
			player.mo.rollangle = 0
		end
		
		// Late stuff
		if not(player.kvars.flashtime)
			and not(starship)
			callAbilityFunc(player, false, false, true)
		end
		
		// Prev cmd!
		player.kvars.prevcmd = player.cmd.buttons
		player.kvars.prevforward = player.cmd.forwardmove
		player.kvars.prevground = P_IsObjectOnGround(player.mo)
		player.kvars.prevsuper = starship
		player.kvars.prevcarry = player.powers[pw_carry]
		
		player.spinheight = player.height
		player.kvars.industdevil = false
		player.cmd.buttons = oldbutt	// For other things added later I guess?
	end
end)

addHook("MobjThinker", function(mo)
	if(P_IsObjectOnGround(mo))
		P_SetObjectMomZ(mo, 10*FRACUNIT)
		P_InstaThrust(mo, R_PointToAngle2(0, 0, mo.momx, mo.momy), 10*mo.scale)
	end
	
	// Timer
	if(mo.startimer == nil)
		mo.startimer = TICRATE*6
	end
	mo.startimer = $1 - 1
	if not(mo.startimer)
		P_KillMobj(mo)
	elseif(mo.health)
		SpawnSparkles(mo, 30, true)
	end
end, MT_KIRBYSTARDROP)
addHook("TouchSpecial", function()
	return true
end, MT_KIRBYSTARDROP)

local iskirby = false
addHook("MobjThinker", function(mo)
	if not(iskirby)
		mo.flags2 = $1|MF2_DONTDRAW
	else
		mo.flags2 = $1 & !MF2_DONTDRAW
	end
	if(P_IsObjectOnGround(mo))
		if not(mo.bounced)
			P_SetObjectMomZ(mo, 3*FRACUNIT)
			P_InstaThrust(mo, R_PointToAngle2(0, 0, mo.momx, mo.momy), 3*mo.scale)
			mo.bounced = true
		else
			mo.momx = 0
			mo.momy = 0
		end
	end
	
	// Timer
	if(mo.startimer == nil)
		mo.startimer = TICRATE*2
	end
	mo.startimer = $1 - 1
	if not(mo.startimer)
		P_KillMobj(mo)
//	elseif(mo.health)
//		SpawnSparkles(mo, 30, true)
	end
end, MT_KIRBY_BOSSSTAR)
addHook("TouchSpecial", function()
	return true
end, MT_KIRBY_BOSSSTAR)
addHook("PostThinkFrame", function()
	iskirby = false
end)

hud.add(function(v, player, cam)
	if(player.mo and player.mo.valid)
		and(player.mo.skin == "kirby")
		iskirby = true
	end
end, "game")

addHook("MobjDeath", function(mo, inflict, source, dtype)		// This line causes an error when a player spectates when the level transitions to score tally? What? Battle also errors on a few lines...
	if(mo and mo.valid and mo.player and mo.player.valid)
		and(mo.skin == "kirby")
		if not(dtype == DMG_DROWNED or dtype == DMG_SPACEDROWN)
			mo.player.kvars.deathtimer = deathtimer
		end
		if not(multiplayer)
			S_StopMusic(mo.player)
		end
		if(mo.player.kvars.ability)			// We have an ability?
			and not(callAbilityFunc(mo.player, true, false, false) & AF_NOLOSE)	// And you can lose it!
			and not(mo.player.powers[pw_carry] == CR_NIGHTSMODE)
			mo.player.kvars.ability = 0
			mo.player.kvars.secretability = false
		end
		spitEatenMobjs(mo.player, 2)
	end
end, MT_PLAYER)

// Makes Kirby lose his ability when he gets hurt!
addHook("MobjDamage", function(mobj)
	if(mobj and mobj.valid and mobj.skin == "kirby")
		and not(mobj.player.powers[pw_super])	// Can't get hurt when super!
		and not(mobj.player.powers[pw_shield])	// Shield prevents losing your ability
		and not(mobj.player.powers[pw_carry] == CR_NIGHTSMODE)
		if(mobj.player.kvars.ability)			// We have an ability?
			and not(callAbilityFunc(mobj.player, true, false, false) & AF_NOLOSE)	// And you can lose it!
			resetKirbyVars(mobj.player, true)
			discardAbility(mobj.player)			// Rip ability
		elseif not(mobj.player.kvars.suckstate == 3)	// Inhaling?
			resetKirbyVars(mobj.player, true)		// No more sucking for you!
		end
	end
end, MT_PLAYER)

// Reset ability on map change in competetive gametypes
addHook("MapLoad", function()
	if(gametyperules & GTR_FRIENDLY)
		return
	end
	
	for player in players.iterate
		if(player.kvars)
			player.kvars.ability = 0
			player.kvars.secretability = false
		end
	end
end)

// Plow through enemies!
addHook("MobjMoveCollide", function(mo, enemy)
	if(enemy.valid and mo.valid)		// We still exsist?
		and((enemy.flags & MF_ENEMY) or (enemy.flags & MF_MONITOR))
		and(enemy.health > 0)		// Enemy isn't already dead?
		and(mo.kirbymulti)			// Only when we're big!
		// Did we actual HIT the enemy?
		and(mo.z+mo.height >= enemy.z)
		and(mo.z <= enemy.z+enemy.height)
		P_KillMobj(enemy, mo, mo.target)	// Rip enemy 1991-2016
		return false
	end
end, MT_KIRBYSPITSTAR)
addHook("MobjThinker", function(mo)
	if(mo.health)
	//	and(mo.kirbymulti)
		SpawnSparkles(mo, 30, true)
		if(mo.kirbymulti)
			// ADD ORBITING STAR
		end
	end
end, MT_KIRBYSPITSTAR)

// Suck object!
// Function that checks if this is a special case that shouldn't be touched!
local function checkShouldBeModified(type)
	if(type == MT_BIGTUMBLEWEED)		// TODO: Only allow this if it's thokker
		or(type == MT_ROSY)
		return true
	end
	
	return false
end

// Function that tells us if this should lock Kirby into sucking!
local function checkTargetObjectIsHeavy(mo)
	if(mo.enemy.valid and mo.enemy.type == MT_RING)
		or(mo.enemy.valid and mo.enemy.type == MT_FLINGRING)
		or(mo.enemy.valid and mo.enemy.type == MT_COIN)
		or(mo.enemy.valid and mo.enemy.type == MT_BLUESPHERE)	// Get blue balls!
		or(mo.enemy.valid and mo.enemy.type == MT_REDTEAMRING and mo.target.player.ctfteam == 1)
		or(mo.enemy.valid and mo.enemy.type == MT_BLUETEAMRING and mo.target.player.ctfteam == 2)
		return false
	end
	
	return true
end

local function checkObjectIsHeavy(mo)
	if(mo.valid and mo.type == MT_RING)
		or(mo.valid and mo.type == MT_FLINGRING)
		or(mo.valid and mo.type == MT_COIN)
		or(mo.valid and mo.type == MT_BLUESPHERE)	// Get blue balls!
		or(mo.valid and mo.type == MT_REDTEAMRING)
		or(mo.valid and mo.type == MT_BLUETEAMRING)
		return false
	end
	
	return true
end

// Garbage
local function setStateSafe(mobj, state)
	local defaultaction = states[state].action
	states[state].action = nil
	mobj.state = state
	states[state].action = defaultaction
end

local function killSafe(mobj)
	local defaultaction = states[mobjinfo[mobj.type].deathstate].action
	states[mobjinfo[mobj.type].deathstate].action = nil
	P_KillMobj(mobj)
	states[mobjinfo[mobj.type].deathstate].action = defaultaction
end

// Move the suck thingy!
addHook("MobjThinker", function(mo)
	if not(mo.sucktime)		// Out of time?
		or(mo.target.player.kvars.suckstate == 3 and not mo.target.player.kvars.inhalesleft)	// Inhaled?
		or not(mo.target.player.kvars.suckstate)
		if(mo.enemy and mo.enemy.valid)			// Release our held object!
			and(mo.enemy.health)
			fixMobj(mo.enemy)
			mo.enemy.beingsucked = false
			mo.enemy.issucked = 0
		end
		P_RemoveMobj(mo)	// rip
		return
	end
	
	local radius = (mo.target.radius*mo.sucktime)+(mo.target.radius/2)
	if(mo.target.flags2 & MF2_TWOD)
		or(twodlevel)
		or(mo.target.currentaxis)
		radius = ((mo.target.radius/2)*mo.sucktime)+mo.target.radius
	end
	
	local xoff = FixedMul(cos(mo.target.angle), radius)
	local yoff = FixedMul(sin(mo.target.angle), radius)
	P_TeleportMove(mo, mo.target.x+xoff, mo.target.y+yoff, mo.target.z+(mo.target.height/2)-(mo.height/2))
	mo.scale = mo.target.scale
	
	if(mo.sucking)
		and(mo.enemy.valid)
		and(mo.enemy.beingsucked)
		if(mo.enemy.type == MT_PLAYER)
			mo.enemy.state = S_PLAY_PAIN
		end
		if not(checkShouldBeModified(mo.enemy.type))	// Don't get hurt by this thing!
			mo.enemy.flags = $1 & !MF_ENEMY	// Having a hard time thinking of something witty to say about this... oh well.
			mo.enemy.flags = $1 & !MF_BOSS		// Not gonna boss me around anymore!
			mo.enemy.flags = $1 & !MF_PAIN		// Noname would not approve... unless you show him your thighs
			mo.enemy.flags = $1 & !MF_FIRE		// Kae would not approve
			mo.enemy.flags = $1 & !MF_MISSILE	// Can't hurt you
			mo.enemy.flags = $1 & !MF_SHOOTABLE	// Can't be hurt
			mo.enemy.flags = $1 & !MF_SPECIAL	// Not special anymore!
			mo.enemy.flags = $1|MF_NOCLIP		// Not solid!
			mo.enemy.flags = $1|MF_NOCLIPHEIGHT	// Rip all the rings that fell through the world #neverforget
			mo.enemy.flags = $1|MF_NOCLIPTHING
		end
		
		P_TeleportMove(mo.enemy, mo.x, mo.y, mo.z+(mo.height/2)-(mo.enemy.height/2))
		
		if(mo.enemy.player)	// Player?
			mo.enemy.player.pflags = $1|PF_STASIS		// Can't use abilities!
			mo.enemy.player.pflags = $1|PF_JUMPSTASIS
			mo.enemy.player.powers[pw_nocontrol] = 1
		end
		
		if(mo.enemy.oldflags & MF_FIRE)		// Was this fire?
			mo.enemy.state = mobjinfo[mo.enemy.type].spawnstate	// Spawn state it is!
		end
		
		if(checkTargetObjectIsHeavy(mo))
			mo.target.player.kvars.suckstate = 2
		end
		
		if(mo.sucktime == 1)
			and(checkTargetObjectIsHeavy(mo))
			local abilityto = 0
			local secretto = false
			
			// Test what ability we should get!
			if(mo.target.skin == "kirby")
				if(mo.enemy.abilitytogive)
					abilityto = mo.enemy.abilitytogive
					if(mo.enemy.secret)
						secretto = mo.enemy.secret
					end
				// Skins
				elseif(mo.enemy.skin)
					and(kirbyabilitytable[mo.enemy.skin])
					abilityto = kirbyabilitytable[mo.enemy.skin]
				// Object type
				elseif(kirbyabilitytable[mo.enemy.type])
					abilityto = kirbyabilitytable[mo.enemy.type]
				// Fire
				elseif(mo.enemy.oldflags & MF_FIRE)
					abilityto = 1
				// Pushable
				elseif(mo.enemy.oldflags & MF_PUSHABLE)
					abilityto = 3
				else
					abilityto = 0
				end
				
				// No idol in competetive gametypes!
				if(abilityto == -3)
					and not(gametyperules & GTR_FRIENDLY)
					abilityto = -2
				end
				
				if(abilityto < 0)
					secretto = true
					abilityto = -$1
				end
			end
			
			// Remove the object!
			if(mo.enemy.player and mo.enemy.player.valid)	// Player?
				mo.target.player.kvars.suckobjs[mo.target.player.kvars.numsuckobjs] = mo.enemy
				mo.target.player.kvars.numsuckobjs = $1 + 1
			elseif(checkShouldBeModified(mo.enemy.type))	// Special object?
				mo.target.player.kvars.suckobjs[mo.target.player.kvars.numsuckobjs] = mo.enemy
				mo.target.player.kvars.numsuckobjs = $1 + 1
			else
				if(mo.enemy.oldflags & MF_BOSS)
					or(mo.enemy.type == MT_METALSONIC_RACE)
					G_ExitLevel()
				end
				
				if(mo.enemy.oldflags & MF_ENEMY)
					server.kirbyinhaleflicky = mo.target
					P_KillMobj(mo.enemy)
					server.kirbyinhaleflicky = nil
					S_StopSound(mo.enemy)
					mo.enemy.flags2 = $1|MF2_DONTDRAW
		//		else
		//			P_RemoveMobj(mo.enemy)
				end
				
				if(mo.enemy and mo.enemy.valid)
					if(mo.enemy.oldflags & MF_MONITOR)
						if not(abilityto)	// No ability?
							mo.target.player.kvars.suckitems[mo.target.player.kvars.numsuckitems] = mobjinfo[mo.enemy.type].damage
							mo.target.player.kvars.numsuckitems = $1 + 1
						end
						server.kirbyremovemonitor = true
						mo.enemy.flags = mo.enemy.oldflags
						mo.enemy.beingsucked = false
						mo.enemy.issucked = 0
						mo.enemy.flags = ($1 & !MF_SOLID)|MF_NOCLIP	// Paranoid
						if(mo.enemy.spawnpoint)	// Respawn at spawn point
							local xat = mo.enemy.spawnpoint.x*FRACUNIT
							local yat = mo.enemy.spawnpoint.y*FRACUNIT
							local sect = R_PointInSubsector(xat, yat).sector
							if(sect.f_slope)
								P_TeleportMove(mo.enemy, xat, yat, (mo.enemy.spawnpoint.z*FRACUNIT)+P_GetZAt(sect.f_slope, xat, yat))
							else
								P_TeleportMove(mo.enemy, xat, yat, (mo.enemy.spawnpoint.z*FRACUNIT)+sect.floorheight)
							end
						elseif(mo.enemy.kirbyateat)
							P_TeleportMove(mo.enemy, mo.enemy.kirbyateat[1], mo.enemy.kirbyateat[2], mo.enemy.kirbyateat[3])
						end
						local olddeathsound = mobjinfo[mo.enemy.type].deathsound
						mobjinfo[mo.enemy.type].deathsound = sfx_none
						P_KillMobj(mo.enemy, mo.target, mo.target)
						mobjinfo[mo.enemy.type].deathsound = olddeathsound
						mo.enemy.flags = ($1 & !MF_SOLID)|MF_NOCLIP
						mo.enemy.state = S_KIRBYMONINVIS
					//	mo.enemy.flags2 = $1|MF2_DONTDRAW
						server.kirbyremovemonitor = false
						mo.enemy = nil	// Just to be sure we don't reset this thing later...
					else
						P_RemoveMobj(mo.enemy)
					end
				end
			end
			
			if not(mo.target.player.kvars.toability)
				mo.target.player.kvars.toability = abilityto
				mo.target.player.kvars.tosecret = secretto
			elseif not(mo.target.player.kvars.toability == abilityto)
				or not(mo.target.player.kvars.tosecret == secretto)
				if(abilityto)	// Don't care about things that dont give abilities!
					mo.target.player.kvars.toability = -1
					mo.target.player.kvars.tosecret = false
				end
			end
			mo.target.player.kvars.inhalesleft = $1 - 1
			if not(mo.target.player.kvars.inhalesleft)
				mo.target.player.kvars.suckstate = 3
				S_StopSound(mo.target)
				S_StartSound(mo.target, sfx_fill)
			end
		// Collecting rings!
		elseif(mo.sucktime == 1)
			if(mo.enemy.type == MT_RING or mo.enemy.type == MT_FLINGRING or mo.enemy.type == MT_COIN or mo.enemy.type == MT_FLINGCOIN or mo.enemy.type == MT_BLUESPHERE)
				or(mo.enemy.type == MT_REDTEAMRING and mo.target.player.ctfteam == 1)
				or(mo.enemy.type == MT_BLUETEAMRING and mo.target.player.ctfteam == 2)
				if(mariomode)
					S_StartSound(mo.target, sfx_mario4)
				else
					S_StartSound(mo.target, mo.enemy.info.deathsound)
				end
				P_KillMobj(mo.enemy, mo.target, mo.target)
				P_GivePlayerRings(mo.target.player, 1)
			end
		end
	else
		mo.target.player.kvars.numsuck = $1 + 1
	end
	
	if(mo.target.player.kvars.numsuck == sucklength)
		and(mo.target.player.kvars.suckstate == 2)
		mo.target.player.kvars.suckstate = 3	// No sucking!
		mo.target.player.kvars.inhalesleft = 0	// Reset this
	end
	
	// The fancy effect!
	mo.flags2 = $1|MF2_DONTDRAW	// We're not even here :O
	if not(leveltime & 1 == mo.sucktime & 1)
		if(mo.suckyangle == nil)
			local angle = P_RandomKey(10)*36*FRACUNIT
			mo.suckyangle = FixedAngle(angle)
		end
		if(mo.suckyangle1 == nil)
			local angle = P_RandomKey(10)*36*FRACUNIT
			mo.suckyangle1 = FixedAngle(angle)
		end
		if(mo.suckyangle2 == nil)
			local angle = P_RandomKey(10)*36*FRACUNIT
			mo.suckyangle2 = FixedAngle(angle)
		end
		if(mo.suckyangle3 == nil)
			local angle = P_RandomKey(10)*36*FRACUNIT
			mo.suckyangle3 = FixedAngle(angle)
		end
		local rotate = 0
		while(rotate < 4)
			// Get the angle!
			local angle = mo.suckyangle
			if(rotate == 1)
				angle = mo.suckyangle1
			elseif(rotate == 2)
				angle = mo.suckyangle2
			elseif(rotate == 3)
				angle = mo.suckyangle3
			end
			
			local size = mo.sucktime + 2
			local size2 = 25
			local dist = (mo.scale*10/sucklength)/4
			local ringx = FixedMul(sin(angle), ((size*size)+size2) * dist)
			local ringy = 0
			local ringz = FixedMul(cos(angle), ((size*size)+size2) * dist)
			
			local m00 = sin(-mo.target.angle)
			local m01 = cos(-mo.target.angle)
			local m02 = 0
			local m10 = m01
			local m11 = -m00
			local m12 = 0
			local m20 = 0
			local m21 = 0
			local m22 = FRACUNIT
			local transformedRingx = FixedMul(ringx, m00) + FixedMul(ringy, m10) + FixedMul(ringz, m20)
			local transformedRingy = FixedMul(ringx, m01) + FixedMul(ringy, m11) + FixedMul(ringz, m21)
			local transformedRingz = FixedMul(ringx, m02) + FixedMul(ringy, m12) + FixedMul(ringz, m22)
			
			transformedRingx = $1 + mo.x
			transformedRingy = $1 + mo.y
			transformedRingz = $1 + mo.z + (mo.height/2)// + (4*mo.scale)
			
			local dustheight = FixedMul(mobjinfo[MT_PARTICLE].height, mo.scale)
			local dust = P_SpawnMobj(transformedRingx, transformedRingy, transformedRingz-dustheight, MT_PARTICLE)
			dust.tics = 2
			dust.scale = mo.scale
			if(mo.eflags & MFE_VERTICALFLIP)
				dust.flags = $1|MFE_VERTICALFLIP
			end
			if(mo.target.eflags & MFE_UNDERWATER)
				dust.sprite = SPR_BUBL	// Bubble now!
				dust.frame = A
			else
				dust.frame = A|TR_TRANS60		// Not as transparent as usual!
			end
		
			rotate = $1 + 1
		end
		// Spin the effect!
		local angle = 36*FRACUNIT/2
		mo.suckyangle = $1 + FixedAngle(angle)
		mo.suckyangle1 = $1 + FixedAngle(angle)
		mo.suckyangle2 = $1 + FixedAngle(angle)
		mo.suckyangle3 = $1 + FixedAngle(angle)
	end
	
	mo.sucktime = $1 - 1
end, MT_KIRBYSUCKS)

// Hope this fixes a long standing bug... 4/26/2020 12:47 am, the day we hope to get this thing out the door :ohh_yeah:
/*
Tripel's jumping spin attack hit Hydro.
<~Tripel> i'm in the ground
<~Tripel> if you get unsucked by kirby you get stuck in the ground
*/
// 4/27/2020
// It didn't. :(
addHook("MobjDeath", function(mobj)
	if(mo.enemy and mo.enemy.valid)			// Release our held object!
		and(mo.enemy.health)
		fixMobj(mo.enemy)
		mo.enemy.beingsucked = false
		mo.enemy.issucked = 0
	end
end, MT_KIRBYSUCKS)

// Suck up the objects!
// Copied from the original Kirby wad
local function suckerCollide(sucker, enemy)
	local isenemysuckable = false
	if(enemy.valid)
		if(enemy.flags & MF_ENEMY)
		//	or(enemy.flags & MF_BOSS)	// OP Pls nerf
			or((enemy.flags & MF_MONITOR) and not(enemy.flags & MF_GRENADEBOUNCE))
			or(enemy.flags & MF_FIRE)	// FIREEEEEE
			or(enemy.flags & MF_MISSILE and not(enemy.target == sucker.target))	// IT'S A HOMING SHOT!
			or(enemy.flags & MF_PUSHABLE)	// Requested, makes gargoyles and things eatable
			or(enemy.abilitytogive)
			// Special list of mobj types
			or(enemy.type == MT_RING)
			or(enemy.type == MT_FLINGRING)
			or(enemy.type == MT_COIN)
			or(enemy.type == MT_BLUESPHERE)	// Get blue balls!
			or(enemy.type == MT_REDTEAMRING and sucker.valid and sucker.target.player.ctfteam == 1)
			or(enemy.type == MT_BLUETEAMRING and sucker.valid and sucker.target.player.ctfteam == 2)
			or(enemy.type == MT_KIRBYSTARDROP)	// Dropped ability!
			or(enemy.type == MT_KIRBY_BOSSSTAR)
			or(enemy.type == MT_BIGMINE)	// That one underwater mine thingy!
			// Sucking in players?!?!
			or(enemy.type == MT_PLAYER and enemy != sucker.target and enemy.player and not sucker.target.beingsucked and enemy.health)
			// Metal Sonic because why not
			or(enemy.type == MT_METALSONIC_RACE)
			or(enemy.type == MT_METALSONIC_BATTLE)
			// Fang too
			or(enemy.type == MT_FANG)
			// Kirby can suck up the flags in ctf, but not if it's firmly planted in the ground!
			// TODO before allowing: Actualy implement the firmly planted part...
			//or(enemy.type == MT_REDFLAG)
			//or(enemy.type == MT_BLUEFLAG)
			// Thokker compatability! Kirby is overpowered!
			// 2019 note: Thokker is dead
			or(enemy.type == MT_BIGTUMBLEWEED)
			// ~~this is op but nobody will try this right~~
			or(enemy.type == MT_EGGROBO1)
			or(enemy.type == MT_GOOP)	// Whoops almost forgot this right before release
			isenemysuckable = true
		end
	end
	
	// Blacklist!
	if(enemy.valid and enemy.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER)	// Brak lighting
		or(enemy.type == MT_RING_REDBOX and not(sucker.target.player.ctfteam == 1))
		or(enemy.type == MT_RING_BLUEBOX and not(sucker.target.player.ctfteam == 2))
		or(enemy.type == MT_REDFLAG)	// Kirby can eat flags?!
		or(enemy.type == MT_BLUEFLAG)
		isenemysuckable = false
	end
	
	if(sucker.valid)		// Not rip?
		and(enemy.valid)	// Also not rip?
		and not(sucker.target and sucker.target.valid and sucker.target.beingsucked)
		// Did we actual HIT the enemy?
		and(sucker.z+sucker.height >= enemy.z)
		and(sucker.z <= enemy.z+enemy.height)
		and(isenemysuckable)
		and not(enemy.beingsucked)	// Don't suck something twice!
		and not(sucker.sucking)		// Don't suck 2 things!
		if(checkObjectIsHeavy(enemy))
			sucker.target.player.inhalestate = 2	// Lock into sucking!
			if(sucker.target.player.kvars.inhalesleft)
				sucker.target.player.kvars.suckedmultiple = true
			end
			sucker.target.player.kvars.inhalesleft = $1 + 1	// More things being sucked!
		else
			return false
		end
		if(sucker.target.beingsucked)
			sucker.target.beingsucked = false	// Don't want 2 kirbies eating eachother!
			fixMobj(sucker.target)
		end
		enemy.beingsucked = true
		enemy.issucked = 1
		sucker.sucking = true
		sucker.enemy = enemy
		// H
		if not(enemy.spawnpoint)
			enemy.kirbyateat = {enemy.x, enemy.y, enemy.z}
		end
		// Modify the enemy to not hurt
		enemy.oldflags = enemy.flags	// Gonna need this later!
		if not(checkShouldBeModified(enemy.type))
			enemy.flags = $1 & !MF_ENEMY	// Having a hard time thinking of something witty to say about this... oh well.
			enemy.flags = $1 & !MF_BOSS		// Not gonna boss me around anymore!
			enemy.flags = $1 & !MF_PAIN		// Noname would not approve... unless you show him your thighs
			enemy.flags = $1 & !MF_FIRE		// Kae would not approve
			enemy.flags = $1 & !MF_MISSILE	// Can't hurt you
			enemy.flags = $1 & !MF_SHOOTABLE	// Can't be hurt
			enemy.flags = $1 & !MF_SPECIAL	// Not special anymore!
			enemy.flags = $1|MF_NOCLIP		// Not solid!
			enemy.flags = $1|MF_NOCLIPHEIGHT	// Rip all the rings that fell through the world #neverforget
			enemy.flags = $1|MF_NOCLIPTHING	// Can you tell the above comment was written in 2016
		end
		
		if(enemy.player)
			enemy.player.pflags = $1|PF_STASIS		// Can't move!
			enemy.player.pflags = $1|PF_JUMPSTASIS	// Also can't jump!
			if(enemy.player.kvars)
				enemy.player.kvars.ablstate = 0	// Can't use abilities!
				enemy.player.kvars.suckstate = 0
				spitEatenMobjs(enemy.player, 2)	// Can't make a black hole
			end
		end
		if(enemy.oldflags & MF_FIRE)		// Was this fire?
			enemy.state = mobjinfo[enemy.type].spawnstate	// Spawn state it is!
		end
	end
	
	return false
end

// Add the function to both hooks!
addHook("MobjMoveCollide", suckerCollide, MT_KIRBYSUCKS)
addHook("MobjCollide", suckerCollide, MT_KIRBYSUCKS)

// Remove Starship ghost
addHook("MobjThinker", function(ghost)
	if(ghost.sprite == SPR_KSTR)	// Can't check if the player is super, this'll do
		P_RemoveMobj(ghost)
	end
end, MT_GHOST)

// Flicky stuff
local function ConsumedByKirby(flicky)
	if(server and server.valid and server.kirbyinhaleflicky and server.kirbyinhaleflicky.valid)
		if(kirbykillsflickies)
			flicky.kirbyatehimhesdead = true
		else
			server.kirbyinhaleflicky.player.kvars.suckobjs[server.kirbyinhaleflicky.player.kvars.numsuckobjs] = flicky
			server.kirbyinhaleflicky.player.kvars.numsuckobjs = $1 + 1
			flicky.oldflags = flicky.flags
		end
	end
end
local function HesDead(flicky)
	if(flicky.kirbyatehimhesdead)
		P_RemoveMobj(flicky)
	end
end

local flicky = 0
while(flicky < 19)
	addHook("MobjSpawn", ConsumedByKirby, MT_FLICKY_01+(flicky*2))	// Between each flicky type, MT_FLICKY_xx_CENTER exists
	if(kirbykillsflickies)
		addHook("MobjThinker", HesDead, MT_FLICKY_01+(flicky*2))
	end
	flicky = $1 + 1
end

// Remove the bubble!
addHook("MobjThinker", function(overlay)
	if(overlay.target and overlay.target.valid and overlay.target.type >= MT_FLICKY_01 and overlay.target.type <= MT_SEED)
		if(overlay.target.flags2 & MF2_DONTDRAW)
			overlay.flags2 = $1|MF2_DONTDRAW
		else
			overlay.flags2 = $1 & !MF2_DONTDRAW
		end
	end
end, MT_OVERLAY)

// Reset jumps when hitting a spring
addHook("MobjMoveCollide", function(mobj, spring)
	if not(spring and spring.valid)
		or not(mobj and mobj.valid)
		return
	end
	
	// Springs!
	if(spring.flags & MF_SPRING)
		and(mobj.z <= spring.z+spring.height)
		and(mobj.z+mobj.height >= spring.z)
		if(mobj.player and mobj.player.valid)
			mobj.player.kvars.usedjumps = 0
		end
	end
end, MT_PLAYER)

// Dust devils
local function OnDustDevil(dustdevil, pmobj)
	if not(pmobj.type == MT_PLAYER)
		return
	end

	// Kirby only!
	if not(pmobj.skin == "kirby")
		return
	end

	if(abs(pmobj.x - dustdevil.x) > dustdevil.radius or abs(pmobj.y - dustdevil.y) > dustdevil.radius)
		return
	end

	if(pmobj.z + pmobj.height >= dustdevil.z and dustdevil.z + dustdevil.height >= pmobj.z)
		local pos = pmobj.z - dustdevil.z
		local thresh = dustdevil.scale * 20

		local player = pmobj.player
		//Player in the swirl part.
		if(dustdevil.height - pos > thresh)
			// General
			player.kvars.ducking = false
			player.kvars.floating = false
			player.kvars.floatingtimer = maxfloattime
			player.kvars.usedjumps = 0
			
			// Abilities
			player.mo.state = S_PLAY_PAIN
			player.kvars.ablstate = 0
			player.kvars.ablvar1 = 0
			player.kvars.ablvar2 = 0
			player.kvars.ablvar3 = 0
			player.mo.flags = $1|MF_SHOOTABLE
			if not(player.kvars.suckstate == 3)
				player.kvars.suckstate = 0
				spitEatenMobjs(player, 2)
			end
			player.kvars.sucktimer = 0
			
			player.kvars.industdevil = true
		else
			// On top
			player.kvars.floatingtimer = maxfloattime
			player.kvars.usedjumps = 0
		end
	end

	return
end
addHook("MobjThinker", function(dustdevil)
	searchBlockmap("objects", OnDustDevil, dustdevil)
end, MT_DUSTDEVIL)

CheckMud = function(player)
	for rover in player.mo.subsector.sector.ffloors()
		if not(rover.flags & FF_EXISTS)
			or not(rover.flags & FF_QUICKSAND)
			continue
		end
		
		local topheight = rover.topheight
		local bottomheight = rover.bottomheight
		if(rover.t_slope) topheight = P_GetZAt(rover.t_slope, player.mo.x, player.mo.y) end
		if(rover.b_slope) bottomheight = P_GetZAt(rover.b_slope, player.mo.x, player.mo.y) end

		if(topheight >= player.mo.z and bottomheight < player.mo.z + player.mo.height)
			return true
		end
	end
	return false
end

rawset(_G, "K_FlipMobjZ", function(mobj, rmobj)
	P_TeleportMove(mobj, mobj.x, mobj.y, mobj.z-(rmobj.height-mobj.height))
end)

addHook("MobjSpawn", function(mobj)
	if(server.kirbyremovemonitor)
		mobj.state = S_KIRBYMONWILLDIE
		return true
	end
end)
states[S_KIRBYMONINVIS] = {SPR_INVS, 0, -1, nil, 0, 0, S_KIRBYMONINVIS}
states[S_KIRBYMONWILLDIE] = {SPR_PLAY, 0, 1, nil, 0, 0, S_NULL}

// Battle
local ScriptLoaded = false

addHook("ThinkFrame", function()
	if not(ScriptLoaded)
		and(CBW_Battle)
		ScriptLoaded = true
		local B = CBW_Battle
		
		B.Action.AbilityRoulette = function(mo,doaction,throwring,tossflag)
			local player = mo.player
			
			player.actiontext = "Ability Roulette"
			player.actiontextflags = 0
			player.actionrings = 30
			player.action2text = nil
			player.action2rings = 0
			
			local cannot = false
			
			if(player.kvars.roulette)
				player.actionstate = 1
			else
				if(player.kvars.floating)
					or(player.kvars.ablstate)
					or(player.kvars.suckstate)
					or(player.kvars.flashtime)
					player.actiontextflags = TF_GRAY
					cannot = true
				end
				player.actionstate = 0
			end
			
			if(player.actionstate == 0)
				if(doaction == 1)	//Action key was pressed and we can perform an action
					if(cannot)
						S_StartSound(mo,sfx_s3k8c,player)
					elseif(B.CheckRings(player,throwring) == 1)
						player.actionstate = 1
						B.PayRings(player)
						
						// Do the roulette!
						callAbilityFunc(player, false, true, false)
						player.kvars.rouletteability = -1
						player.kvars.ablstate = 0
						player.kvars.ablvar1 = 0
						player.kvars.ablvar2 = 0
						player.kvars.ablvar3 = 0
						player.kvars.suckstate = 0
						spitEatenMobjs(player, 2)
						S_StartSound(player.mo, sfx_ablg)
						player.kvars.eattime = TICRATE*3/2
						player.kvars.badtime = 0
						player.kvars.itemtime = 0
						player.kvars.itsnouse = 0
						player.kvars.flashtime = ablflashtime
						player.kvars.abljingletime = 0
						player.kvars.abilityrandom = P_RandomByte()
						player.kvars.ducking = false
						player.kvars.floating = false
					end
				end
			end
		end
		
		B.ActionChar["kirby"] = B.Action.AbilityRoulette
		B.CharShieldMax["kirby"] = 0	// Not sure if 0 is warrented, though defenetly not 2 as Kirby's inhale gives him a great defense
	end
end)
