local function doYCollision(self,collision,mx,my)
	local stage = self.stage or stage
	local off
	local sign
	if my >= 0
		off = collision[BBOX.BOTTOM]
		sign = 1
	else
		off = -collision[BBOX.TOP]
		sign = -1
	end

	local was_grounded = self.grounded
	self.grounded = false
	self.slope = false

	-- (Floor) slope stuff
	if CLIENT and sign > 0
		for y = math.floor((math.floor(self.y) + off - 1) / 16),math.floor((math.floor(self.y) + off + my) / 16),sign
			if y < 0 or y >= stage.height
				continue
			end
			local x = math.floor(math.floor(self.x) / 16)
			if x < 0 or x >= stage.width
				continue
			end
			local pos = x+y*stage.width
			local tile = stage.tiles:byte(pos+1)
			local flags = stage.tileflags:byte(tile+1)
			if flags & 0x10 ~= 0
				local slope = flags & 0x0F
				if slope >= 4 and slope <= 7
					local sly = 0
					if slope == 4
						sly = math.floor((math.floor(self.x) - x*16)/2)
					elseif slope == 5
						sly = 8+math.floor((math.floor(self.x) - x*16)/2)
					elseif slope == 6
						sly = 8+8-math.floor((math.floor(self.x) - x*16)/2)
					elseif slope == 7
						sly = 8-math.floor((math.floor(self.x) - x*16)/2)
					end
					if (was_grounded and math.floor(self.y + off) >= y*16 + sly - 4) -- On the ground, stick to slopes more
					or math.floor(self.y + off + my) >= y*16 + sly -- In the air, only stop if you actually hit the ground
						self.grounded = true
						self.y = y*16 - off + sly
						self.slope = true
						return true
					end
				end
			end
		end
	end

	-- Block collisions
	local minx = math.floor((math.floor(self.x) - collision[BBOX.LEFT] + 2) / 16)
	local maxx = math.floor((math.floor(self.x) + collision[BBOX.RIGHT] - 2) / 16)
	if mx > 0
		maxx = math.floor((math.floor(self.x + mx) + collision[BBOX.RIGHT] - 2) / 16)
	elseif mx < 0
		minx = math.floor((math.floor(self.x + mx) - collision[BBOX.LEFT] + 2) / 16)
	end
	for y = math.floor((math.floor(self.y) + off) / 16),math.floor((math.floor(self.y) + off + my) / 16),sign
		if y < 0 or y >= stage.height
			continue
		end
		for x = minx,maxx
			if x < 0 or x >= stage.width
				continue
			end
			local pos = x+y*stage.width
			local tile = stage.tiles:byte(pos+1)
			local flags = stage.tileflags:byte(tile+1)
			if flags == 0x41
				if sign < 0
					-- TODO: Headbump sparks here.
					y = y+1
				else
					self.grounded = true
				end
				self.y = y*16 - off
				return true
			end
		end
	end
	self.y = self.y + my
end

local function doXCollision(self,collision,mx,my)
	local stage = self.stage or stage
	local off
	local sign
	if mx > 0
		off = collision[BBOX.RIGHT]
		sign = 1
	elseif mx < 0
		off = -collision[BBOX.LEFT]
		sign = -1
	else
		return
	end

	-- Crazy size-changin shit
	local miny = math.floor((math.floor(self.y) - collision[BBOX.TOP] + 2) / 16)
	local maxy = math.floor((math.floor(self.y) + collision[BBOX.BOTTOM] - 2) / 16)
	if self.slope
		-- This adds a small step up so slopes walk smoothly up onto the next level without crashing into it like a wall.
		-- If we do this while not on a slope, you can walk over one block gaps in the floor without falling.
		maxy = math.floor((math.floor(self.y) + collision[BBOX.BOTTOM] - 4) / 16)
	elseif not self.grounded and my < 0
		-- For jumping into one block gaps, get shorter and bonk your head first.
		miny = math.floor(math.floor(math.floor(self.y) + collision[BBOX.BOTTOM] / 2) / 16)
		maxy = math.floor((math.floor(self.y) + collision[BBOX.BOTTOM] - 2) / 16)
	end
	if miny > maxy
		miny = maxy
	end

	-- TODO: Collide with other block types.
	-- Including the high sides of slopes and bustable blocks (when the blocks indeed become bustable)
	for x = math.floor((math.floor(self.x) + off) / 16),math.floor((math.floor(self.x) + off + mx) / 16),sign
		if x < 0 or x >= stage.width
			continue
		end
		for y = miny,maxy
			if y < 0 or y >= stage.height
				continue
			end
			local pos = x+y*stage.width
			local tile = stage.tiles:byte(pos+1)
			local flags = stage.tileflags:byte(tile+1)
			if flags == 0x41
				if sign < 0
					x = x+1
				end
				self.x = x*16 - off
				return true
			end
		end
	end
	self.x = self.x + mx
end

local function applySpecial(self,collision)
	local stage = self.stage or stage
	self.underwater = false
	local y = math.floor((math.floor(self.y)) / 16)
	if y < 0 or y >= stage.height
		return
	end
	for x = math.floor((math.floor(self.x) - collision[BBOX.LEFT]) / 16),math.floor((math.floor(self.x) + collision[BBOX.RIGHT]) / 16)
		if x < 0 or x >= stage.width
			continue
		end
		local pos = x+y*stage.width
		local tile = stage.tiles:byte(pos+1)
		local flags = stage.tileflags:byte(tile+1)
		if flags & 0x20 ~= 0
			self.underwater = true
		end
	end
	if self.skin
		-- TODO: Water droplets/sfx from entering/exiting water!
		if self.underwater
			self.physics = self.skin.physics[2]
		else
			self.physics = self.skin.physics[1]
		end
	end
end

function stage.doCollisions(self,mx,my)
	local collision = {}
	local info = self.skin or NPC[self.type]
	for i,v in ipairs(info.collision)
		if self.flip and i == BBOX.LEFT
			collision[BBOX.RIGHT] = v
		elseif self.flip and i == BBOX.RIGHT
			collision[BBOX.LEFT] = v
		else
			collision[i] = v
		end
	end

	if doYCollision(self,collision,mx,my)
		my = 0
	end
	if doXCollision(self,collision,mx,my)
		mx = 0
	end
	applySpecial(self,collision)
	return mx,my
end
