788 lines
25 KiB
Lua
Raw Normal View History

-- If true, the Perlin test nodes will support color
-- (set to false in case of performance problems)
local COLORIZE_NODES = true
2022-04-15 21:43:23 +02:00
-- The number of available test node colors is divided by this number.
-- A number between 1 to 256
-- * 1 = Full 256 palette.
-- * 2 = 128 colors.
-- * 4 = 64 colors.
-- etc.
-- Higher values lead to less colors but increased performance.
-- This value is only used for performance reason, because Minetest
-- has a sharp performance drop when there are many different node colors on screen.
-- Consider removing this one when Minetest's performance problem has been solved.
local COLOR_PRECISION = 4
2022-04-15 05:55:17 +02:00
-- Color of the formspec box[] element
local FORMSPEC_BOX_COLOR = "#00000080"
2022-04-09 18:25:27 +02:00
local S = minetest.get_translator("perlin_explorer")
2022-04-10 01:45:19 +02:00
local F = minetest.formspec_escape
2022-04-09 18:23:01 +02:00
2022-04-15 05:55:17 +02:00
-- Per-player formspec states (mostly for remembering checkbox states)
2022-04-14 04:47:05 +02:00
local formspec_states = {}
2022-04-15 05:55:17 +02:00
-- List of noise parameters profiles
local np_profiles = {}
local default_noiseparams = {
2022-04-10 01:54:20 +02:00
offset = 0.0,
scale = 1.0,
spread = vector.new(10, 10, 10),
seed = 0,
octaves = 2,
2022-04-14 05:14:00 +02:00
persistence = 0.5,
2022-04-10 01:54:20 +02:00
lacunarity = 2.0,
2022-04-14 04:47:05 +02:00
flags = "noeased,noabsvalue",
2022-04-10 01:54:20 +02:00
}
2022-04-15 05:55:17 +02:00
-- Holds the currently used Perlin noise
local current_perlin = {}
-- holds the current PerlinNoise object
current_perlin.noise = nil
current_perlin.noiseparams = table.copy(default_noiseparams)
table.insert(np_profiles, current_perlin.noiseparams)
2022-04-10 00:33:16 +02:00
-- Side length of calculated perlin area
2022-04-11 02:21:07 +02:00
current_perlin.size = 64
2022-04-09 18:23:01 +02:00
-- Theoretical min and max values for Perlin noise (for colorization)
current_perlin.min = -1
2022-04-11 02:21:07 +02:00
current_perlin.max = 1
2022-04-09 18:23:01 +02:00
2022-04-09 21:43:33 +02:00
-- dimensions of current Perlin noise (2 or 3)
current_perlin.dimensions = 2
-- If greater than 1, the Perlin noise values are "pixelized". Noise values at
-- coordinates not divisible by sidelen will be set equal to the noise value
-- of the nearest number (counting downwards) that is divisible by sidelen.
-- This is (kind of) analogous to the "sidelen" parameter of mapgen decorations.
current_perlin.sidelen = 1
2022-04-11 02:21:07 +02:00
current_perlin.pos = {}
2022-04-14 03:07:42 +02:00
current_perlin.auto_build = true
current_perlin.loaded_areas = {}
------------
2022-04-09 21:43:33 +02:00
-- Helper functions
-- Reduce the pos coordinates down to the closest numbers divisible by sidelen
local sidelen_pos = function(pos, sidelen)
local newpos = table.copy(pos)
if sidelen <= 1 then
return newpos
end
2022-04-09 23:11:58 +02:00
newpos.x = newpos.x - newpos.x % sidelen
newpos.y = newpos.y - newpos.y % sidelen
newpos.z = newpos.z - newpos.z % sidelen
2022-04-09 21:43:33 +02:00
return newpos
end
2022-04-15 22:11:32 +02:00
-- Sets the currently active Perlin noise.
-- * noiseparams: NoiseParams table (see Minetest's Lua API documentation)
local set_perlin_noise = function(noiseparams)
current_perlin.noise = PerlinNoise(noiseparams)
current_perlin.noiseparams = noiseparams
end
2022-04-09 18:23:01 +02:00
-- Test nodes to generate a map based on a perlin noise
local paramtype2
if COLORIZE_NODES then
paramtype2 = "color"
end
2022-04-09 18:25:27 +02:00
minetest.register_node("perlin_explorer:node", {
2022-04-09 18:23:01 +02:00
description = S("Perlin Test Node"),
2022-04-10 01:54:20 +02:00
paramtype = "light",
sunlight_propagates = true,
paramtype2 = paramtype2,
2022-04-09 18:25:27 +02:00
tiles = {"perlin_explorer_node.png"},
palette = "perlin_explorer_node_palette.png",
2022-04-09 18:23:01 +02:00
groups = { dig_immediate = 3 },
-- Force-drop without metadata to avoid spamming the inventory
2022-04-09 18:25:27 +02:00
drop = "perlin_explorer:node",
2022-04-09 18:23:01 +02:00
})
2022-04-09 18:25:27 +02:00
minetest.register_node("perlin_explorer:node_negative", {
2022-04-09 18:23:01 +02:00
description = S("Negative Perlin Test Node"),
2022-04-10 01:54:20 +02:00
paramtype = "light",
sunlight_propagates = true,
paramtype2 = paramtype2,
2022-04-09 18:25:27 +02:00
tiles = {"perlin_explorer_node_neg.png"},
palette = "perlin_explorer_node_palette_neg.png",
2022-04-09 18:23:01 +02:00
groups = { dig_immediate = 3 },
-- Force-drop without metadata to avoid spamming the inventory
2022-04-09 18:25:27 +02:00
drop = "perlin_explorer:node_negative",
2022-04-09 18:23:01 +02:00
})
2022-04-09 20:39:15 +02:00
minetest.register_tool("perlin_explorer:getter", {
description = S("Perlin Value Getter"),
_tt_help = S("Punch a node to display the Perlin noise value at this position"),
inventory_image = "perlin_explorer_getter.png",
wield_image = "perlin_explorer_getter.png",
groups = { disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing)
2022-04-10 01:45:19 +02:00
if not user then
return
end
local privs = minetest.get_player_privs(user:get_player_name())
if not privs.server then
2022-04-10 01:54:20 +02:00
minetest.chat_send_player(user:get_player_name(), S("Insufficient privileges! You need the @1 privilege to use this tool.", "server"))
return
2022-04-10 01:45:19 +02:00
end
2022-04-09 20:39:15 +02:00
if current_perlin.noise then
if pointed_thing.type ~= "node" then
-- No-op for non-nodes
return
end
local pos = pointed_thing.under
2022-04-09 21:43:33 +02:00
local getpos = sidelen_pos(pos, current_perlin.sidelen)
2022-04-09 20:39:15 +02:00
local val
if current_perlin.dimensions == 2 then
2022-04-09 21:43:33 +02:00
val = current_perlin.noise:get_2d({x=getpos.x, y=getpos.z})
2022-04-09 21:00:12 +02:00
elseif current_perlin.dimensions == 3 then
2022-04-09 21:43:33 +02:00
val = current_perlin.noise:get_3d(getpos)
2022-04-09 21:00:12 +02:00
else
2022-04-09 21:43:33 +02:00
minetest.chat_send_player(user:get_player_name(), S("Unknown/invalid number of Perlin noise dimensions. Use /generate_perlin first!"))
2022-04-09 20:39:15 +02:00
end
minetest.chat_send_player(user:get_player_name(), S("pos=@1, value=@2", minetest.pos_to_string(pos), val))
else
2022-04-15 21:44:49 +02:00
local msg = S("No Perlin noise set. Set one first!")
2022-04-09 20:39:15 +02:00
minetest.chat_send_player(user:get_player_name(), msg)
end
end,
})
2022-04-15 22:11:32 +02:00
minetest.register_tool("perlin_explorer:seeder", {
description = S("Random Perlin seed setter"),
_tt_help = S("Set a random Perlin seed for the current Perlin noise params"),
inventory_image = "perlin_explorer_seeder.png",
wield_image = "perlin_explorer_seeder.png",
groups = { disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing)
if not user then
return
end
local privs = minetest.get_player_privs(user:get_player_name())
if not privs.server then
minetest.chat_send_player(user:get_player_name(), S("Insufficient privileges! You need the @1 privilege to use this tool.", "server"))
return
end
if current_perlin.noise then
local noiseparams = table.copy(current_perlin.noiseparams)
noiseparams.seed = math.random(0, 2^32-1)
set_perlin_noise(noiseparams)
current_perlin.loaded_areas = {}
local msg = S("New random seed set!")
minetest.chat_send_player(user:get_player_name(), msg)
else
local msg = S("No Perlin noise set. Set one first!")
minetest.chat_send_player(user:get_player_name(), msg)
end
end,
})
2022-04-09 20:39:15 +02:00
2022-04-09 18:25:27 +02:00
local CONTENT_TEST_NODE = minetest.get_content_id("perlin_explorer:node")
local CONTENT_TEST_NODE_NEGATIVE = minetest.get_content_id("perlin_explorer:node_negative")
2022-04-09 18:23:01 +02:00
2022-04-09 23:17:35 +02:00
local update_map = function(pos, set_nodes)
2022-04-09 18:23:01 +02:00
local stats
if not current_perlin.noise then
return
end
2022-04-10 00:33:16 +02:00
local size_v = vector.new(current_perlin.size, current_perlin.size, current_perlin.size)
2022-04-09 18:23:01 +02:00
local startpos = pos
2022-04-10 00:33:16 +02:00
local endpos = vector.add(startpos, current_perlin.size-1)
2022-04-09 18:23:01 +02:00
local y_max = endpos.y - startpos.y
if current_perlin.dimensions == 2 then
y_max = 0
startpos.y = pos.y
endpos.y = pos.y
end
2022-04-09 23:17:35 +02:00
local vmanip, emin, emax, vdata, vdata2, varea
if set_nodes then
vmanip = VoxelManip(startpos, endpos)
emin, emax = vmanip:get_emerged_area()
vdata = vmanip:get_data()
vdata2 = vmanip:get_param2_data()
varea = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
end
2022-04-09 21:43:33 +02:00
2022-04-09 18:23:01 +02:00
stats = {}
stats.avg = 0
local sum_of_values = 0
local value_count = 0
for x=0, endpos.x - startpos.x do
for y=0, y_max do
for z=0, endpos.z - startpos.z do
-- Get Perlin value at current pos
local relpos = vector.new(x,y,z)
local abspos = vector.add(startpos, relpos)
2022-04-09 21:43:33 +02:00
local abspos_get = sidelen_pos(abspos, current_perlin.sidelen)
2022-04-09 18:23:01 +02:00
local perlin_value
if current_perlin.dimensions == 2 then
2022-04-09 21:43:33 +02:00
perlin_value = current_perlin.noise:get_2d({x=abspos_get.x, y=abspos_get.z})
2022-04-09 18:23:01 +02:00
elseif current_perlin.dimensions == 3 then
2022-04-09 21:43:33 +02:00
perlin_value = current_perlin.noise:get_3d(abspos_get)
2022-04-09 18:23:01 +02:00
else
2022-04-09 18:25:27 +02:00
error("[perlin_explorer] Unknown/invalid number of Perlin noise dimensions!")
2022-04-09 18:23:01 +02:00
return
end
-- Statistics
if not stats.min then
stats.min = perlin_value
elseif perlin_value < stats.min then
stats.min = perlin_value
end
if not stats.max then
stats.max = perlin_value
elseif perlin_value > stats.max then
stats.max = perlin_value
end
sum_of_values = sum_of_values + perlin_value
value_count = value_count + 1
-- Calculate color (param2) for node
2022-04-09 22:48:27 +02:00
local zeropoint = 0
local min_size = zeropoint - current_perlin.min
local max_size = current_perlin.max - zeropoint
local node_param2
if perlin_value >= zeropoint then
node_param2 = (math.abs(perlin_value) / max_size) * 255
else
node_param2 = (math.abs(perlin_value) / min_size) * 255
end
node_param2 = math.floor(math.abs(node_param2))
2022-04-09 18:23:01 +02:00
node_param2 = math.max(0, math.min(255, node_param2))
2022-04-15 21:43:23 +02:00
if node_param2 < 255 then
node_param2 = node_param2 - (node_param2 % COLOR_PRECISION)
end
2022-04-09 18:23:01 +02:00
2022-04-09 23:17:35 +02:00
if set_nodes then
-- Get vmanip index
local index = varea:indexp(abspos)
if not index then
return
end
2022-04-09 18:23:01 +02:00
2022-04-09 23:17:35 +02:00
-- Set node and param2
if perlin_value >= zeropoint then
vdata[index] = CONTENT_TEST_NODE
2022-04-09 18:23:01 +02:00
vdata2[index] = node_param2
else
2022-04-09 23:17:35 +02:00
if current_perlin.show_negative == true then
vdata[index] = CONTENT_TEST_NODE_NEGATIVE
vdata2[index] = node_param2
else
vdata[index] = minetest.CONTENT_AIR
vdata2[index] = 0
end
2022-04-09 18:23:01 +02:00
end
end
end
end
end
stats.avg = sum_of_values / value_count
2022-04-09 23:17:35 +02:00
if set_nodes then
-- Set vmanip, return stats
vmanip:set_data(vdata)
vmanip:set_param2_data(vdata2)
vmanip:write_to_map()
end
2022-04-09 18:23:01 +02:00
return stats
end
-- Creates and demonstrates a Perlin noise.
-- * pos: Where the Perlin noise starts
-- * options: table with:
-- * dimensions: number of Perlin noise dimensions (2 or 3)
2022-04-10 00:33:16 +02:00
-- * size: side length of area/volume to calculate)
2022-04-09 19:44:15 +02:00
-- * show_negative: if true, places nodes for negative Perlin values (default: true for 2 dimensions and false for 3 dimensions)
2022-04-09 23:17:35 +02:00
-- * set_nodes: if true, will set nodes, otherwise it's a "dry run"
local create_perlin = function(pos, options)
2022-04-09 19:44:15 +02:00
if not current_perlin.noise then
return false
end
2022-04-09 18:23:01 +02:00
current_perlin.dimensions = options.dimensions
2022-04-10 00:33:16 +02:00
current_perlin.size = options.size
2022-04-09 18:23:01 +02:00
current_perlin.show_negative = options.show_negative
if current_perlin.show_negative == nil then
2022-04-09 19:44:15 +02:00
if current_perlin.dimensions == 2 then
2022-04-09 20:39:15 +02:00
current_perlin.show_negative = true
2022-04-09 19:44:15 +02:00
elseif current_perlin.dimensions == 3 then
2022-04-09 20:39:15 +02:00
current_perlin.show_negative = false
2022-04-09 19:44:15 +02:00
end
2022-04-09 18:23:01 +02:00
end
local mpos = vector.round(pos)
2022-04-11 02:21:07 +02:00
current_perlin.pos = mpos
2022-04-09 23:17:35 +02:00
local set_nodes = options.set_nodes ~= false
local stats = update_map(mpos, set_nodes)
2022-04-09 18:23:01 +02:00
if stats then
2022-04-09 19:44:15 +02:00
return string.format("Perlin noise created! Stats: min. value=%.3f, max. value=%.3f, avg. value=%.3f", stats.min, stats.max, stats.avg)
2022-04-09 18:23:01 +02:00
end
end
2022-04-09 21:43:33 +02:00
minetest.register_chatcommand("perlin_set_options", {
privs = { server = true },
description = S("Set Perlin options"),
2022-04-09 22:48:27 +02:00
params = S("<dimensions> <sidelen> <minval> <maxval>"),
2022-04-09 21:43:33 +02:00
func = function(name, param)
2022-04-09 22:48:27 +02:00
local dimensions, sidelen, min, max = string.match(param, "([23]) ([0-9]+) ([0-9.-]+) ([0-9.-]+)")
if not dimensions then
2022-04-09 21:43:33 +02:00
return false
end
dimensions = tonumber(dimensions)
sidelen = tonumber(sidelen)
2022-04-09 22:48:27 +02:00
min = tonumber(min)
max = tonumber(max)
if not dimensions or not sidelen or not min or not max then
2022-04-09 21:43:33 +02:00
return false, S("Invalid parameter type.")
end
current_perlin.dimensions = dimensions
current_perlin.sidelen = sidelen
2022-04-09 22:48:27 +02:00
current_perlin.min = min
current_perlin.max = max
2022-04-14 03:07:42 +02:00
current_perlin.loaded_areas = {}
2022-04-09 21:43:33 +02:00
return true, S("Perlin options set!")
end,
})
2022-04-09 23:17:35 +02:00
2022-04-09 21:43:33 +02:00
minetest.register_chatcommand("perlin_set_noise", {
2022-04-09 19:44:15 +02:00
privs = { server = true },
description = S("Set Perlin noise parameters"),
params = S("<octaves> <offset> <scale> <spread_x> <spread_y> <spread_z> <persistence> <lacunarity> <seed>"),
func = function(name, param)
local octaves, offset, scale, sx, sy, sz, persistence, lacunarity, seed = string.match(param, string.rep("([0-9.-]+) ", 8) .. "([0-9.-]+)")
if not octaves then
return false
end
octaves = tonumber(octaves)
offset = tonumber(offset)
sx = tonumber(sx)
sy = tonumber(sy)
sz = tonumber(sz)
persistence = tonumber(persistence)
lacunarity = tonumber(lacunarity)
seed = tonumber(seed)
if not octaves or not offset or not sx or not sy or not sz or not persistence or not lacunarity or not seed then
2022-04-09 21:43:33 +02:00
return false, S("Invalid parameter type.")
2022-04-09 19:44:15 +02:00
end
set_perlin_noise({
octaves = octaves,
offset = offset,
scale = scale,
spread = { x = sx, y = sy, z = sz },
persistence = persistence,
lacunarity = lacunarity,
seed = seed,
})
2022-04-14 03:07:42 +02:00
current_perlin.loaded_areas = {}
2022-04-09 19:44:15 +02:00
return true, S("Perlin noise set!")
end,
})
minetest.register_chatcommand("perlin_generate", {
privs = { server = true },
description = S("Generate Perlin noise"),
2022-04-10 00:33:16 +02:00
params = S("<pos> <size>"),
2022-04-09 19:44:15 +02:00
func = function(name, param)
2022-04-10 00:33:16 +02:00
local x, y, z, size = string.match(param, "([0-9.-]+) ([0-9.-]+) ([0-9.-]+) ([0-9]+)")
2022-04-09 20:39:15 +02:00
if not x then
2022-04-09 19:44:15 +02:00
return false
end
x = tonumber(x)
y = tonumber(y)
z = tonumber(z)
2022-04-10 00:33:16 +02:00
size = tonumber(size)
if not x or not y or not z or not size then
2022-04-09 19:44:15 +02:00
return false
end
2022-04-10 00:33:16 +02:00
if not x or not y or not z or not size then
2022-04-09 19:44:15 +02:00
return false, S("Invalid parameter type.")
end
local pos = vector.new(x, y, z)
minetest.chat_send_player(name, S("Creating Perlin noise, please wait …"))
2022-04-10 00:33:16 +02:00
local msg = create_perlin(pos, {dimensions=current_perlin.dimensions, size=size})
2022-04-09 19:44:15 +02:00
if msg == false then
2022-04-09 21:43:33 +02:00
return false, S("No Perlin noise set. Set one with '/perlin_set_noise' first!")
2022-04-09 19:44:15 +02:00
end
return true, msg
end,
})
2022-04-14 04:47:05 +02:00
local build_flags_string = function(eased, absvalue)
local flags = ""
if eased then
flags = "eased"
else
flags = "noeased"
end
if absvalue then
flags = flags .. ",absvalue"
else
flags = flags .. ",noabsvalue"
end
return flags
end
local parse_flags_string = function(flags)
local ftable = string.split(flags, ",")
local eased, absvalue = false, false
for f=1, #ftable do
local s = string.trim(ftable[f])
if s == "eased" then
eased = true
elseif s == "absvalue" then
absvalue = true
end
end
return { eased = eased, absvalue = absvalue }
end
local show_formspec = function(player, noiseparams, profile_id)
2022-04-15 20:49:01 +02:00
local np
if noiseparams then
np = noiseparams
2022-04-15 20:49:01 +02:00
else
np = current_perlin.noiseparams
end
if not profile_id then
2022-04-15 20:49:01 +02:00
profile_id = 1
end
2022-04-15 06:55:11 +02:00
local offset = tostring(np.offset or "")
local scale = tostring(np.scale or "")
local seed = tostring(np.seed or "")
2022-04-10 01:45:19 +02:00
local sx, sy, sz = "", "", ""
2022-04-15 06:55:11 +02:00
if np.spread then
sx = tostring(np.spread.x or "")
sy = tostring(np.spread.y or "")
sz = tostring(np.spread.z or "")
2022-04-10 01:45:19 +02:00
end
2022-04-15 06:55:11 +02:00
local octaves = tostring(np.octaves or "")
local persistence = tostring(np.persistence or "")
local lacunarity = tostring(np.lacunarity or "")
2022-04-11 02:21:07 +02:00
local size = tostring(current_perlin.size or "")
local sidelen = tostring(current_perlin.sidelen or "")
local pos_x = tostring(current_perlin.pos.x or "")
local pos_y = tostring(current_perlin.pos.y or "")
local pos_z = tostring(current_perlin.pos.z or "")
local value_min = tostring(current_perlin.min or "")
local value_max = tostring(current_perlin.max or "")
2022-04-15 06:55:11 +02:00
local flags = np.flags
2022-04-14 04:47:05 +02:00
local flags_table = parse_flags_string(flags)
local eased = tostring(flags_table.eased)
local absvalue = tostring(flags_table.absvalue)
2022-04-15 05:55:17 +02:00
local noiseparams_list = {}
for i=1, #np_profiles do
table.insert(noiseparams_list, S("Profile @1", i))
end
local noiseparams_list_str = table.concat(noiseparams_list, ",")
2022-04-10 23:41:41 +02:00
local dimensions_index = (current_perlin.dimensions or 2) - 1
2022-04-15 20:49:01 +02:00
local delete_btn = ""
if #np_profiles > 1 then
2022-04-15 20:53:11 +02:00
delete_btn = "button[7.25,0.5;2.0,0.5;delete_np_profile;"..F(S("Delete")).."]"
2022-04-15 20:49:01 +02:00
end
2022-04-10 01:45:19 +02:00
local form = [[
2022-04-11 02:21:07 +02:00
formspec_version[4]size[10,11]
2022-04-15 03:17:19 +02:00
container[0.25,0.25]
2022-04-15 05:55:17 +02:00
box[0,0;9.5,5.5;]]..FORMSPEC_BOX_COLOR..[[]
2022-04-15 03:17:19 +02:00
label[0.15,0.25;]]..F(S("Noise parameters"))..[[]
2022-04-15 20:49:01 +02:00
dropdown[0.25,0.5;2,0.5;np_profiles;]]..noiseparams_list_str..[[;]]..profile_id..[[;true]
2022-04-15 20:53:11 +02:00
button[3.25,0.5;2.0,0.5;add_np_profile;]]..F(S("Add"))..[[]
button[5.25,0.5;2.0,0.5;load_np_profile;]]..F(S("Load"))..[[]
2022-04-15 20:49:01 +02:00
]]..delete_btn..[[
2022-04-15 05:55:17 +02:00
field[0.25,1.75;2,0.75;offset;]]..F(S("Offset"))..[[;]]..offset..[[]
field[3.25,1.75;2,0.75;scale;]]..F(S("Scale"))..[[;]]..scale..[[]
field[6.25,1.75;2,0.75;seed;]]..F(S("Seed"))..[[;]]..seed..[[]
2022-04-15 22:18:53 +02:00
image_button[8.35,1.75;0.75,0.75;perlin_explorer_seeder.png;set_random_seed;]
2022-04-15 05:55:17 +02:00
field[0.25,3.0;2,0.75;spread_x;]]..F(S("X Spread"))..[[;]]..sx..[[]
field[3.25,3.0;2,0.75;spread_y;]]..F(S("Y Spread"))..[[;]]..sy..[[]
field[6.25,3.0;2,0.75;spread_z;]]..F(S("Z Spread"))..[[;]]..sz..[[]
field[0.25,4.25;2,0.75;octaves;]]..F(S("Octaves"))..[[;]]..octaves..[[]
field[3.25,4.25;2,0.75;persistence;]]..F(S("Persistence"))..[[;]]..persistence..[[]
field[6.25,4.25;2,0.75;lacunarity;]]..F(S("Lacunarity"))..[[;]]..lacunarity..[[]
checkbox[0.5,5.25;eased;]]..F(S("eased"))..[[;]]..eased..[[]
checkbox[3.5,5.25;absvalue;]]..F(S("absvalue"))..[[;]]..absvalue..[[]
2022-04-15 22:18:53 +02:00
tooltip[set_random_seed;]]..F(S("Random seed"))..[[]
2022-04-15 03:17:19 +02:00
container_end[]
2022-04-15 05:55:17 +02:00
container[0.25,6.0]
2022-04-15 03:17:19 +02:00
label[0.25,0.0;]]..F(S("Dimensions"))..[[]
2022-04-15 05:55:17 +02:00
dropdown[0.25,0.2;1,0.75;dimensions;]]..F(S("2D"))..[[,]]..F(S("3D"))..[[;]]..dimensions_index..[[;true]
2022-04-15 03:17:19 +02:00
field[6.25,0.2;2,0.75;sidelen;]]..F(S("Sidelen"))..[[;]]..sidelen..[[]
field[0.25,1.7;2,0.75;pos_x;]]..F(S("X"))..[[;]]..pos_x..[[]
field[2.25,1.7;2,0.75;pos_y;]]..F(S("Y"))..[[;]]..pos_y..[[]
field[4.25,1.7;2,0.75;pos_z;]]..F(S("Z"))..[[;]]..pos_z..[[]
field[3.25,0.2;2,0.75;size;]]..F(S("Size"))..[[;]]..size..[[]
field[0.25,2.7;2,0.75;value_min;]]..F(S("Min."))..[[;]]..value_min..[[]
field[2.25,2.7;2,0.75;value_max;]]..F(S("Max."))..[[;]]..value_max..[[]
container_end[]
2022-04-11 02:21:07 +02:00
2022-04-10 01:45:19 +02:00
field_close_on_enter[offset;false]
field_close_on_enter[scale;false]
field_close_on_enter[seed;false]
field_close_on_enter[spread_x;false]
field_close_on_enter[spread_y;false]
field_close_on_enter[spread_z;false]
field_close_on_enter[octaves;false]
field_close_on_enter[persistence;false]
field_close_on_enter[lacunarity;false]
2022-04-11 02:21:07 +02:00
field_close_on_enter[sidelen;false]
field_close_on_enter[pos_x;false]
field_close_on_enter[pos_y;false]
field_close_on_enter[pos_z;false]
field_close_on_enter[value_min;false]
field_close_on_enter[value_max;false]
2022-04-10 01:45:19 +02:00
2022-04-15 03:17:19 +02:00
container[0,9.5]
button[0.5,0;3,1;apply;]]..F(S("Apply"))..[[]
button[3.5,0;3,1;create;]]..F(S("Apply and create"))..[[]
button_exit[6.5,0;3,1;close;]]..F(S("Close"))..[[]
container_end[]
2022-04-10 01:45:19 +02:00
]]
minetest.show_formspec(player:get_player_name(), "perlin_explorer:creator", form)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "perlin_explorer:creator" then
return
end
2022-04-10 01:46:30 +02:00
-- Require 'server' priv
local privs = minetest.get_player_privs(player:get_player_name())
if not privs.server then
return
end
2022-04-14 04:47:05 +02:00
-- Handle checkboxes
local name = player:get_player_name()
2022-04-15 06:55:11 +02:00
local flags_touched = false
2022-04-14 04:47:05 +02:00
if fields.eased == "true" then
formspec_states[name].eased = true
2022-04-15 21:45:32 +02:00
return
2022-04-14 04:47:05 +02:00
elseif fields.eased == "false" then
formspec_states[name].eased = false
2022-04-15 21:45:32 +02:00
return
2022-04-14 04:47:05 +02:00
end
if fields.absvalue == "true" then
formspec_states[name].absvalue = true
2022-04-15 21:45:32 +02:00
return
2022-04-14 04:47:05 +02:00
elseif fields.absvalue == "false" then
formspec_states[name].absvalue = false
2022-04-15 21:45:32 +02:00
return
2022-04-14 04:47:05 +02:00
end
2022-04-15 05:55:17 +02:00
-- Deleting a profile does not require any other field
if fields.delete_np_profile then
if #np_profiles <= 1 then
return
end
local profile_to_delete = tonumber(fields.np_profiles)
table.remove(np_profiles, profile_to_delete)
local new_id = math.max(1, profile_to_delete - 1)
show_formspec(player, noiseparams, new_id)
2022-04-15 05:55:17 +02:00
return
end
2022-04-14 04:47:05 +02:00
-- Handle other fields
2022-04-10 01:45:19 +02:00
local do_apply = fields.apply ~= nil
local do_create = fields.create ~= nil or fields.key_enter_field ~= nil
2022-04-15 05:55:17 +02:00
if (do_create or do_apply or fields.add_np_profile or fields.np_profiles) then
2022-04-10 01:45:19 +02:00
if fields.offset and fields.scale and fields.seed and fields.spread_x and fields.spread_y and fields.spread_z and fields.octaves and fields.persistence and fields.lacunarity then
2022-04-15 05:55:17 +02:00
2022-04-14 03:07:42 +02:00
current_perlin.loaded_areas = {}
2022-04-10 01:45:19 +02:00
local offset = tonumber(fields.offset)
local scale = tonumber(fields.scale)
local seed = tonumber(fields.seed)
local sx = tonumber(fields.spread_x)
local sy = tonumber(fields.spread_y)
local sz = tonumber(fields.spread_z)
2022-04-10 03:33:22 +02:00
if not sx or not sy or not sz then
return
end
2022-04-10 01:45:19 +02:00
local spread = vector.new(sx, sy, sz)
local octaves = tonumber(fields.octaves)
local persistence = tonumber(fields.persistence)
local lacunarity = tonumber(fields.lacunarity)
2022-04-10 23:41:41 +02:00
local dimensions = tonumber(fields.dimensions)
2022-04-11 02:21:07 +02:00
local sidelen = tonumber(fields.sidelen)
local px = tonumber(fields.pos_x)
local py = tonumber(fields.pos_y)
local pz = tonumber(fields.pos_z)
local size = tonumber(fields.size)
local value_min = tonumber(fields.value_min)
local value_max = tonumber(fields.value_max)
2022-04-15 05:55:17 +02:00
if (offset and scale and spread and octaves and persistence) then
2022-04-14 04:47:05 +02:00
local eased = formspec_states[name].eased
local absvalue = formspec_states[name].absvalue
2022-04-15 05:55:17 +02:00
local noiseparams = {
2022-04-10 01:45:19 +02:00
offset = offset,
scale = scale,
seed = seed,
spread = spread,
octaves = octaves,
persistence = persistence,
lacunarity = lacunarity,
2022-04-14 04:47:05 +02:00
flags = build_flags_string(eased, absvalue),
2022-04-15 05:55:17 +02:00
}
-- Change NP profile selection
2022-04-15 20:49:01 +02:00
if fields.load_np_profile and fields.np_profiles then
local profile = tonumber(fields.np_profiles)
local loaded_np = np_profiles[profile]
2022-04-15 20:49:01 +02:00
-- Load new profile
show_formspec(player, loaded_np, profile)
2022-04-15 20:49:01 +02:00
minetest.log("action", "[perlin_explorer] Loaded perlin noise profile "..profile)
return
2022-04-15 20:53:11 +02:00
-- Add new profile and save current noiseparams to it
2022-04-15 20:49:01 +02:00
elseif fields.add_np_profile then
2022-04-15 05:55:17 +02:00
table.insert(np_profiles, noiseparams)
2022-04-15 20:49:01 +02:00
local new_profile = #np_profiles
minetest.log("action", "[perlin_explorer] Perlin noise profile "..new_profile.." added!")
show_formspec(player, noiseparams, new_profile)
2022-04-15 05:55:17 +02:00
return
2022-04-15 22:18:53 +02:00
elseif fields.set_random_seed then
-- Randomize seed
local profile = tonumber(fields.np_profiles)
noiseparams.seed = math.random(0, 2^32-1)
show_formspec(player, noiseparams, profile)
return
2022-04-15 05:55:17 +02:00
end
2022-04-15 22:18:53 +02:00
2022-04-15 05:55:17 +02:00
if not (dimensions and sidelen and size and value_min and value_max) then
return
end
-- Convert dropdown index to actual dimensions number
dimensions = dimensions + 1
-- Spread is used differently in 2D
if dimensions == 2 then
spread.y = spread.z
end
set_perlin_noise(noiseparams)
minetest.log("action", "[perlin_explorer] Perlin noise set!")
2022-04-11 02:21:07 +02:00
current_perlin.dimensions = dimensions
current_perlin.size = size
current_perlin.sidelen = sidelen
current_perlin.min = value_min
current_perlin.max = value_max
2022-04-10 01:45:19 +02:00
if do_create then
2022-04-15 05:55:17 +02:00
if not px or not py or not pz then
return
end
local place_pos = vector.new(px, py, pz)
2022-04-13 22:35:29 +02:00
local msg = S("Creating Perlin noise, please wait …")
2022-04-14 04:47:05 +02:00
minetest.chat_send_player(name, msg)
2022-04-13 22:35:29 +02:00
msg = create_perlin(place_pos, {dimensions=dimensions, size=size})
2022-04-11 02:21:07 +02:00
if msg then
2022-04-14 04:47:05 +02:00
minetest.chat_send_player(name, msg)
2022-04-11 02:21:07 +02:00
elseif msg == false then
2022-04-10 01:45:19 +02:00
minetest.log("error", "[perlin_explorer] Error generating Perlin noise nodes!")
end
end
end
end
end
end)
minetest.register_tool("perlin_explorer:creator", {
description = S("Perlin Noise Creator"),
_tt_help = S("Punch to open the Perlin noise creation menu"),
inventory_image = "perlin_explorer_creator.png",
wield_image = "perlin_explorer_creator.png",
groups = { disable_repair = 1 },
on_use = function(itemstack, user, pointed_thing)
if not user then
return
end
local privs = minetest.get_player_privs(user:get_player_name())
if not privs.server then
2022-04-10 01:54:20 +02:00
minetest.chat_send_player(user:get_player_name(), S("Insufficient privileges! You need the @1 privilege to use this tool.", "server"))
return
2022-04-10 01:45:19 +02:00
end
show_formspec(user)
end,
})
2022-04-14 03:07:42 +02:00
local TIME_UPDATE = 1
local AUTOBUILD_SIZE = 16
local timer = 0
minetest.register_globalstep(function(dtime)
timer = timer + dtime
if timer < TIME_UPDATE then
return
end
timer = 0
if current_perlin.noise and current_perlin.auto_build then
local player = minetest.get_player_by_name("singleplayer")
if not player then
return
end
local build = function(pos, player_name)
local hash = minetest.hash_node_position(pos)
if not current_perlin.loaded_areas[hash] then
local msg = create_perlin(pos, {
dimensions = current_perlin.dimensions,
size = AUTOBUILD_SIZE,
})
minetest.chat_send_player(player_name, msg)
current_perlin.loaded_areas[hash] = true
end
end
2022-04-09 19:44:15 +02:00
2022-04-14 03:07:42 +02:00
local pos = vector.round(player:get_pos())
pos = sidelen_pos(pos, AUTOBUILD_SIZE)
local neighbors = {
vector.new(0, 0, 0),
vector.new(0, 0, -1),
vector.new(0, 0, 1),
vector.new(0, -1, -1),
vector.new(0, -1, 0),
vector.new(0, -1, 1),
vector.new(0, 1, -1),
vector.new(0, 1, 0),
vector.new(0, 1, 1),
vector.new(-1, -1, -1),
vector.new(-1, -1, 0),
vector.new(-1, -1, 1),
vector.new(-1, 0, -1),
vector.new(-1, 0, 0),
vector.new(-1, 0, 1),
vector.new(-1, 1, -1),
vector.new(-1, 1, 0),
vector.new(-1, 1, 1),
vector.new(1, -1, -1),
vector.new(1, -1, 0),
vector.new(1, -1, 1),
vector.new(1, 0, -1),
vector.new(1, 0, 0),
vector.new(1, 0, 1),
vector.new(1, 1, -1),
vector.new(1, 1, 0),
vector.new(1, 1, 1),
}
for n=1, #neighbors do
2022-04-15 22:26:49 +02:00
if current_perlin.dimensions == 3 or neighbors[n].y == 0 then
local offset = vector.multiply(neighbors[n], 16)
local npos = vector.add(pos, offset)
build(npos, player:get_player_name())
end
2022-04-14 03:07:42 +02:00
end
end
end)
2022-04-14 04:47:05 +02:00
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
formspec_states[name] = {
eased = false,
}
end)
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
formspec_states[name] = nil
end)