--[[

  Nether mod for minetest

  "mapgen_nobiomes.lua" is the legacy version of the mapgen, only used
    in older versions of Minetest or in v6 worlds.
  "mapgen.lua" is the modern biomes-based Nether mapgen, which
    requires Minetest v5.1 or greater


  Copyright (C) 2013 PilzAdam

  Permission to use, copy, modify, and/or distribute this software for
  any purpose with or without fee is hereby granted, provided that the
  above copyright notice and this permission notice appear in all copies.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
  WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
  BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
  OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  SOFTWARE.

]]--


-- Parameters

local NETHER_CEILING = abyss.DEPTH_CEILING
local NETHER_FLOOR   = abyss.DEPTH_FLOOR
local TCAVE = 0.6
local BLEND = 128


-- 3D noise

local np_cave = {
	offset = 0,
	scale = 1,
	spread = {x = 384, y = 128, z = 384}, -- squashed 3:1
	seed = 59033,
	octaves = 5,
	persist = 0.7,
	lacunarity = 2.0,
	--flags = ""
}


-- Stuff

local yblmin = NETHER_FLOOR   + BLEND * 2
local yblmax = NETHER_CEILING - BLEND * 2



-- Mapgen

dofile(abyss.path .. "/mapgen_decorations.lua")

-- Initialize noise object, localise noise and data buffers

local nobj_cave = nil
local nbuf_cave = {}
local dbuf = {}


-- Content ids

local c_air = minetest.get_content_id("air")
local c_netherrack = minetest.get_content_id("abyss:rack")

minetest.register_biome({
	name = "abyss",
	node_stone = "abyss:rack",
	y_max = abyss.DEPTH_CEILING, 
	y_min = abyss.DEPTH_FLOOR,
	heat_point = 50,
	humidity_point = 50,
	node_dungeon = "abyss:brick",
	node_dungeon_alt = "air",
	-- node_dungeon_stair = "stairs:stair_abyss_brick",
	vertical_blend = 2,
})

-- On-generated function

minetest.register_on_generated(function(minp, maxp, seed)
	if minp.y > NETHER_CEILING or maxp.y < NETHER_FLOOR then
		return
	end

	local x1 = maxp.x
	local y1 = math.min(maxp.y, NETHER_CEILING)
	local z1 = maxp.z
	local x0 = minp.x
	local y0 = math.max(minp.y, NETHER_FLOOR)
	local z0 = minp.z

	local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
	local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax}
	local data = vm:get_data(dbuf)

	local x11 = emax.x -- Limits of mapchunk plus mapblock shell
	local y11 = emax.y
	local z11 = emax.z
	local x00 = emin.x
	local y00 = emin.y
	local z00 = emin.z

	local ystride = x1 - x0 + 1
	local zstride = ystride * ystride
	local chulens = {x = ystride, y = ystride, z = ystride}
	local minposxyz = {x = x0, y = y0, z = z0}

	nobj_cave = nobj_cave or minetest.get_perlin_map(np_cave, chulens)
	local nvals_cave = nobj_cave:get_3d_map_flat(minposxyz, nbuf_cave)

	for y = y00, y11 do -- Y loop first to minimise tcave calculations
		local tcave
		local in_chunk_y = false
		if y >= y0 and y <= y1 then
			tcave = TCAVE
			if y > yblmax then tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2 end
			if y < yblmin then tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2 end
			in_chunk_y = true
		end

		for z = z00, z11 do
			local vi = area:index(x00, y, z) -- Initial voxelmanip index
			local ni
			local in_chunk_yz = in_chunk_y and z >= z0 and z <= z1

			for x = x00, x11 do
				if in_chunk_yz and x == x0 then
					-- Initial noisemap index
					ni = (z - z0) * zstride + (y - y0) * ystride + 1
				end

				local in_chunk_yzx = in_chunk_yz and x >= x0 and x <= x1 -- In mapchunk
				local id = data[vi] -- Existing node
				if in_chunk_yzx then -- In mapchunk
					if nvals_cave[ni] > tcave then
						data[vi] = c_air
					end
					ni = ni + 1 -- Only increment noise index in mapchunk
				end

				vi = vi + 1
			end
		end
	end

	vm:set_data(data)

	minetest.generate_decorations(vm)

	vm:set_lighting({day = 0, night = 0}, minp, maxp)
	vm:calc_lighting()
	vm:update_liquids()
	vm:write_to_map()
end)


-- use knowledge of the nether mapgen algorithm to return a suitable ground level for placing a portal.
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
function abyss.find_nether_ground_y(target_x, target_z, start_y, player_name)
	local nobj_cave_point = minetest.get_perlin(np_cave)
	local air = 0 -- Consecutive air nodes found

	local minp_schem, maxp_schem = abyss.get_schematic_volume({x = target_x, y = 0, z = target_z}, nil, "abyss_portal")
	local minp = {x = minp_schem.x, y = 0, z = minp_schem.z}
	local maxp = {x = maxp_schem.x, y = 0, z = maxp_schem.z}

	for y = start_y, math.max(NETHER_FLOOR + BLEND, start_y - 4096), -1 do
		local nval_cave = nobj_cave_point:get_3d({x = target_x, y = y, z = target_z})

		if nval_cave > TCAVE then -- Cavern
			air = air + 1
		else -- Not cavern, check if 4 nodes of space above
			if air >= 4 then
				local portal_y = y + 1
				-- Check volume for non-natural nodes
				minp.y = minp_schem.y + portal_y
				maxp.y = maxp_schem.y + portal_y
				if abyss.volume_is_natural_and_unprotected(minp, maxp, player_name) then
					return portal_y
				else -- Restart search a little lower
					abyss.find_nether_ground_y(target_x, target_z, y - 16, player_name)
				end
			else -- Not enough space, reset air to zero
				air = 0
			end
		end
	end

	return math.max(start_y, NETHER_FLOOR + BLEND) -- Fallback
end
