function castSprites()
	-- After sorting the sprites, do the projection and draw them
	for i = 1, #sprites, 1 do
		-- Translate sprite position to relative to camera
		local spriteX = sprites[i].x - player.posX;
		local spriteY = sprites[i].y - player.posY;
				 
		-- Transform sprite with the inverse camera matrix
		local invDet = 1.0 / (player.planeX * player.dirY - player.dirX * player.planeY); -- Required for correct matrix multiplication
		
		local transformX = invDet * (player.dirY * spriteX - player.dirX * spriteY);
		local transformY = invDet * (-player.planeY * spriteX + player.planeX * spriteY); -- This is actually the depth inside the screen, that what Z is in 3D       

		local vMoveScreen = 0

		if sprites[i].id == 5 then
			vMoveScreen = curSize / transformY
		end
					
		local spriteScreenX = math.floor((w / 2) * (1 + transformX / transformY));
		
		-- Calculate height of the sprite on screen
		local spriteHeight = math.abs(math.floor(h / (transformY))); -- Using "transformY" instead of the real distance prevents fisheye

		if sprites[i].id == 5 then
			spriteHeight = spriteHeight / 2.5
		end

		-- Calculate lowest and highest pixel to fill in current stripe
		local drawStartY = -spriteHeight / 2 + ((h / 2) + TILT) + vMoveScreen;

		local drawEndY = spriteHeight / 2 + ((h / 2) + TILT) + vMoveScreen;

		-- Calculate width of the sprite
		local spriteWidth = math.abs( math.floor (h / (transformY)));

		if sprites[i].id == 5 then
			spriteWidth = spriteWidth / 2.5
		end

		local drawStartX = -spriteWidth / 2 + spriteScreenX;

		local drawEndX = spriteWidth / 2 + spriteScreenX;
			
		-- Loop through every vertical stripe of the sprite on screen
		for stripe = math.floor(drawStartX), drawEndX, 1 do
			local texX = math.floor((stripe - (-spriteWidth / 2 + spriteScreenX)) * textureWidth / spriteWidth);

			if texX < 0 then texX = 0 end
				-- The conditions in the if are:
				--1) It's in front of camera plane so you don't see things behind you
				--2) It's on the screen (left)
				--3) It's on the screen (right)
				--4) zBuffer, with perpendicular distance

			if(transformY > 0 and stripe > 0 and stripe < w and transformY < zBuffer[stripe]) then
				local deathColor = 1 - (transformY / maxFogDist)
				if deathColor < 0 then deathColor = 0 end

				local temp = {
					x = stripe,
					hor = texX,
					y = drawStartY,
					scale = (drawEndY - drawStartY) / textureHeight,
					id = sprites[i].id,
					color = {255 * deathColor, 255 * deathColor, 255 * deathColor},
					type = "sprite",
					z = math.sqrt((player.posX - sprites[i].x) * (player.posX - sprites[i].x) + (player.posY - sprites[i].y) * (player.posY - sprites[i].y)),
				}
				if temp.id == 4 then
					temp.scale = (drawEndY - drawStartY) / (textureHeight / 2)
					temp.y = drawStartY - (textureHeight * temp.scale / 2)
				end

				table.insert(spritePool, temp)
			end
		end
	end
end

function castDoors()
	-- After sorting the sprites, do the projection and draw them
	for i = 1, #doors, 1 do
		-- Translate sprite position to relative to camera
		local spriteX = doors[i].x - player.posX;
		local spriteY = doors[i].y - player.posY;
				 
		-- Transform sprite with the inverse camera matrix
		local invDet = 1.0 / (player.planeX * player.dirY - player.dirX * player.planeY); -- Required for correct matrix multiplication
		
		local transformX = invDet * (player.dirY * spriteX - player.dirX * spriteY);
		local transformY = invDet * (-player.planeY * spriteX + player.planeX * spriteY); -- This is actually the depth inside the screen, that what Z is in 3D       

		-- Start of door cast
		local addY = 0
		local addX = 0
		if doors[i].wide then
			addY = 0.5
		else
			addX = 0.5
		end

		local tx1 = invDet * (player.dirY * (spriteX + addX) - player.dirX * (spriteY + addY));
		local ty1 = invDet * (-player.planeY * (spriteX + addX) + player.planeX * (spriteY + addY)); -- This is actually the depth inside the screen, that what Z is in 3D       
					
		local starX = math.floor((w / 2) * (1 + tx1 / ty1));
		local starY1 = -math.abs(math.floor(h / (ty1))) / 2 + (h/ 2)
		local endY1 = math.abs(math.floor(h / (ty1))) / 2 + (h/ 2)

		-- End of door cast
		local tx2 = invDet * (player.dirY * (spriteX - addX) - player.dirX * (spriteY - addY));
		local ty2 = invDet * (-player.planeY * (spriteX - addX) + player.planeX * (spriteY - addY)); -- This is actually the depth inside the screen, that what Z is in 3D       
					
		local endX = math.floor((w / 2) * (1 + tx2 / ty2));
		local starY2 = -math.abs(math.floor(h / (ty2))) / 2 + (h/ 2)
		local endY2 = math.abs(math.floor(h / (ty2))) / 2 + (h/ 2)
		-----------

		if transformY > 0 and ty1 > 0 and ty2 > 0 then
			local deathColor = 1 - (transformY / maxFogDist)
			if deathColor < 0 then deathColor = 0 end

			local i = {
				id = doors[i].id,
				v1 = {starX, starY1},
				v2 = {endX, starY2},
				v3 = {endX, endY2},
				v4 = {starX, endY1},
				cov = {9001, 9001},
				starX = starX,
				endX = endX,
				type = "door",
				color = {255 * deathColor, 255 * deathColor, 255 * deathColor},
				z = math.sqrt((player.posX - doors[i].x) * (player.posX - doors[i].x) + (player.posY - doors[i].y) * (player.posY - doors[i].y)),
			}

			if starX < endX then
				for stripe = starX, endX do
					if  (transformY > 0 and stripe >= 0 and stripe <= w and transformY < zBuffer[stripe]) then
						if i.cov[1] == 9001 then
							i.cov[1] = stripe
							i.cov[2] = stripe
						else
							if stripe > i.cov[1] then
								i.cov[1] = stripe
							end
							if stripe < i.cov[2] then
								i.cov[2] = stripe
							end
						end
					end
				end
			else
				for stripe = endX, starX do
					if  (transformY > 0 and stripe >= 0 and stripe <= w and transformY < zBuffer[stripe]) then
						if i.cov[1] == 9001 then
							i.cov[1] = stripe
							i.cov[2] = stripe
						else
							if stripe < i.cov[1] then
								i.cov[1] = stripe
							end
							if stripe > i.cov[2] then
								i.cov[2] = stripe
							end
						end
					end
				end
			end

			table.insert(spritePool, i)
		end
	end
end