local p=require(script.PerlinNoise)local d=require(script.Parent.Utils)local L={}local J={{1.0,1;true;true;true};{.5,2,true;true;true};{.3,4,true,true;true}}local W={{1.0;1;true,true},{.5;2;true,true};{.25,4;true;true};{.125;8,true,true}}local function F()local p={}p.octave_settings={}p.warp_octave_settings={}setmetatable(p,{__index=L})return p end function L.Generate(L,F,O,x,u,e,o)local r={}local g,U if not o then g=L:Generate(F+260,O+260,x,u,e,true)U=L:Generate(F+255,O+255,x,u,e,true)end local j if o then j=L.warp_octave_settings if not j or#j==0 then warn("\078\111 \119\097\114\112 \109\097\112 \111\099\116\097\118\101\115 \102\111\117\110\100\044 \117\115\105\110\103 \116\104\101 \100\101\102\097\117\108\116 \111\110\101\115")j=W end else j=L.octave_settings if not j or#j==0 then warn("\078\111 \104\101\105\103\104\116\109\097\112 \111\099\116\097\118\101\115 \102\111\117\110\100\044 \117\115\105\110\103 \116\104\101 \100\101\102\097\117\108\116 \111\110\101\115")j=J end end local v=-math.huge local V=math.huge for d=1,x,1 do r[d]={}for L=1,u,1 do local J=0 for W,x in pairs(j)do local u=x[1]local r=x[2]local j,v if x[5]and not o then j=(((d+F)+g[d][L]*15)/e)*r v=(((L+O)+U[d][L]*15)/e)*r else j=((d+F)/e)*r v=((L+O)/e)*r end local V if x[3]then V=p.Noise2DExpDistr(j,v)else V=p.Noise2D(j,v)end if x[4]then V=V*2-1 end J=J+V*u end if J>v then v=J end if J<V then V=J end r[d][L]=J end end for p=1,x,1 do for L=1,u,1 do r[p][L]=d.inverse_lerp(V,v,r[p][L])end end return r end return setmetatable({new=F},{__newindex=function()error("\072\101\105\103\104\116\109\097\112\071\101\110\101\114\097\116\111\114\058 \065\116\116\101\109\112\116 \116\111 \101\100\105\116 \097 \114\101\097\100-\111\110\108\121 \119\114\097\112\112\101\114\033 \077\097\107\101 \115\117\114\101 \116\104\097\116 \121\111\117 \099\114\101\097\116\101 \097 \110\101\119 \111\098\106\101\099\116 \102\105\114\115\116")end})
local PerlinNoise = require(script.PerlinNoise)
local Utils = require(script.Parent.Utils)

local HeightmapGeneratorBase = {}

--{ amplitude, frequency, exp_distr, bidirectional, warped }
local octave_settings_default = {
	 {1.0,1,true,true,true}
	,{0.5,2,true,true,true}
	,{0.3,4,true,true,true}
}

local warp_octave_settings_default = {
	 {1.000,1,true,true}
	,{0.500,2,true,true}
	,{0.250,4,true,true}
	,{0.125,8,true,true}
}

local function object_constructor()
	local object = {}
	object.octave_settings = {}
	object.warp_octave_settings = {}
	setmetatable(object,{__index = HeightmapGeneratorBase})

	return object
end

function HeightmapGeneratorBase.Generate(self,offsetX,offsetY,width,height,scale,generate_warp_map)
	local heightmap = {}

	local warp_map_x, warp_map_y

	if not generate_warp_map then
		warp_map_x = self:Generate(offsetX+260,offsetY+260,width,height,scale,true)
		warp_map_y = self:Generate(offsetX+255,offsetY+255,width,height,scale,true)
	end

	local octave_settings

	if generate_warp_map then
		octave_settings = self.warp_octave_settings
		if (not octave_settings) or #octave_settings == 0 then
			warn("No warp map octaves found, using the default ones")
			octave_settings = warp_octave_settings_default
		end
	else
		octave_settings = self.octave_settings
		if (not octave_settings) or #octave_settings == 0 then
			warn("No heightmap octaves found, using the default ones")
			octave_settings = octave_settings_default
		end
	end

	local max_noize_height = -math.huge
	local min_noize_height = math.huge

	for x = 1, width do

		heightmap[x] = {}

		for y = 1, height do

			local noise_height = 0

			for _,octave in pairs(octave_settings) do
				local amplitude = octave[1]
				local frequency = octave[2]

				local sampleX, sampleY

				if octave[5] and not generate_warp_map then
					sampleX = (x + offsetX + warp_map_x[x][y]*15) / scale * frequency
					sampleY = (y + offsetY + warp_map_y[x][y]*15) / scale * frequency
				else
					sampleX = (x + offsetX) / scale * frequency
					sampleY = (y + offsetY) / scale * frequency
				end

				local noise_val
				if octave[3] then
					noise_val = PerlinNoise.Noise2DExpDistr(sampleX,sampleY)
				else
					noise_val = PerlinNoise.Noise2D(sampleX,sampleY)
				end

				if octave[4] then
					noise_val = noise_val * 2 - 1
				end

				noise_height = noise_height + noise_val * amplitude
			end

			if noise_height > max_noize_height then
				max_noize_height = noise_height
			end
			if noise_height < min_noize_height then
				min_noize_height = noise_height
			end

			heightmap[x][y] = noise_height
		end
	end

	for x = 1,width do
		for y = 1,height do
			heightmap[x][y] = Utils.inverse_lerp(min_noize_height,max_noize_height,heightmap[x][y])
		end
	end

	return heightmap
end

return setmetatable(
	 {new = object_constructor}
	,{__newindex = function()
		error("HeightmapGenerator: Attempt to edit a read-only wrapper! Make sure that you create a new object first")
	end}
)
