diff --git a/description.txt b/description.txt new file mode 100644 index 0000000..40a2ac7 --- /dev/null +++ b/description.txt @@ -0,0 +1 @@ +A mapgen mod that allows any image to be used as a heightmap. If the image is not grayscale then the red channel is used. A tool is provided to change settings in-game. diff --git a/init.lua b/init.lua index 4dad969..a8ffac6 100644 --- a/init.lua +++ b/init.lua @@ -1,25 +1,28 @@ +local realterrain = {} local ie = minetest.request_insecure_environment() ie.require "luarocks.loader" local imlib2 = ie.require "imlib2" -- Parameters local DEM = 'mandelbrot16bit.tif' -local COVER = 'cover.tif' +local COVER = 'cover.tif' --cover should only be an 8-bit file of the same dimensions as the DEM -local VERSCA = 5 -- Vertical scale, meters per node -local YWATER = 1 +realterrain.settings = {} --form persistence + +--defaults +realterrain.settings.yscale = 1 +realterrain.settings.xscale = 1 +realterrain.settings.zscale = 1 +realterrain.settings.waterlevel = 0 -offset = 0 --will be populated by ImageSize() ---local ImageSize = dofile(minetest.get_modpath("realterrain").."/lua-imagesize-1.2/imagesize.lua") local demfilename = minetest.get_modpath("realterrain").."/dem/"..DEM ---local width, length, format = ImageSize.imgsize(demfilename) local dem = imlib2.image.load(demfilename) local width = dem:get_width() local length = dem:get_height() print("width: "..width..", height: "..length) ---open the river tif with no safety checks -covertiff = io.open(minetest.get_modpath("realterrain").."/dem/"..COVER, "rb") +local coverfilename = minetest.get_modpath("realterrain").."/dem/"..COVER +local cover = imlib2.image.load(coverfilename) -- Set mapgen parameters @@ -63,7 +66,7 @@ minetest.register_on_generated(function(minp, maxp, seed) local elev, cover = get_pixel(x, z) -- elevation in meters from DEM and water true/false -- use demi to get elevation value from flat array - local node_elev = math.floor(YWATER + elev / VERSCA) + local node_elev = math.floor(tonumber(realterrain.settings.waterlevel) + elev / tonumber(realterrain.settings.yscale)) local vi = area:index(x, y0, z) -- voxelmanip index for y = y0, y1 do if y < node_elev then @@ -76,7 +79,7 @@ minetest.register_on_generated(function(minp, maxp, seed) elseif cover > 128 then data[vi] = c_stone else - if y <= YWATER then + if y <= tonumber(realterrain.settings.waterlevel) then data[vi] = c_sand else if y > 100 then @@ -86,7 +89,7 @@ minetest.register_on_generated(function(minp, maxp, seed) end end end - elseif y <= YWATER then + elseif y <= tonumber(realterrain.settings.waterlevel) then data[vi] = c_water end vi = vi + ystridevm @@ -108,17 +111,159 @@ end) --and a header offset of function get_pixel(x,z) - if x > math.ceil(width / 2) or x < - math.floor(width / 2) - or z > math.ceil(length / 2) or z < - math.floor(length / 2) then - --print ("out of range of tiff") - return -1, 0 --off the TIFF, + --local row = math.floor(length / 2) + (z / tonumber(realterrain.settings.zscale)) + --local col = math.floor(width / 2) + (x / tonumber(realterrain.settings.xscale)) + local row,col = 0+x, 0-z + local elev = dem:get_pixel(math.floor(col / tonumber(realterrain.settings.xscale)), math.floor(row / tonumber(realterrain.settings.zscale))) + local cover = cover:get_pixel(math.floor(col / tonumber(realterrain.settings.xscale)), math.floor(row / tonumber(realterrain.settings.zscale))) + return elev.red, cover.red +end + +-- the controller for changing map settings +minetest.register_tool("realterrain:remote" , { + description = "Realterrain Settings", + inventory_image = "remote.png", + --left-clicking the tool + on_use = function (itemstack, user, pointed_thing) + local pname = user:get_player_name() + realterrain.show_rc_form(pname) + end, +}) + +-- Processing the form from the RC +minetest.register_on_player_receive_fields(function(player, formname, fields) + if string.sub(formname, 1, 12) == "realterrain:" then + local wait = os.clock() + while os.clock() - wait < 0.05 do end --popups don't work without this see issue #30 + print("fields submitted: "..dump(fields)) + local pname = player:get_player_name() + + -- always save any form fields + for k,v in next, fields do + realterrain.settings[k] = v --we will preserve field entries exactly as entered + end + realterrain.save_settings() + if formname == "realterrain:popup" then + if fields.exit == "Back" then + realterrain.show_rc_form(pname) + return true + end + end + + --the main form + if formname == "realterrain:rc_form" then + --actual form submissions + if fields.exit == "Delete" then + minetest.chat_send_player(pname, "You changed mapgen settings and are deleting the current map, restart the world!") + --kick all other players + + --delete the map.sqlite file + + --kick this player? @todo what if this is a dedicated server? + + return true + elseif fields.exit == "Apply" then + minetest.chat_send_player(pname, "You changed the mapgen settings!") + return true + end + return true + end + return true end +end) +--the formspecs and related settings and functions / selected field variables + +--called at each form submission +function realterrain.save_settings() + local file = io.open(minetest.get_worldpath().."/realterrain_settings", "w") + if file then + for k,v in next, realterrain.settings do + local line = {key=k, values=v} + file:write(minetest.serialize(line).."\n") + end + file:close() + end +end +-- load settings run at EOF at mod start +function realterrain.load_settings() + local file = io.open(minetest.get_worldpath().."/realterrain_settings", "r") + if file then + for line in file:lines() do + if line ~= "" then + local tline = minetest.deserialize(line) + realterrain.settings[tline.key] = tline.values + end + end + file:close() + end +end +--retrieve individual form field +function realterrain.get_setting(setting) + if realterrain.settings ~= {} then + if realterrain.settings[setting] then + if realterrain.settings[setting] ~= "" then + return realterrain.settings[setting] + else + return false + end + else + return false + end + else + return false + end +end +-- show the main remote control form +function realterrain.show_rc_form(pname) + local player = minetest.get_player_by_name(pname) + local ppos = player:getpos() + local degree = player:get_look_yaw()*180/math.pi - 90 + if degree < 0 then degree = degree + 360 end + local dir + if degree <= 45 or degree > 315 then dir = "North" + elseif degree <= 135 then dir = "West" + elseif degree <= 225 then dir = "South" + else dir = "South" end - local row = math.floor(length / 2) + z - local col = math.floor(width / 2) + x - local pixel = dem:get_pixel(row,col) - --print(pixel.red) - --demtiff:seek("set", ( offset + (row * width) + col )) - covertiff:seek("set", ( offset + (row * width) + col )) - return pixel.red, 0 -end \ No newline at end of file + local yscale = realterrain.get_setting("yscale") + local xscale = realterrain.get_setting("xscale") + local zscale = realterrain.get_setting("zscale") + local waterlevel = realterrain.get_setting("waterlevel") + + --form header + local f_header = "size[12,10]" .. + --"tabheader[0,0;tab;1D, 2D, 3D, Import, Manage;"..tab.."]".. + "label[0,0;You are at x= "..math.floor(ppos.x).. + " y= "..math.floor(ppos.y).." z= "..math.floor(ppos.z).." and mostly facing "..dir.."]" + --Scale settings + local f_scale_settings = "field[1,4;4,1;yscale;Vertical Scale;"..minetest.formspec_escape(yscale).."]" .. + "field[1,5;4,1;xscale;East-West Scale;"..minetest.formspec_escape(xscale).."]" .. + "field[1,6;4,1;zscale;North-South Scale;"..minetest.formspec_escape(zscale).."]" .. + "field[1,7;4,1;waterlevel;Water Level;"..minetest.formspec_escape(waterlevel).."]" + --Action buttons + local f_footer = "label[3,8.5;Delete the map, reset]".. + "button_exit[3,9;2,1;exit;Delete]".. + "label[7,8.5;Reset the map only]".. + "button_exit[7,9;2,1;exit;Apply]" + + minetest.show_formspec(pname, "realterrain:rc_form", + f_header .. + f_scale_settings .. + f_footer + ) + return true +end + +-- this is the form-error popup +function realterrain.show_popup(pname, message) + minetest.chat_send_player(pname, "Form error: ".. message) + minetest.show_formspec(pname, "realterrain:popup", + "size[10,8]" .. + "button_exit[1,1;2,1;exit;Back]".. + "label[1,3;"..minetest.formspec_escape(message).."]" + ) + return true +end + +--read from file, various persisted settings +realterrain.load_settings() \ No newline at end of file diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..05e5517 Binary files /dev/null and b/screenshot.png differ diff --git a/textures/remote.png b/textures/remote.png new file mode 100644 index 0000000..3c7dfef Binary files /dev/null and b/textures/remote.png differ