This commit is contained in:
root 2021-04-10 09:51:05 +02:00
parent d412c25a61
commit fef615b588
27 changed files with 1486 additions and 372 deletions

View File

@ -446,12 +446,22 @@ question is already loaded, or false if not.
=====
dbg(string)
dbg(string, level)
This is a simple debug output function which takes one string parameter. It
just checks if DEBUG is true and outputs the phrase "[Plantlife] " followed by
the supplied string, via the print() function, if so.
'level' is a number that, if supplied, dictates the lowest 'biome_lib_debug'
can be set to in minetest.conf for this message to be displayed. Both the
default log level and the default message level are 0, thus always showing the
supplied message.
Although it's not set in stone, a good practice is to use a level of 0 (or
just omit the value) for anything that generally important enough that it
ought always be shown, 1 for errors, 2 for warnings, 3 for info, 4 for verbose
spammy stuff.
=====
biome_lib:generate_tree(pos, treemodel)
biome_lib:grow_tree(pos, treemodel)

View File

@ -4,37 +4,42 @@
-- Splizard's snow mod.
--
biome_lib = {}
-- Boilerplate to support localized strings if intllib mod is installed.
local S
if minetest.global_exists("intllib") then
if intllib.make_gettext_pair then
S = intllib.make_gettext_pair()
else
S = intllib.Getter()
end
else
S = function(s) return s end
end
biome_lib.intllib = S
-- Various settings - most of these probably won't need to be changed
biome_lib = {}
biome_lib.air = {name = "air"}
biome_lib.blocklist_aircheck = {}
biome_lib.blocklist_no_aircheck = {}
biome_lib.surface_nodes_aircheck = {}
biome_lib.surface_nodes_no_aircheck = {}
biome_lib.surfaceslist_aircheck = {}
biome_lib.surfaceslist_no_aircheck = {}
biome_lib.actioncount_aircheck = {}
biome_lib.actioncount_no_aircheck = {}
biome_lib.block_log = {}
biome_lib.block_recheck_list = {}
biome_lib.run_block_recheck_list = false
biome_lib.actionslist_aircheck = {}
biome_lib.actionslist_no_aircheck = {}
biome_lib.surfaceslist_aircheck = {}
biome_lib.surfaceslist_no_aircheck = {}
biome_lib.modpath = minetest.get_modpath("biome_lib")
biome_lib.total_no_aircheck_calls = 0
biome_lib.queue_run_ratio = tonumber(minetest.settings:get("biome_lib_queue_run_ratio")) or 100
local function tableize(s)
return string.split(string.trim(string.gsub(s, " ", "")))
end
local c1 minetest.settings:get("biome_lib_default_grow_through_nodes")
local c1 = minetest.settings:get("biome_lib_default_grow_through_nodes")
biome_lib.default_grow_through_nodes = {["air"] = true}
if c1 then
for _, i in ipairs(tableize(c1)) do
@ -44,7 +49,7 @@ else
biome_lib.default_grow_through_nodes["default:snow"] = true
end
local c2 minetest.settings:get("biome_lib_default_water_nodes")
local c2 = minetest.settings:get("biome_lib_default_water_nodes")
biome_lib.default_water_nodes = {}
if c2 then
for _, i in ipairs(tableize(c2)) do
@ -65,29 +70,27 @@ biome_lib.default_wet_surfaces = c3 and tableize(c3) or {"default:dirt", "defaul
biome_lib.default_ground_nodes = c4 and tableize(c4) or {"default:dirt_with_grass"}
biome_lib.default_grow_nodes = c5 and tableize(c5) or {"default:dirt_with_grass"}
-- Boilerplate to support localized strings if intllib mod is installed.
local S
if minetest.global_exists("intllib") then
if intllib.make_gettext_pair then
S = intllib.make_gettext_pair()
else
S = intllib.Getter()
end
else
S = function(s) return s end
end
biome_lib.intllib = S
biome_lib.debug_log_level = tonumber(minetest.settings:get("biome_lib_debug_log_level")) or 0
local DEBUG = minetest.settings:get_bool("biome_lib_debug", false)
local rr = tonumber(minetest.settings:get("biome_lib_queue_run_ratio")) or -100
biome_lib.queue_run_ratio = 100 - rr
biome_lib.entries_per_step = math.max(-rr, 1)
function biome_lib:dbg(msg)
if DEBUG then
print("[Biome Lib] "..msg)
minetest.log("verbose", "[Biome Lib] "..msg)
end
end
-- the timer that manages the block timeout is in microseconds, but the timer
-- that manages the queue wakeup call has to be in seconds, and works best if
-- it takes a fraction of the block timeout interval.
biome_lib.plantlife_seed_diff = 329 -- needs to be global so other mods can see it
local t = tonumber(minetest.settings:get("biome_lib_block_timeout")) or 300
biome_lib.block_timeout = t * 1000000
-- we don't want the wakeup function to trigger TOO often,
-- in case the user's block timeout setting is really low
biome_lib.block_queue_wakeup_time = math.min(t/2, math.max(20, t/10))
local time_speed = tonumber(minetest.settings:get("time_speed"))
biome_lib.plantlife_seed_diff = 329 -- needs to be global so other mods can see it
local perlin_octaves = 3
local perlin_persistence = 0.6
@ -104,7 +107,6 @@ local humidity_persistence = 0.5
local humidity_scale = 250
local time_scale = 1
local time_speed = tonumber(minetest.settings:get("time_speed"))
if time_speed and time_speed > 0 then
time_scale = 72 / time_speed
@ -117,6 +119,14 @@ biome_lib.perlin_humidity = PerlinNoise(humidity_seeddiff, humidity_octaves, hum
-- Local functions
function biome_lib.dbg(msg, level)
local l = tonumber(level) or 0
if biome_lib.debug_log_level >= l then
print("[Biome Lib] "..msg)
minetest.log("verbose", "[Biome Lib] "..msg)
end
end
local function get_biome_data(pos, perlin_fertile)
local fertility = perlin_fertile:get_2d({x=pos.x, y=pos.z})
@ -187,17 +197,17 @@ function biome_lib:register_generate_plant(biomedef, nodes_or_function_or_model)
if type(nodes_or_function_or_model) == "string"
and string.find(nodes_or_function_or_model, ":")
and not minetest.registered_nodes[nodes_or_function_or_model] then
biome_lib:dbg("Warning: Ignored registration for undefined spawn node: "..dump(nodes_or_function_or_model))
biome_lib.dbg("Warning: Ignored registration for undefined spawn node: "..dump(nodes_or_function_or_model), 2)
return
end
if type(nodes_or_function_or_model) == "string"
and not string.find(nodes_or_function_or_model, ":") then
biome_lib:dbg("Warning: Registered function call using deprecated string method: "..dump(nodes_or_function_or_model))
biome_lib.dbg("Warning: Registered function call using deprecated string method: "..dump(nodes_or_function_or_model), 2)
end
if biomedef.check_air == false then
biome_lib:dbg("Register no-air-check mapgen hook: "..dump(nodes_or_function_or_model))
biome_lib.dbg("Register no-air-check mapgen hook: "..dump(nodes_or_function_or_model), 3)
biome_lib.actionslist_no_aircheck[#biome_lib.actionslist_no_aircheck + 1] = { biomedef, nodes_or_function_or_model }
local s = biomedef.surface
if type(s) == "string" then
@ -206,7 +216,7 @@ function biome_lib:register_generate_plant(biomedef, nodes_or_function_or_model)
biome_lib.surfaceslist_no_aircheck[#biome_lib.surfaceslist_no_aircheck + 1] = s
end
else
biome_lib:dbg("Warning: Ignored no-air-check registration for undefined surface node: "..dump(s))
biome_lib.dbg("Warning: Ignored no-air-check registration for undefined surface node: "..dump(s), 2)
end
else
for i = 1, #biomedef.surface do
@ -216,12 +226,12 @@ function biome_lib:register_generate_plant(biomedef, nodes_or_function_or_model)
biome_lib.surfaceslist_no_aircheck[#biome_lib.surfaceslist_no_aircheck + 1] = s
end
else
biome_lib:dbg("Warning: Ignored no-air-check registration for undefined surface node: "..dump(s))
biome_lib.dbg("Warning: Ignored no-air-check registration for undefined surface node: "..dump(s), 2)
end
end
end
else
biome_lib:dbg("Register with-air-checking mapgen hook: "..dump(nodes_or_function_or_model))
biome_lib.dbg("Register with-air-checking mapgen hook: "..dump(nodes_or_function_or_model), 3)
biome_lib.actionslist_aircheck[#biome_lib.actionslist_aircheck + 1] = { biomedef, nodes_or_function_or_model }
local s = biomedef.surface
if type(s) == "string" then
@ -230,7 +240,7 @@ function biome_lib:register_generate_plant(biomedef, nodes_or_function_or_model)
biome_lib.surfaceslist_aircheck[#biome_lib.surfaceslist_aircheck + 1] = s
end
else
biome_lib:dbg("Warning: Ignored with-air-checking registration for undefined surface node: "..dump(s))
biome_lib.dbg("Warning: Ignored with-air-checking registration for undefined surface node: "..dump(s), 2)
end
else
for i = 1, #biomedef.surface do
@ -240,7 +250,7 @@ function biome_lib:register_generate_plant(biomedef, nodes_or_function_or_model)
biome_lib.surfaceslist_aircheck[#biome_lib.surfaceslist_aircheck + 1] = s
end
else
biome_lib:dbg("Warning: Ignored with-air-checking registration for undefined surface node: "..dump(s))
biome_lib.dbg("Warning: Ignored with-air-checking registration for undefined surface node: "..dump(s), 2)
end
end
end
@ -328,7 +338,7 @@ local function populate_single_surface(biome, pos, perlin_fertile_area, checkair
return true
end
function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes, checkair)
function biome_lib.populate_surfaces(biome, nodes_or_function_or_model, snodes, checkair)
local items_added = 0
biome_lib:set_defaults(biome)
@ -392,13 +402,16 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes,
if objtype == "table" then
if nodes_or_function_or_model.axiom then
biome_lib:generate_tree(p_top, nodes_or_function_or_model)
biome_lib.dbg("An L-tree was spawned at "..minetest.pos_to_string(p_top), 4)
spawned = true
else
local fdir = nil
if biome.random_facedir then
fdir = math.random(biome.random_facedir[1], biome.random_facedir[2])
end
minetest.swap_node(p_top, { name = nodes_or_function_or_model[math.random(#nodes_or_function_or_model)], param2 = fdir })
local n=nodes_or_function_or_model[math.random(#nodes_or_function_or_model)]
minetest.swap_node(p_top, { name = n, param2 = fdir })
biome_lib.dbg("Node \""..n.."\" was randomly picked from a list and placed at "..minetest.pos_to_string(p_top), 4)
spawned = true
end
elseif objtype == "string" and
@ -408,15 +421,18 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes,
fdir = math.random(biome.random_facedir[1], biome.random_facedir[2])
end
minetest.swap_node(p_top, { name = nodes_or_function_or_model, param2 = fdir })
biome_lib.dbg("Node \""..nodes_or_function_or_model.."\" was placed at "..minetest.pos_to_string(p_top), 4)
spawned = true
elseif objtype == "function" then
nodes_or_function_or_model(pos)
biome_lib.dbg("A function was run on surface node at "..minetest.pos_to_string(pos), 4)
spawned = true
elseif objtype == "string" and pcall(loadstring(("return %s(...)"):
format(nodes_or_function_or_model)),pos) then
spawned = true
biome_lib.dbg("An obsolete string-specified function was run on surface node at "..minetest.pos_to_string(p_top), 4)
else
biome_lib:dbg("Warning: Ignored invalid definition for object "..dump(nodes_or_function_or_model).." that was pointed at {"..dump(pos).."}")
biome_lib.dbg("Warning: Ignored invalid definition for object "..dump(nodes_or_function_or_model).." that was pointed at {"..dump(pos).."}", 2)
end
else
tries = tries + 1
@ -427,134 +443,196 @@ function biome_lib:populate_surfaces(biome, nodes_or_function_or_model, snodes,
return items_added
end
-- Primary mapgen spawner, for mods that can work with air checking enabled on
-- a surface during the initial map read stage.
-- Primary log read-out/mapgen spawner
function biome_lib:generate_block_with_air_checking()
if not biome_lib.blocklist_aircheck[1] then
return
end
local function confirm_block_surroundings(p)
local n=minetest.get_node_or_nil(p)
if not n or n.name == "ignore" then return false end
local minp = biome_lib.blocklist_aircheck[1][1]
local maxp = biome_lib.blocklist_aircheck[1][2]
-- use the block hash as a unique key into the surface nodes
-- tables, so that we can write the tables thread-safely.
local blockhash = minetest.hash_node_position(minp)
if not biome_lib.surface_nodes_aircheck.blockhash then -- read it into the block cache
biome_lib.surface_nodes_aircheck.blockhash =
minetest.find_nodes_in_area_under_air(minp, maxp, biome_lib.surfaceslist_aircheck)
biome_lib.actioncount_aircheck.blockhash = 1
if #biome_lib.surface_nodes_aircheck.blockhash > 0 then
biome_lib:dbg("Mapblock at "..minetest.pos_to_string(minp).." added, with "..#biome_lib.surface_nodes_aircheck.blockhash.." surface nodes detected.")
end
elseif not minetest.get_node_or_nil(minp) then
minetest.load_area(minp)
else
if biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash] then
-- [1] is biome, [2] is node/function/model
local added = biome_lib:populate_surfaces(
biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][1],
biome_lib.actionslist_aircheck[biome_lib.actioncount_aircheck.blockhash][2],
biome_lib.surface_nodes_aircheck.blockhash, true)
if added > 0 then
biome_lib:dbg("Ran biome_lib:populate_surfaces for block at "..minetest.pos_to_string(minp)..
". Entry #"..biome_lib.actioncount_aircheck.blockhash.." added "..added.." items.")
for x = -32,32,64 do -- step of 64 causes it to only check the 8 corner blocks
for y = -32,32,64 do
for z = -32,32,64 do
local n=minetest.get_node_or_nil({x=p.x + x, y=p.y + y, z=p.z + z})
if not n or n.name == "ignore" then return false end
end
biome_lib.actioncount_aircheck.blockhash = biome_lib.actioncount_aircheck.blockhash + 1
else
table.remove(biome_lib.blocklist_aircheck, 1)
biome_lib.surface_nodes_aircheck.blockhash = nil
biome_lib.actioncount_aircheck.blockhash = nil
end
end
return true
end
-- Secondary mapgen spawner, for mods that require disabling of
-- checking for air during the initial map read stage.
function biome_lib.generate_block(shutting_down)
function biome_lib:generate_block_no_aircheck()
if not biome_lib.blocklist_no_aircheck[1] then
return
if shutting_down then
if #biome_lib.block_recheck_list > 0 then
for i = 1, #biome_lib.block_recheck_list do
biome_lib.block_log[#biome_lib.block_log + 1] = biome_lib.block_recheck_list[i]
end
biome_lib.block_recheck_list = {}
end
biome_lib.run_block_recheck_list = false
else
if biome_lib.run_block_recheck_list
and not biome_lib.block_recheck_list[1] then
biome_lib.run_block_recheck_list = false
end
end
local minp = biome_lib.blocklist_no_aircheck[1][1]
local maxp = biome_lib.blocklist_no_aircheck[1][2]
local blocklog = biome_lib.run_block_recheck_list
and biome_lib.block_recheck_list
or biome_lib.block_log
local blockhash = minetest.hash_node_position(minp)
if not blocklog[1] then return end
if not biome_lib.surface_nodes_no_aircheck.blockhash then
biome_lib.surface_nodes_no_aircheck.blockhash =
minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_no_aircheck)
biome_lib.actioncount_no_aircheck.blockhash = 1
elseif not minetest.get_node_or_nil(minp) then
local minp = blocklog[1][1]
local maxp = blocklog[1][2]
local airflag = blocklog[1][3]
local pos_hash = minetest.hash_node_position(minp)
if not biome_lib.pos_hash then -- we need to read the maplock and get the surfaces list
local now = minetest.get_us_time()
biome_lib.pos_hash = {}
minetest.load_area(minp)
else
if biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash] then
biome_lib:populate_surfaces(
biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash][1],
biome_lib.actionslist_no_aircheck[biome_lib.actioncount_no_aircheck.blockhash][2],
biome_lib.surface_nodes_no_aircheck.blockhash, false)
biome_lib.actioncount_no_aircheck.blockhash = biome_lib.actioncount_no_aircheck.blockhash + 1
if not confirm_block_surroundings(minp)
and not shutting_down
and (blocklog[1][4] + biome_lib.block_timeout) > now then -- if any neighbors appear not to be loaded and the block hasn't expired yet, defer it
if biome_lib.run_block_recheck_list then
biome_lib.block_log[#biome_lib.block_log + 1] = table.copy(biome_lib.block_recheck_list[1])
table.remove(biome_lib.block_recheck_list, 1)
else
biome_lib.block_recheck_list[#biome_lib.block_recheck_list + 1] = table.copy(biome_lib.block_log[1])
table.remove(biome_lib.block_log, 1)
end
biome_lib.pos_hash = nil
biome_lib.dbg("Mapblock at "..minetest.pos_to_string(minp)..
" had a neighbor not fully emerged, skipped it for now.", 4)
return
else
table.remove(biome_lib.blocklist_no_aircheck, 1)
biome_lib.surface_nodes_no_aircheck.blockhash = nil
biome_lib.actioncount_no_aircheck.blockhash = nil
biome_lib.pos_hash.surface_node_list = airflag
and minetest.find_nodes_in_area_under_air(minp, maxp, biome_lib.surfaceslist_aircheck)
or minetest.find_nodes_in_area(minp, maxp, biome_lib.surfaceslist_no_aircheck)
biome_lib.pos_hash.action_index = 1
if #biome_lib.pos_hash.surface_node_list > 0 then
biome_lib.dbg("Mapblock at "..minetest.pos_to_string(minp)..
" has "..#biome_lib.pos_hash.surface_node_list..
" surface nodes to work on (airflag="..dump(airflag)..")", 4)
end
end
elseif not (airflag and biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index])
and not (not airflag and biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index]) then
-- the block is finished, remove it
if #biome_lib.pos_hash.surface_node_list > 0 then
biome_lib.dbg("Deleted mapblock "..minetest.pos_to_string(minp).." from the block log", 4)
end
table.remove(blocklog, 1)
biome_lib.pos_hash = nil
else
-- below, [1] is biome, [2] is the thing to be added
local added = 0
if airflag then
if biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index] then
added = biome_lib.populate_surfaces(
biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index][1],
biome_lib.actionslist_aircheck[biome_lib.pos_hash.action_index][2],
biome_lib.pos_hash.surface_node_list, true)
biome_lib.pos_hash.action_index = biome_lib.pos_hash.action_index + 1
end
else
if biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index] then
added = biome_lib.populate_surfaces(
biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index][1],
biome_lib.actionslist_no_aircheck[biome_lib.pos_hash.action_index][2],
biome_lib.pos_hash.surface_node_list, false)
biome_lib.pos_hash.action_index = biome_lib.pos_hash.action_index + 1
end
end
if added > 0 then
biome_lib.dbg("biome_lib.populate_surfaces ran on mapblock at "..
minetest.pos_to_string(minp)..". Entry #"..
(biome_lib.pos_hash.action_index-1).." added "..added.." items.", 4)
end
end
end
-- "Play" them back, populating them with new stuff in the process
local step_duration = tonumber(minetest.settings:get("dedicated_server_step"))
minetest.register_globalstep(function(dtime)
if dtime >= step_duration + 0.1 -- don't attempt to populate if lag is already too high
or math.random(100) > biome_lib.queue_run_ratio
or (#biome_lib.blocklist_aircheck == 0 and #biome_lib.blocklist_no_aircheck == 0) then
return
end
if not biome_lib.block_log[1] then return end -- the block log is empty
biome_lib.globalstep_start_time = minetest.get_us_time()
biome_lib.globalstep_runtime = 0
while (#biome_lib.blocklist_aircheck > 0 or #biome_lib.blocklist_no_aircheck > 0)
and biome_lib.globalstep_runtime < 200000 do -- 0.2 seconds, in uS.
if #biome_lib.blocklist_aircheck > 0 then
biome_lib:generate_block_with_air_checking()
end
if #biome_lib.blocklist_no_aircheck > 0 then
biome_lib:generate_block_no_aircheck()
end
biome_lib.globalstep_runtime = minetest.get_us_time() - biome_lib.globalstep_start_time
if math.random(100) > biome_lib.queue_run_ratio then return end
for s = 1, biome_lib.entries_per_step do
biome_lib.generate_block()
end
end)
-- Periodically wake-up the queue to give old blocks a chance to time-out
-- if the player isn't currently exploring (i.e. they're just playing in one area)
function biome_lib.wake_up_queue()
if #biome_lib.block_recheck_list > 1
and #biome_lib.block_log == 0 then
biome_lib.block_log[#biome_lib.block_log + 1] =
table.copy(biome_lib.block_recheck_list[#biome_lib.block_recheck_list])
biome_lib.block_recheck_list[#biome_lib.block_recheck_list] = nil
biome_lib.run_block_recheck_list = true
biome_lib.dbg("Woke-up the map queue to give old blocks a chance to time-out.", 3)
end
minetest.after(biome_lib.block_queue_wakeup_time, biome_lib.wake_up_queue)
end
biome_lib.wake_up_queue()
-- Play out the entire log all at once on shutdown
-- to prevent unpopulated map areas
local function format_time(t)
if t > 59999999 then
return os.date("!%M minutes and %S seconds", math.ceil(t/1000000))
else
return os.date("!%S seconds", math.ceil(t/1000000))
end
end
function biome_lib.check_remaining_time()
if minetest.get_us_time() > (biome_lib.shutdown_last_timestamp + 10000000) then -- report progress every 10s
biome_lib.shutdown_last_timestamp = minetest.get_us_time()
local entries_remaining = #biome_lib.block_log + #biome_lib.block_recheck_list
local total_purged = biome_lib.starting_count - entries_remaining
local elapsed_time = biome_lib.shutdown_last_timestamp - biome_lib.shutdown_start_time
biome_lib.dbg(string.format("%i entries, approximately %s remaining.",
entries_remaining, format_time(elapsed_time/total_purged * entries_remaining)))
end
end
minetest.register_on_shutdown(function()
if #biome_lib.blocklist_aircheck == 0 then
biome_lib.shutdown_start_time = minetest.get_us_time()
biome_lib.shutdown_last_timestamp = minetest.get_us_time()+1
biome_lib.starting_count = #biome_lib.block_log + #biome_lib.block_recheck_list
if biome_lib.starting_count == 0 then
return
end
print("[biome_lib] Stand by, playing out the rest of the aircheck mapblock log")
print("(there are "..#biome_lib.blocklist_aircheck.." entries)...")
while #biome_lib.blocklist_aircheck > 0 do
biome_lib:generate_block_with_air_checking(0.1)
end
end)
biome_lib.dbg("Stand by, purging the mapblock log "..
"(there are "..(#biome_lib.block_log + #biome_lib.block_recheck_list).." entries) ...", 0)
minetest.register_on_shutdown(function()
if #biome_lib.blocklist_aircheck == 0 then
return
while #biome_lib.block_log > 0 do
biome_lib.generate_block(true)
biome_lib.check_remaining_time()
end
print("[biome_lib] Stand by, playing out the rest of the no-aircheck mapblock log")
print("(there are "..#biome_lib.blocklist_no_aircheck.." entries)...")
while #biome_lib.blocklist_no_aircheck > 0 do
biome_lib:generate_block_no_aircheck(0.1)
if #biome_lib.block_recheck_list > 0 then
biome_lib.block_log = table.copy(biome_lib.block_recheck_list)
while #biome_lib.block_log > 0 do
biome_lib.generate_block(true)
biome_lib.check_remaining_time()
end
end
biome_lib.dbg("Log purge completed after "..
format_time(minetest.get_us_time() - biome_lib.shutdown_start_time)..".", 0)
end)
-- The spawning ABM
@ -747,29 +825,34 @@ function biome_lib:get_nodedef_field(nodename, fieldname)
return minetest.registered_nodes[nodename][fieldname]
end
if DEBUG then
biome_lib.last_count_air = 0
biome_lib.last_count_no_air = 0
if biome_lib.debug_log_level >= 3 then
biome_lib.last_count = 0
function biome_lib.show_pending_block_counts()
if biome_lib.last_count_air ~= #biome_lib.blocklist_aircheck
or biome_lib.last_count_no_air ~= #biome_lib.blocklist_no_aircheck then
biome_lib:dbg(string.format("Pending block counts, air: %-7i no-air: %i",
#biome_lib.blocklist_aircheck, #biome_lib.blocklist_no_aircheck))
biome_lib.last_count_air = #biome_lib.blocklist_aircheck
biome_lib.last_count_no_air = #biome_lib.blocklist_no_aircheck
function biome_lib.show_pending_block_count()
if biome_lib.last_count ~= #biome_lib.block_log then
biome_lib.dbg(string.format("Pending block counts - ready to process: %-8icurrently deferred: %i",
#biome_lib.block_log, #biome_lib.block_recheck_list), 3)
biome_lib.last_count = #biome_lib.block_log
biome_lib.queue_idle_flag = false
elseif not biome_lib.queue_idle_flag then
if #biome_lib.block_recheck_list > 0 then
biome_lib.dbg("Mapblock queue only contains blocks that can't yet be processed.", 3)
biome_lib.dbg("Idling the queue until new blocks arrive or the next wake-up call occurs.", 3)
else
biome_lib.dbg("Mapblock queue has run dry.", 3)
biome_lib.dbg("Idling the queue until new blocks arrive.", 3)
end
biome_lib.queue_idle_flag = true
end
minetest.after(1, biome_lib.show_pending_block_counts)
minetest.after(1, biome_lib.show_pending_block_count)
end
biome_lib.show_pending_block_counts()
minetest.after(0, function()
print("Registered a total of "..(#biome_lib.surfaceslist_aircheck)+(#biome_lib.surfaceslist_no_aircheck).." surface types to be evaluated, spread")
print("across "..#biome_lib.actionslist_aircheck.." actions with air-checking and "..#biome_lib.actionslist_no_aircheck.." actions without.")
end)
biome_lib.show_pending_block_count()
end
print("[Biome Lib] Loaded")
minetest.after(0, function()
biome_lib.dbg("Registered a total of "..(#biome_lib.surfaceslist_aircheck)+(#biome_lib.surfaceslist_no_aircheck).." surface types to be evaluated, spread", 0)
biome_lib.dbg("across "..#biome_lib.actionslist_aircheck.." actions with air-checking and "..#biome_lib.actionslist_no_aircheck.." actions without.", 0)
end)
biome_lib.dbg("[Biome Lib] Loaded", 0)

View File

@ -56,6 +56,7 @@ end
-- split into individual mapblocks to reduce lag
minetest.register_on_generated(function(minp, maxp, blockseed)
local timestamp = minetest.get_us_time()
for x = 0, 4 do
local minx = minp.x + x*16
for y = 0, 4 do
@ -65,10 +66,10 @@ minetest.register_on_generated(function(minp, maxp, blockseed)
local bmin = {x=minx, y=miny, z=minz}
local bmax = {x=minx + 15, y=miny + 15, z=minz + 15}
biome_lib.blocklist_aircheck[#biome_lib.blocklist_aircheck + 1] = { bmin, bmax }
biome_lib.blocklist_no_aircheck[#biome_lib.blocklist_no_aircheck + 1] = { bmin, bmax }
biome_lib.block_log[#biome_lib.block_log + 1] = { bmin, bmax, true, timestamp }
biome_lib.block_log[#biome_lib.block_log + 1] = { bmin, bmax, false, timestamp }
end
end
end
biome_lib.run_block_recheck_list = true
end)

View File

@ -639,3 +639,25 @@ minetest.register_craft({
{"group:food_skillet", "farming:skillet"}
}
})
-- Mochi
minetest.register_craftitem("farming:mochi", {
description = S("Mochi"),
inventory_image = "farming_mochi.png",
on_use = minetest.item_eat(3),
groups = {flammable = 2},
})
minetest.register_craft({
type = "shapeless",
output = "farming:mochi",
recipe = {
"group:food_mortar_pestle", "group:food_rice", "group:food_rice",
"group:food_sugar", "bucket:bucket_river_water"
},
replacements = {
{"group:food_mortar_pestle", "farming:mortar_pestle"},
{"bucket:bucket_river_water", "bucket:bucket_empty"}
}
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

View File

@ -138,7 +138,7 @@ mobs:register_arrow("mobs_animal:egg_entity", {
end,
hit_mob = function(self, player)
player:punch(minetest.get_player_by_name(self.playername) or self.object, 1.0, {
player:punch(self.object, 1.0, {
full_punch_interval = 1.0,
damage_groups = {fleshy = 1},
}, nil)
@ -213,6 +213,7 @@ local mobs_shoot_egg = function (item, player, pointed_thing)
ent.velocity = egg_VELOCITY -- needed for api internal timing
ent.switch = 1 -- needed so that egg doesn't despawn straight away
ent._is_arrow = true -- tell advanced mob protection this is an arrow
obj:setvelocity({
x = dir.x * egg_VELOCITY,

View File

@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi")
mobs = {
mod = "redo",
version = "20210405",
version = "20210407",
intllib = S,
invis = minetest.global_exists("invisibility") and invisibility or {}
}
@ -4499,8 +4499,9 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net,
return false
end
-- cannot pick up if not owner
if self.owner ~= name and force_take == false then
-- cannot pick up if not owner (unless player has protection_bypass priv)
if not minetest.check_player_privs(name, "protection_bypass")
and self.owner ~= name and force_take == false then
minetest.chat_send_player(name, S("@1 is owner!", self.owner))
@ -4729,12 +4730,12 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
end
local item = clicker:get_wielded_item()
local name = clicker:get_player_name()
-- if mob has been tamed you can name it with a nametag
if item:get_name() == "mobs:nametag"
and clicker:get_player_name() == self.owner then
local name = clicker:get_player_name()
and (name == self.owner
or minetest.check_player_privs(name, "protection_bypass")) then
-- store mob and nametag stack in external variables
mob_obj[name] = self

View File

@ -29,7 +29,10 @@ local dropbox = gen_def({
return 0
end
return stack_count
end
end,
allow_metadata_inventory_take = actions.get_allow_metadata_inventory_take({
"dropbox", check_privs = actions.has_locked_chest_privilege
}),
})

View File

@ -64,7 +64,7 @@ for i in ipairs(moretrees.treelist) do
grow_function = "moretrees.grow_"..treename
end
biome_lib:dbg(dump(moretrees[tree_biome].surface))
biome_lib.dbg(dump(moretrees[tree_biome].surface), 4)
biome_lib:grow_plants({
grow_delay = moretrees.sapling_interval,

View File

@ -7,6 +7,8 @@ local mname = "cavestuff"
-- support for i18n
local S = minetest.get_translator("cavestuff")
cavestuff = {}
dofile(minetest.get_modpath("cavestuff").."/nodes.lua")
dofile(minetest.get_modpath("cavestuff").."/mapgen.lua")

View File

@ -1,52 +1,39 @@
--Map Generation Stuff
minetest.register_on_generated(function(minp, maxp, seed)
if maxp.y >= 2 and minp.y <= 0 then
-- Generate pebbles
local perlin1 = minetest.get_perlin(329, 3, 0.6, 100)
-- Assume X and Z lengths are equal
local divlen = 16
local divs = (maxp.x-minp.x)/divlen+1;
for divx=0,divs-1 do
for divz=0,divs-1 do
local x0 = minp.x + math.floor((divx+0)*divlen)
local z0 = minp.z + math.floor((divz+0)*divlen)
local x1 = minp.x + math.floor((divx+1)*divlen)
local z1 = minp.z + math.floor((divz+1)*divlen)
-- Determine pebble amount from perlin noise
local pebble_amount = math.floor(perlin1:get2d({x=x0, y=z0}) ^ 2 * 2)
-- Find random positions for pebbles based on this random
local pr = PseudoRandom(seed+1)
for i=0,pebble_amount do
local x = pr:next(x0, x1)
local z = pr:next(z0, z1)
-- Find ground level (0...15)
local ground_y = nil
for y=30,0,-1 do
if minetest.get_node({x=x,y=y,z=z}).name ~= "air" then
ground_y = y
break
end
end
biome_lib:register_generate_plant(
{
surface = {
"default:dirt_with_grass",
"default:gravel",
"default:stone",
"default:permafrost_with_stones"
},
max_count = 50,
rarity = 0,
plantlife_limit = -1,
check_air = true,
random_facedir = {0, 3}
},
{
"cavestuff:pebble_1",
"cavestuff:pebble_2"
}
)
if ground_y then
local p = {x=x,y=ground_y+1,z=z}
local nn = minetest.get_node(p).name
-- Check if the node can be replaced
if minetest.registered_nodes[nn] and
minetest.registered_nodes[nn].buildable_to then
nn = minetest.get_node({x=x,y=ground_y,z=z}).name
-- If desert sand, add dry shrub
if nn == "default:dirt_with_grass" then
minetest.swap_node(p,{name="cavestuff:pebble_"..pr:next(1,2), param2=math.random(0,3)})
elseif nn == "default:desert_sand" then
minetest.swap_node(p,{name="cavestuff:desert_pebble_"..pr:next(1,2), param2=math.random(0,3)})
end
end
end
end
end
end
end
end)
biome_lib:register_generate_plant(
{
surface = {
"default:desert_sand",
"default:desert_stone"
},
max_count = 50,
rarity = 0,
plantlife_limit = -1,
check_air = true,
random_facedir = {0, 3}
},
{
"cavestuff:desert_pebble_1",
"cavestuff:desert_pebble_2"
}
)

View File

@ -1,2 +1,2 @@
name = cavestuff
depends = default
depends = default,biome_lib

View File

@ -7,26 +7,24 @@
-- Looked at code from: default
-----------------------------------------------------------------------------------------------
abstract_dryplants.grow_grass = function(pos)
local right_here = {x=pos.x, y=pos.y+1, z=pos.z}
local grass_size = math.random(1,5)
if minetest.get_node(right_here).name == "air" -- instead of check_air = true,
or minetest.get_node(right_here).name == "default:junglegrass" then
minetest.swap_node(right_here, {name="default:grass_"..grass_size})
end
end
biome_lib:register_generate_plant({
surface = {
"default:dirt_with_grass",
"stoneage:grass_with_silex",
"sumpf:peat",
"sumpf:sumpf"
biome_lib:register_generate_plant(
{
surface = {
"default:dirt_with_grass",
"stoneage:grass_with_silex",
"sumpf:peat",
"sumpf:sumpf"
},
max_count = TALL_GRASS_PER_MAPBLOCK,
rarity = 101 - TALL_GRASS_RARITY,
min_elevation = 1, -- above sea level
plantlife_limit = -0.9,
check_air = true,
},
max_count = TALL_GRASS_PER_MAPBLOCK,
rarity = 101 - TALL_GRASS_RARITY,
min_elevation = 1, -- above sea level
plantlife_limit = -0.9,
},
abstract_dryplants.grow_grass
{ "default:grass_1",
"default:grass_2",
"default:grass_3",
"default:grass_4",
"default:grass_5"
}
)

View File

@ -20,6 +20,7 @@ https://forum.minetest.net/viewtopic.php?t=10090&p=153667
- 1.2 - Added POVA support, tweaked code slightly
- 1.3 - Add setting under Advanced to enable older sneak glitch movement
- 1.4 - Add minetest 5.0 check for knockback y_offset
- 1.5 - Use Minetext 5.x functions for proper player knockback
API:

View File

@ -49,7 +49,7 @@ minetest.register_globalstep(function(dtime)
time = 0
-- define locals outside loop
local name, pos, ndef, def, nslow, nfast
local name, pos, ndef, def, nslow, nfast, prop
-- get list of players
local players = minetest.get_connected_players()
@ -66,8 +66,8 @@ if name and playerplus[name] then
pos = player:get_pos()
-- what is around me?
pos.y = pos.y - 0.1 -- standing on
playerplus[name].nod_stand = node_ok(pos)
playerplus[name].nod_stand = node_ok({
x = pos.x, y = pos.y - 0.1, z = pos.z})
-- Does the node below me have an on_walk_over function set?
ndef = minetest.registered_nodes[playerplus[name].nod_stand]
@ -75,13 +75,15 @@ if name and playerplus[name] then
ndef.on_walk_over(pos, ndef, player)
end
pos.y = pos.y + 1.5 -- head level
playerplus[name].nod_head = node_ok(pos)
prop = player:get_properties()
pos.y = pos.y - 1.2 -- feet level
playerplus[name].nod_feet = node_ok(pos)
-- node at eye level
playerplus[name].nod_head = node_ok({
x = pos.x, y = pos.y + prop.eye_height, z = pos.z})
pos.y = pos.y - 0.2 -- reset pos
-- node at foot level
playerplus[name].nod_feet = node_ok({
x = pos.x, y = pos.y + 0.2, z = pos.z})
-- get player physics
def = player:get_physics_override()
@ -247,29 +249,19 @@ minetest.register_privilege("no_knockback", {
-- is player knock-back effect enabled?
if minetest.settings:get_bool("player_knockback") == true then
minetest.register_entity("playerplus:temp", {
physical = true,
collisionbox = {-0.20, -1, -0.20, 0.20, 1, 0.20},
visual_size = {x = 0, y = 0},
visual = "sprite",
textures = {"trans.png"},
stepheight = 0.6,
on_step = function(self, dtime)
self.timer = (self.timer or 0) + dtime
if self.timer > 1 then
self.object:remove()
end
end
})
-- player knock-back function
local punchy = function(
player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
if not dir then return end
-- check if player has 'no_knockback' privelage
local privs = minetest.get_player_privs(player:get_player_name())
if privs["no_knockback"] then
return
end
local damage = 0
-- get tool damage
@ -289,7 +281,6 @@ local punchy = function(
end
damage = damage + (tool_capabilities.damage_groups[group] or 0) * tmp
end
-- check for knockback value
@ -302,48 +293,12 @@ local punchy = function(
-- print ("---", player:get_player_name(), damage)
if not dir then return end
-- check if player has 'no_knockback' privelage
local privs = minetest.get_player_privs(player:get_player_name())
if privs["no_knockback"] then
return
end
local y_offset = mt50 and -10 or 0
local vel = damage * 2
local pos = player:get_pos() ; pos.y = pos.y + 1.0
local ent = minetest.add_entity(pos, "playerplus:temp")
local obj = ent:get_luaentity()
local yaw = player:get_look_horizontal() or 0 ; yaw = -yaw * (180 / 3.14) -- pi
if obj and not player:get_attach() then
player:set_attach(ent, "", {x = 0, y = y_offset, z = 0}, {x = 0, y = yaw, z = 0})
ent:set_velocity({
x = dir.x * vel,
y = -1,
z = dir.z * vel
})
ent:set_acceleration({
x = dir.x * -1,
y = 0,
z = dir.z * -1
})
minetest.after(0.25, function()
player:set_detach()
ent:remove()
end)
else
ent:remove()
end
-- knock back player
player:add_velocity({
x = dir.x * (damage * 2),
y = -1,
z = dir.z * (damage * 2)
})
end
minetest.register_on_punchplayer(punchy)

View File

@ -544,7 +544,7 @@ minetest.register_node("protector:chest", {
local inv = meta:get_inventory()
meta:set_string("infotext", S("Protected Chest"))
meta:set_string("name", "")
meta:set_string("name", S("Protected Chest"))
inv:set_size("main", 8 * 4)
end,
@ -575,7 +575,8 @@ minetest.register_node("protector:chest", {
minetest.pos_to_string(pos))
end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
on_metadata_inventory_move = function(
pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", player:get_player_name() ..
" moves stuff inside protected chest at " ..
@ -600,7 +601,8 @@ minetest.register_node("protector:chest", {
return stack:get_count()
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
allow_metadata_inventory_move = function(
pos, from_list, from_index, to_list, to_index, count, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
@ -645,15 +647,39 @@ minetest.register_node("protector:chest", {
on_blast = function() end,
})
-- Container transfer helper
local to_from = function(src, dst)
local stack, item, leftover
local size = dst:get_size("main")
for i = 1, size do
stack = src:get_stack("main", i)
item = stack:get_name()
if item ~= "" and dst:room_for_item("main", item) then
leftover = dst:add_item("main", stack)
if leftover and not leftover:is_empty() then
src:set_stack("main", i, leftover)
else
src:set_stack("main", i, nil)
end
end
end
end
-- Protected Chest formspec buttons
minetest.register_on_player_receive_fields(function(player, formname, fields)
if string.sub(formname, 0, string.len("protector:chest_")) ~= "protector:chest_" then
if string.sub(formname, 0, 16) ~= "protector:chest_" then
return
end
local pos_s = string.sub(formname,string.len("protector:chest_") + 1)
local pos_s = string.sub(formname, 17)
local pos = minetest.string_to_pos(pos_s)
if minetest.is_protected(pos, player:get_player_name()) then
@ -663,43 +689,16 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local meta = minetest.get_meta(pos) ; if not meta then return end
local chest_inv = meta:get_inventory() ; if not chest_inv then return end
local player_inv = player:get_inventory()
local leftover
-- copy contents of player inventory to chest
if fields.toup then
-- copy contents of players inventory to chest
for i, v in ipairs(player_inv:get_list("main") or {}) do
if chest_inv:room_for_item("main", v) then
leftover = chest_inv:add_item("main", v)
player_inv:remove_item("main", v)
if leftover
and not leftover:is_empty() then
player_inv:add_item("main", v)
end
end
end
to_from(player_inv, chest_inv)
-- copy contents of chest to player inventory
elseif fields.todn then
-- copy contents of chest to players inventory
for i, v in ipairs(chest_inv:get_list("main") or {}) do
if player_inv:room_for_item("main", v) then
leftover = player_inv:add_item("main", v)
chest_inv:remove_item("main", v)
if leftover
and not leftover:is_empty() then
chest_inv:add_item("main", v)
end
end
end
to_from(chest_inv, player_inv)
elseif fields.chestname then
@ -707,9 +706,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.chestname ~= "" then
meta:set_string("name", fields.chestname)
meta:set_string("infotext",
S("Protected Chest (@1)", fields.chestname))
meta:set_string("infotext", fields.chestname)
else
meta:set_string("name", S("Protected Chest"))
meta:set_string("infotext", S("Protected Chest"))
end

View File

@ -14,6 +14,7 @@ read_globals = {
"ItemStack", "datastorage",
"hb",
"doors",
}
files["callbacks.lua"].ignore = { "player", "draw_lite_mode" }

View File

@ -19,6 +19,8 @@ minetest.register_on_joinplayer(function(player)
unified_inventory.active_search_direction[player_name] = "nochange"
unified_inventory.apply_filter(player, "", "nochange")
unified_inventory.current_searchbox[player_name] = ""
unified_inventory.current_category[player_name] = "all"
unified_inventory.current_category_scroll[player_name] = 0
unified_inventory.alternate[player_name] = 1
unified_inventory.current_item[player_name] = nil
unified_inventory.current_craft_direction[player_name] = "recipe"
@ -69,6 +71,41 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
unified_inventory.current_searchbox[player_name] = fields.searchbox
end
local clicked_category
for name, value in pairs(fields) do
local category_name = string.match(name, "^category_(.+)$")
if category_name then
clicked_category = category_name
break
end
end
if clicked_category
and clicked_category ~= unified_inventory.current_category[player_name] then
unified_inventory.current_category[player_name] = clicked_category
unified_inventory.apply_filter(player, unified_inventory.current_searchbox[player_name], "nochange")
unified_inventory.set_inventory_formspec(player,
unified_inventory.current_page[player_name])
end
if fields.next_category then
local scroll = math.min(#unified_inventory.category_list-ui_peruser.pagecols, unified_inventory.current_category_scroll[player_name] + 1)
if scroll ~= unified_inventory.current_category_scroll[player_name] then
unified_inventory.current_category_scroll[player_name] = scroll
unified_inventory.set_inventory_formspec(player,
unified_inventory.current_page[player_name])
end
end
if fields.prev_category then
local scroll = math.max(0, unified_inventory.current_category_scroll[player_name] - 1)
if scroll ~= unified_inventory.current_category_scroll[player_name] then
unified_inventory.current_category_scroll[player_name] = scroll
unified_inventory.set_inventory_formspec(player,
unified_inventory.current_page[player_name])
end
end
for i, def in pairs(unified_inventory.buttons) do
if fields[def.name] then
def.action(player)
@ -126,6 +163,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
clicked_item = unified_inventory.demangle_for_formspec(mangled_item)
if string.sub(clicked_item, 1, 6) == "group:" then
-- Change search filter to this group
unified_inventory.current_category[player_name] = "all"
apply_new_filter(player, clicked_item, new_dir)
return
end

View File

@ -0,0 +1,149 @@
local S = minetest.get_translator("unified_inventory")
unified_inventory.registered_categories = {}
unified_inventory.registered_category_items = {}
unified_inventory.category_list = {}
local function char_to_sort_index(char_code)
if char_code <= 32 then
-- Command codes, no thanks
return 0
end
if char_code <= 64 then
-- Sorts numbers, and some punctuation, after letters
return char_code
end
if char_code >= 158 then
-- Out of sortable range
return 0
end
if char_code > 122 then
-- Avoids overlap with {, |, } and ~
return char_code - 58
end
if char_code > 96 then
-- Normalises lowercase with uppercase
return char_code - 96
end
return char_code - 64
end
local function string_to_sort_index(str)
local max_chars = 5
local power = 100
local index = 0
for i=1,math.min(#str, max_chars) do
index = index + (char_to_sort_index(string.byte(str, i))/(power^i))
end
return index
end
function update_category_list()
local category_list = {}
table.insert(category_list, {
name = "all",
label = S("All Items"),
symbol = "ui_category_all.png",
index = -2,
})
table.insert(category_list, {
name = "uncategorized",
label = S("Misc. Items"),
symbol = "ui_category_none.png",
index = -1,
})
for category, def in pairs(unified_inventory.registered_categories) do
table.insert(category_list, {
name = category,
label = def.label or category,
symbol = def.symbol,
index = def.index or -- sortby defined order
string_to_sort_index(category) -- or do a rudimentary alphabetical sort
})
end
table.sort(category_list, function (a,b)
return a.index < b.index
end)
unified_inventory.category_list = category_list
end
local function ensure_category_exists(category_name)
if not unified_inventory.registered_categories[category_name] then
unified_inventory.registered_categories[category_name] = {
symbol = "default:stick",
label = category_name
}
end
if not unified_inventory.registered_category_items[category_name] then
unified_inventory.registered_category_items[category_name] = {}
end
end
function unified_inventory.register_category(category_name, config)
ensure_category_exists(category_name)
if config and config.symbol then
unified_inventory.set_category_symbol(category_name, config.symbol)
end
if config and config.label then
unified_inventory.set_category_label(category_name, config.label)
end
if config and config.index then
unified_inventory.set_category_index(category_name, config.index)
end
if config and config.items then
unified_inventory.add_category_items(category_name, config.items)
end
update_category_list()
end
function unified_inventory.set_category_symbol(category_name, symbol)
ensure_category_exists(category_name)
unified_inventory.registered_categories[category_name].symbol = symbol
update_category_list()
end
function unified_inventory.set_category_label(category_name, label)
ensure_category_exists(category_name)
unified_inventory.registered_categories[category_name].label = label
update_category_list()
end
function unified_inventory.set_category_index(category_name, index)
ensure_category_exists(category_name)
unified_inventory.registered_categories[category_name].index = index
update_category_list()
end
function unified_inventory.add_category_item(category_name, item)
ensure_category_exists(category_name)
unified_inventory.registered_category_items[category_name][item] = true
end
function unified_inventory.add_category_items(category_name, items)
for _,item in ipairs(items) do
unified_inventory.add_category_item(category_name, item)
end
end
function unified_inventory.remove_category_item(category_name, item)
unified_inventory.registered_category_items[category_name][item] = nil
end
function unified_inventory.remove_category(category_name)
unified_inventory.registered_categories[category_name] = nil
unified_inventory.registered_category_items[category_name] = nil
update_category_list()
end
function unified_inventory.find_category(item)
-- Returns the first category the item exists in
-- Best for checking if an item has any category at all
for category, items in pairs(unified_inventory.registered_category_items) do
if items[item] then return category end
end
end
function unified_inventory.find_categories(item)
-- Returns all the categories the item exists in
-- Best for listing all categories
local categories = {}
for category, items in pairs(unified_inventory.registered_category_items) do
if items[item] then
table.insert(categories, category)
end
end
return categories
end

View File

@ -0,0 +1,704 @@
local S = minetest.get_translator("unified_inventory")
unified_inventory.register_category('plants', {
symbol = "flowers:tulip",
label = S("Plant Life")
})
unified_inventory.register_category('building', {
symbol = "default:brick",
label = S("Building Materials")
})
unified_inventory.register_category('tools', {
symbol = "default:pick_diamond",
label = S("Tools")
})
unified_inventory.register_category('minerals', {
symbol = "default:iron_lump",
label = S("Minerals and Metals")
})
unified_inventory.register_category('environment', {
symbol = "default:dirt_with_grass",
label = S("Environment and Worldgen")
})
unified_inventory.register_category('lighting', {
symbol = "default:torch",
label = S("Lighting")
})
if unified_inventory.automatic_categorization then
minetest.register_on_mods_loaded(function()
-- Add biome nodes to environment category
for _,def in pairs(minetest.registered_biomes) do
local env_nodes = {
def.node_riverbed, def.node_top, def.node_filler, def.node_dust,
}
for i,node in pairs(env_nodes) do
if node then
unified_inventory.add_category_item('environment', node)
end
end
end
-- Add minable ores to minerals and everything else (pockets of stone & sand variations) to environment
for _,item in pairs(minetest.registered_ores) do
if item.ore_type == "scatter" then
local drop = minetest.registered_nodes[item.ore].drop
if drop and drop ~= "" then
unified_inventory.add_category_item('minerals', item.ore)
unified_inventory.add_category_item('minerals', drop)
else
unified_inventory.add_category_item('environment', item.ore)
end
else
unified_inventory.add_category_item('environment', item.ore)
end
end
-- Add items by item definition
for name, def in pairs(minetest.registered_items) do
local group = def.groups or {}
if not group.not_in_creative_inventory then
if group.stair or
group.slab or
group.wall or
group.fence then
unified_inventory.add_category_item('building', name)
elseif group.flora or
group.flower or
group.seed or
group.leaves or
group.sapling or
group.tree then
unified_inventory.add_category_item('plants', name)
elseif def.type == 'tool' then
unified_inventory.add_category_item('tools', name)
elseif def.liquidtype == 'source' then
unified_inventory.add_category_item('environment', name)
elseif def.light_source and def.light_source > 0 then
unified_inventory.add_category_item('lighting', name)
elseif group.door or
minetest.global_exists("doors") and (
doors.registered_doors and doors.registered_doors[name..'_a'] or
doors.registered_trapdoors and doors.registered_trapdoors[name]
) then
unified_inventory.add_category_item('building', name)
end
end
end
end)
end
-- [[
unified_inventory.add_category_items('plants', {
"default:dry_grass_5",
"default:acacia_sapling",
"default:blueberry_bush_sapling",
"default:grass_2",
"default:pine_bush_stem",
"default:leaves",
"default:pine_needles",
"default:cactus",
"default:junglegrass",
"default:pine_sapling",
"default:sapling",
"default:bush_stem",
"default:dry_grass_2",
"default:fern_1",
"default:grass_3",
"default:marram_grass_1",
"default:pine_tree",
"default:dry_grass_3",
"default:dry_shrub",
"default:grass_4",
"default:marram_grass_2",
"default:jungleleaves",
"default:apple",
"default:tree",
"default:aspen_tree",
"default:bush_sapling",
"default:grass_5",
"default:blueberry_bush_leaves_with_berries",
"default:acacia_bush_sapling",
"default:grass_1",
"default:aspen_leaves",
"default:marram_grass_3",
"default:large_cactus_seedling",
"default:junglesapling",
"default:dry_grass_4",
"default:acacia_bush_stem",
"default:papyrus",
"default:pine_bush_needles",
"default:bush_leaves",
"default:fern_3",
"default:aspen_sapling",
"default:acacia_tree",
"default:apple_mark",
"default:acacia_leaves",
"default:jungletree",
"default:dry_grass_1",
"default:acacia_bush_leaves",
"default:emergent_jungle_sapling",
"default:fern_2",
"default:blueberries",
"default:sand_with_kelp",
"default:blueberry_bush_leaves",
"default:pine_bush_sapling",
"farming:cotton",
"farming:cotton_1",
"farming:cotton_2",
"farming:cotton_3",
"farming:cotton_4",
"farming:cotton_5",
"farming:cotton_6",
"farming:cotton_7",
"farming:cotton_8",
"farming:cotton_wild",
"farming:seed_cotton",
"farming:seed_wheat",
"farming:straw",
"farming:wheat",
"farming:wheat_1",
"farming:wheat_2",
"farming:wheat_3",
"farming:wheat_4",
"farming:wheat_5",
"farming:wheat_6",
"farming:wheat_7",
"farming:wheat_8",
"flowers:chrysanthemum_green",
"flowers:dandelion_white",
"flowers:dandelion_yellow",
"flowers:geranium",
"flowers:mushroom_brown",
"flowers:mushroom_red",
"flowers:rose",
"flowers:tulip",
"flowers:tulip_black",
"flowers:viola",
"flowers:waterlily",
"flowers:waterlily_waving",
})
unified_inventory.add_category_items('tools', {
"default:sword_diamond",
"default:axe_diamond",
"default:shovel_diamond",
"default:axe_steel",
"default:shovel_mese",
"default:sword_wood",
"default:pick_bronze",
"default:axe_stone",
"default:sword_stone",
"default:pick_stone",
"default:shovel_stone",
"default:sword_mese",
"default:shovel_bronze",
"default:sword_bronze",
"default:axe_bronze",
"default:shovel_steel",
"default:sword_steel",
"default:axe_mese",
"default:shovel_wood",
"default:pick_mese",
"default:axe_wood",
"default:pick_diamond",
"default:pick_wood",
"default:pick_steel",
"farming:hoe_bronze",
"farming:hoe_diamond",
"farming:hoe_mese",
"farming:hoe_steel",
"farming:hoe_stone",
"farming:hoe_wood",
"fire:flint_and_steel",
"map:mapping_kit",
"screwdriver:screwdriver",
"fireflies:bug_net",
"bucket:bucket_empty",
"binoculars:binoculars",
"default:skeleton_key",
})
unified_inventory.add_category_items('minerals', {
"default:stone_with_copper",
"default:stone_with_gold",
"default:stone_with_iron",
"default:copper_ingot",
"default:copper_lump",
"default:gold_lump",
"default:diamondblock",
"default:stone_with_diamond",
"default:stone_with_mese",
"default:steel_ingot",
"default:gold_ingot",
"default:iron_lump",
"default:tinblock",
"default:tin_lump",
"default:stone_with_tin",
"default:mese_crystal",
"default:diamond",
"default:bronze_ingot",
"default:mese",
"default:mese_crystal_fragment",
"default:copperblock",
"default:stone_with_coal",
"default:steelblock",
"default:tin_ingot",
"default:coalblock",
"default:coal_lump",
"default:bronzeblock",
"default:goldblock",
"stairs:slab_bronzeblock",
"stairs:slab_copperblock",
"stairs:slab_steelblock",
"stairs:slab_tinblock",
"stairs:stair_bronzeblock",
"stairs:stair_copperblock",
"stairs:stair_inner_bronzeblock",
"stairs:stair_inner_copperblock",
"stairs:stair_inner_steelblock",
"stairs:stair_inner_tinblock",
"stairs:stair_outer_bronzeblock",
"stairs:stair_outer_copperblock",
"stairs:stair_outer_steelblock",
"stairs:stair_outer_tinblock",
"stairs:stair_steelblock",
"stairs:stair_tinblock",
})
unified_inventory.add_category_items('building', {
"default:fence_rail_aspen_wood",
"default:fence_rail_acacia_wood",
"default:fence_junglewood",
"default:fence_rail_junglewood",
"default:fence_aspen_wood",
"default:fence_pine_wood",
"default:fence_rail_wood",
"default:fence_rail_pine_wood",
"default:fence_acacia_wood",
"default:junglewood",
"default:acacia_wood",
"default:aspen_wood",
"default:fence_wood",
"default:pine_wood",
"default:silver_sandstone",
"default:desert_sandstone",
"default:sandstone_block",
"default:desert_sandstone_brick",
"default:stone_block",
"default:stonebrick",
"default:obsidian_glass",
"default:desert_sandstone_block",
"default:silver_sandstone_brick",
"default:brick",
"default:obsidianbrick",
"default:sandstonebrick",
"default:sandstone",
"default:desert_stone_block",
"default:silver_sandstone_block",
"default:wood",
"default:obsidian_block",
"default:glass",
"default:clay_brick",
"default:desert_stonebrick",
"default:desert_cobble",
"default:cobble",
"default:mossycobble",
"doors:door_glass",
"doors:door_glass_a",
"doors:door_glass_b",
"doors:door_glass_c",
"doors:door_glass_d",
"doors:door_obsidian_glass",
"doors:door_obsidian_glass_a",
"doors:door_obsidian_glass_b",
"doors:door_obsidian_glass_c",
"doors:door_obsidian_glass_d",
"doors:door_steel",
"doors:door_steel_a",
"doors:door_steel_b",
"doors:door_steel_c",
"doors:door_steel_d",
"doors:door_wood",
"doors:door_wood_a",
"doors:door_wood_b",
"doors:door_wood_c",
"doors:door_wood_d",
"doors:gate_acacia_wood_closed",
"doors:gate_acacia_wood_open",
"doors:gate_aspen_wood_closed",
"doors:gate_aspen_wood_open",
"doors:gate_junglewood_closed",
"doors:gate_junglewood_open",
"doors:gate_pine_wood_closed",
"doors:gate_pine_wood_open",
"doors:gate_wood_closed",
"doors:gate_wood_open",
"doors:hidden",
"doors:trapdoor",
"doors:trapdoor_open",
"doors:trapdoor_steel",
"doors:trapdoor_steel_open",
"stairs:slab_bronzeblock",
"stairs:slab_copperblock",
"stairs:slab_steelblock",
"stairs:slab_tinblock",
"stairs:stair_bronzeblock",
"stairs:stair_copperblock",
"stairs:stair_inner_bronzeblock",
"stairs:stair_inner_copperblock",
"stairs:stair_inner_steelblock",
"stairs:stair_inner_tinblock",
"stairs:stair_outer_bronzeblock",
"stairs:stair_outer_copperblock",
"stairs:stair_outer_steelblock",
"stairs:stair_outer_tinblock",
"stairs:stair_steelblock",
"stairs:stair_tinblock",
"stairs:slab_acacia_wood",
"stairs:slab_aspen_wood",
"stairs:slab_brick",
"stairs:slab_cobble",
"stairs:slab_desert_cobble",
"stairs:slab_desert_sandstone",
"stairs:slab_desert_sandstone_block",
"stairs:slab_desert_sandstone_brick",
"stairs:slab_desert_stone",
"stairs:slab_desert_stone_block",
"stairs:slab_desert_stonebrick",
"stairs:slab_glass",
"stairs:slab_goldblock",
"stairs:slab_ice",
"stairs:slab_junglewood",
"stairs:slab_mossycobble",
"stairs:slab_obsidian",
"stairs:slab_obsidian_block",
"stairs:slab_obsidian_glass",
"stairs:slab_obsidianbrick",
"stairs:slab_pine_wood",
"stairs:slab_sandstone",
"stairs:slab_sandstone_block",
"stairs:slab_sandstonebrick",
"stairs:slab_silver_sandstone",
"stairs:slab_silver_sandstone_block",
"stairs:slab_silver_sandstone_brick",
"stairs:slab_snowblock",
"stairs:slab_stone",
"stairs:slab_stone_block",
"stairs:slab_stonebrick",
"stairs:slab_straw",
"stairs:slab_wood",
"stairs:stair_acacia_wood",
"stairs:stair_aspen_wood",
"stairs:stair_brick",
"stairs:stair_cobble",
"stairs:stair_desert_cobble",
"stairs:stair_desert_sandstone",
"stairs:stair_desert_sandstone_block",
"stairs:stair_desert_sandstone_brick",
"stairs:stair_desert_stone",
"stairs:stair_desert_stone_block",
"stairs:stair_desert_stonebrick",
"stairs:stair_glass",
"stairs:stair_goldblock",
"stairs:stair_ice",
"stairs:stair_inner_acacia_wood",
"stairs:stair_inner_aspen_wood",
"stairs:stair_inner_brick",
"stairs:stair_inner_cobble",
"stairs:stair_inner_desert_cobble",
"stairs:stair_inner_desert_sandstone",
"stairs:stair_inner_desert_sandstone_block",
"stairs:stair_inner_desert_sandstone_brick",
"stairs:stair_inner_desert_stone",
"stairs:stair_inner_desert_stone_block",
"stairs:stair_inner_desert_stonebrick",
"stairs:stair_inner_glass",
"stairs:stair_inner_goldblock",
"stairs:stair_inner_ice",
"stairs:stair_inner_junglewood",
"stairs:stair_inner_mossycobble",
"stairs:stair_inner_obsidian",
"stairs:stair_inner_obsidian_block",
"stairs:stair_inner_obsidian_glass",
"stairs:stair_inner_obsidianbrick",
"stairs:stair_inner_pine_wood",
"stairs:stair_inner_sandstone",
"stairs:stair_inner_sandstone_block",
"stairs:stair_inner_sandstonebrick",
"stairs:stair_inner_silver_sandstone",
"stairs:stair_inner_silver_sandstone_block",
"stairs:stair_inner_silver_sandstone_brick",
"stairs:stair_inner_snowblock",
"stairs:stair_inner_stone",
"stairs:stair_inner_stone_block",
"stairs:stair_inner_stonebrick",
"stairs:stair_inner_straw",
"stairs:stair_inner_wood",
"stairs:stair_junglewood",
"stairs:stair_mossycobble",
"stairs:stair_obsidian",
"stairs:stair_obsidian_block",
"stairs:stair_obsidian_glass",
"stairs:stair_obsidianbrick",
"stairs:stair_outer_acacia_wood",
"stairs:stair_outer_aspen_wood",
"stairs:stair_outer_brick",
"stairs:stair_outer_cobble",
"stairs:stair_outer_desert_cobble",
"stairs:stair_outer_desert_sandstone",
"stairs:stair_outer_desert_sandstone_block",
"stairs:stair_outer_desert_sandstone_brick",
"stairs:stair_outer_desert_stone",
"stairs:stair_outer_desert_stone_block",
"stairs:stair_outer_desert_stonebrick",
"stairs:stair_outer_glass",
"stairs:stair_outer_goldblock",
"stairs:stair_outer_ice",
"stairs:stair_outer_junglewood",
"stairs:stair_outer_mossycobble",
"stairs:stair_outer_obsidian",
"stairs:stair_outer_obsidian_block",
"stairs:stair_outer_obsidian_glass",
"stairs:stair_outer_obsidianbrick",
"stairs:stair_outer_pine_wood",
"stairs:stair_outer_sandstone",
"stairs:stair_outer_sandstone_block",
"stairs:stair_outer_sandstonebrick",
"stairs:stair_outer_silver_sandstone",
"stairs:stair_outer_silver_sandstone_block",
"stairs:stair_outer_silver_sandstone_brick",
"stairs:stair_outer_snowblock",
"stairs:stair_outer_stone",
"stairs:stair_outer_stone_block",
"stairs:stair_outer_stonebrick",
"stairs:stair_outer_straw",
"stairs:stair_outer_wood",
"stairs:stair_pine_wood",
"stairs:stair_sandstone",
"stairs:stair_sandstone_block",
"stairs:stair_sandstonebrick",
"stairs:stair_silver_sandstone",
"stairs:stair_silver_sandstone_block",
"stairs:stair_silver_sandstone_brick",
"stairs:stair_snowblock",
"stairs:stair_stone",
"stairs:stair_stone_block",
"stairs:stair_stonebrick",
"stairs:stair_straw",
"stairs:stair_wood",
"xpanes:bar",
"xpanes:bar_flat",
"xpanes:door_steel_bar",
"xpanes:door_steel_bar_a",
"xpanes:door_steel_bar_b",
"xpanes:door_steel_bar_c",
"xpanes:door_steel_bar_d",
"xpanes:obsidian_pane",
"xpanes:obsidian_pane_flat",
"xpanes:pane",
"xpanes:pane_flat",
"xpanes:trapdoor_steel_bar",
"xpanes:trapdoor_steel_bar_open",
"walls:cobble",
"walls:desertcobble",
"walls:mossycobble",
})
unified_inventory.add_category_items('environment', {
"air",
"default:cave_ice",
"default:dirt_with_rainforest_litter",
"default:gravel",
"default:dry_dirt_with_dry_grass",
"default:permafrost",
"default:desert_stone",
"default:ice",
"default:dry_dirt",
"default:obsidian",
"default:sand",
"default:river_water_source",
"default:dirt_with_snow",
"default:dirt_with_grass",
"default:water_flowing",
"default:dirt",
"default:desert_sand",
"default:permafrost_with_moss",
"default:dirt_with_coniferous_litter",
"default:water_source",
"default:dirt_with_dry_grass",
"default:river_water_flowing",
"default:stone",
"default:snow",
"default:lava_flowing",
"default:lava_source",
"default:permafrost_with_stones",
"default:dirt_with_grass_footsteps",
"default:silver_sand",
"default:snowblock",
"default:clay",
"farming:desert_sand_soil",
"farming:desert_sand_soil_wet",
"farming:dry_soil",
"farming:dry_soil_wet",
"farming:soil",
"farming:soil_wet",
})
unified_inventory.add_category_items('lighting', {
"default:mese_post_light_junglewood",
"default:torch_ceiling",
"default:meselamp",
"default:torch",
"default:mese_post_light_acacia_wood",
"default:mese_post_light",
"default:torch_wall",
"default:mese_post_light_pine_wood",
"default:mese_post_light_aspen_wood"
})
--]]
--[[ UNCATEGORISED
"farming:string",
"beds:bed_bottom",
"beds:bed_top",
"beds:fancy_bed_bottom",
"beds:fancy_bed_top",
"boats:boat",
"bones:bones",
"bucket:bucket_lava",
"bucket:bucket_river_water",
"bucket:bucket_water",
"butterflies:butterfly_red",
"butterflies:butterfly_violet",
"butterflies:butterfly_white",
"butterflies:hidden_butterfly_red",
"butterflies:hidden_butterfly_violet",
"butterflies:hidden_butterfly_white",
"carts:brakerail",
"carts:cart",
"carts:powerrail",
"carts:rail",
"default:book",
"default:book_written",
"default:bookshelf",
"default:chest",
"default:chest_locked",
"default:chest_locked_open",
"default:chest_open",
"default:clay_lump",
"default:cloud",
"default:coral_brown",
"default:coral_cyan",
"default:coral_green",
"default:coral_orange",
"default:coral_pink",
"default:coral_skeleton",
"default:flint",
"default:furnace",
"default:furnace_active",
"default:key",
"default:ladder_steel",
"default:ladder_wood",
"default:obsidian_shard",
"default:paper",
"default:sign_wall_steel",
"default:sign_wall_wood",
"default:stick",
"fire:basic_flame",
"fire:permanent_flame",
"fireflies:firefly",
"fireflies:firefly_bottle",
"fireflies:hidden_firefly",
"ignore",
"unknown",
"tnt:boom",
"tnt:gunpowder",
"tnt:gunpowder_burning",
"tnt:tnt",
"tnt:tnt_burning",
"tnt:tnt_stick",
"vessels:drinking_glass",
"vessels:glass_bottle",
"vessels:glass_fragments",
"vessels:shelf",
"vessels:steel_bottle",
"dye:black",
"dye:blue",
"dye:brown",
"dye:cyan",
"dye:dark_green",
"dye:dark_grey",
"dye:green",
"dye:grey",
"dye:magenta",
"dye:orange",
"dye:pink",
"dye:red",
"dye:violet",
"dye:white",
"dye:yellow",
"wool:black",
"wool:blue",
"wool:brown",
"wool:cyan",
"wool:dark_green",
"wool:dark_grey",
"wool:green",
"wool:grey",
"wool:magenta",
"wool:orange",
"wool:pink",
"wool:red",
"wool:violet",
"wool:white",
"wool:yellow",
"unified_inventory:bag_large",
"unified_inventory:bag_medium",
"unified_inventory:bag_small",
--]]
--[[ LIST UNCATEGORIZED AFTER LOAD
minetest.register_on_mods_loaded(function()
minetest.after(1, function ( )
local l = {}
for name,_ in pairs(minetest.registered_items) do
if not unified_inventory.find_category(name) then
-- minetest.log("error", minetest.serialize(minetest.registered_items[name]))
table.insert(l, name)
end
end
table.sort(l)
minetest.log(table.concat(l, '",'.."\n"..'"'))
end)
end)
--]]

View File

@ -101,3 +101,72 @@ Register a non-standard craft recipe:
-- ^ Same as `minetest.register_recipe`
})
Categories
----------
Register a new category:
The config table (second argument) is optional, and all its members are optional
See the unified_inventory.set_category_* functions for more details on the members of the config table
unified_inventory.register_category("category_name", {
symbol = "mod_name:item_name" or "texture.png",
label = "Human Readable Label",
index = 5,
items = {
"mod_name:item_name",
"another_mod:different_item"
}
})
Add / override the symbol for a category:
The category does not need to exist first
The symbol can be an item name or a texture image
If unset this will default to "default:stick"
unified_inventory.set_category_symbol("category_name", "mod_name:item_name" or "texture.png")
Add / override the human readable label for a category:
If unset this will default to the category name
unified_inventory.set_category_label("category_name", "Human Readable Label")
Add / override the sorting index of the category:
Must be a number, can also be negative (-5) or fractional (2.345)
This determines the position the category appears in the list of categories
The "all" meta-category has index -2, the "misc"/"uncategorized" meta-category has index -1, use a negative number smaller than these to make a category appear before these in the list
By default categories are sorted alphabetically with an index between 0.0101(AA) and 0.2626(ZZ)
unified_inventory.set_category_index("category_name", 5)
Add a single item to a category:
unified_inventory.add_category_item("category_name", "mod_name:item_name")
Add multiple items to a category:
unified_inventory.add_category_items("category_name", {
"mod_name:item_name",
"another_mod:different_item"
})
Remove an item from a category:
unified_inventory.remove_category_item("category_name", "mod_name:item_name")
Remove a category entirely:
unified_inventory.remove_category("category_name")
Finding existing items in categories:
This will find the first category an item exists in
It should be used for checking if an item is catgorised
Returns "category_name" or nil
unified_inventory.find_category("mod_name:item_name")
This will find all the categories an item exists in
Returns a number indexed table (list) of category names
unified_inventory.find_categories("mod_name:item_name")

View File

@ -10,6 +10,8 @@ unified_inventory = {
alternate = {},
current_page = {},
current_searchbox = {},
current_category = {},
current_category_scroll = {},
current_index = {},
current_item = {},
current_craft_direction = {},
@ -33,6 +35,9 @@ unified_inventory = {
-- "Lite" mode
lite_mode = minetest.settings:get_bool("unified_inventory_lite"),
-- Items automatically added to categories based on item definitions
automatic_categorization = (minetest.settings:get_bool("unified_inventory_automatic_categorization") ~= false),
-- Trash enabled
trash_enabled = (minetest.settings:get_bool("unified_inventory_trash") ~= false),
imgscale = 1.25,
@ -52,9 +57,9 @@ ui.style_full = {
formw = 17.75,
formh = 12.25,
pagecols = 8,
pagerows = 10,
pagerows = 9,
page_x = 10.75,
page_y = 1.45,
page_y = 2.30,
craft_x = 2.8,
craft_y = 1.15,
craftresult_x = 7.8,
@ -85,9 +90,9 @@ ui.style_lite = {
formw = 14,
formh = 9.75,
pagecols = 4,
pagerows = 6,
pagerows = 5,
page_x = 10.5,
page_y = 1.25,
page_y = 2.15,
craft_x = 2.6,
craft_y = 0.75,
craftresult_x = 5.75,
@ -100,9 +105,9 @@ ui.style_lite = {
craft_guide_resultstr_y = 0.35,
give_btn_x = 0.15,
main_button_x = 10.5,
main_button_y = 7.9,
main_button_y = 8.15,
page_buttons_x = 10.5,
page_buttons_y = 6.3,
page_buttons_y = 6.15,
searchwidth = 1.6,
form_header_x = 0.2,
form_header_y = 0.2,
@ -149,6 +154,8 @@ if sfinv then
end
dofile(modpath.."/group.lua")
dofile(modpath.."/category.lua")
dofile(modpath.."/default-categories.lua")
dofile(modpath.."/internal.lua")
dofile(modpath.."/callbacks.lua")
dofile(modpath.."/match_craft.lua")

View File

@ -25,6 +25,20 @@ function ui.get_per_player_formspec(player_name)
return table.copy(draw_lite_mode and ui.style_lite or ui.style_full), draw_lite_mode
end
local function formspec_button(ui_peruser, name, image, offset, pos, scale, label)
local element = 'image_button'
if minetest.registered_items[image] then
element = 'item_image_button'
end
local spc = (1-scale)*ui_peruser.btn_size/2
local size = ui_peruser.btn_size*scale
return string.format("%s[%f,%f;%f,%f;%s;%s;]", element,
(offset.x or offset[1]) + ( ui_peruser.btn_spc * (pos.x or pos[1]) ) + spc,
(offset.y or offset[2]) + ( ui_peruser.btn_spc * (pos.y or pos[2]) ) + spc,
size, size, image, name) ..
string.format("tooltip[%s;%s]", name, F(label or name))
end
function ui.get_formspec(player, page)
if not player then
@ -107,6 +121,48 @@ function ui.get_formspec(player, page)
return table.concat(formspec, "")
end
-- Category filters
local categories_pos = { ui_peruser.page_x, ui_peruser.page_y-ui_peruser.btn_spc-0.5 }
local categories_scroll_pos = { ui_peruser.page_x, ui_peruser.form_header_y-(draw_lite_mode and 0 or 0.2) }
formspec[n] = string.format("background9[%f,%f;%f,%f;%s;false;3]",
ui_peruser.page_x-0.1, categories_scroll_pos[2],
(ui_peruser.btn_spc * ui_peruser.pagecols) + 0.13, 1.4+(draw_lite_mode and 0 or 0.2),
"ui_smallbg_9_sliced.png")
n = n + 1
formspec[n] = string.format("label[%f,%f;%s]", ui_peruser.page_x, ui_peruser.form_header_y+(draw_lite_mode and 0.3 or 0.2), "Category:")
n = n + 1
local scroll_offset = 0
local category_count = #unified_inventory.category_list
if category_count > ui_peruser.pagecols then
scroll_offset = unified_inventory.current_category_scroll[player_name]
end
for index, category in ipairs(unified_inventory.category_list) do
local column = index - scroll_offset
if column > 0 and column <= ui_peruser.pagecols then
local scale = 0.8
if unified_inventory.current_category[player_name] == category.name then
scale = 1
end
formspec[n] = formspec_button(ui_peruser, "category_"..category.name, category.symbol, categories_pos, {column-1, 0}, scale, category.label)
n = n + 1
end
end
if category_count > ui_peruser.pagecols and scroll_offset > 0 then
-- prev
formspec[n] = formspec_button(ui_peruser, "prev_category", "ui_left_icon.png", categories_scroll_pos, {ui_peruser.pagecols - 2, 0}, 0.8, S("Scroll categories left"))
n = n + 1
end
if category_count > ui_peruser.pagecols and category_count - scroll_offset > ui_peruser.pagecols then
-- next
formspec[n] = formspec_button(ui_peruser, "next_category", "ui_right_icon.png", categories_scroll_pos, {ui_peruser.pagecols - 1, 0}, 0.8, S("Scroll categories right"))
n = n + 1
end
-- Search box
formspec[n] = "field_close_on_enter[searchbox;false]"
@ -205,16 +261,16 @@ function ui.get_formspec(player, page)
end
end
formspec[n] = string.format("label[%f,%f;%s: %s]",
ui_peruser.page_x, ui_peruser.form_header_y,
ui_peruser.page_buttons_x + ui_peruser.btn_spc * (draw_lite_mode and 1 or 2),
ui_peruser.page_buttons_y + 0.1 + ui_peruser.btn_spc * 2,
F(S("Page")), S("@1 of @2",page2,pagemax))
end
n= n+1
if ui.activefilter[player_name] ~= "" then
formspec[n] = string.format("label[%f,%f;%s:]",
ui_peruser.page_x, ui_peruser.page_y - 0.65, F(S("Filter")))
formspec[n+1] = string.format("label[%f,%f;%s]",
ui_peruser.page_x, ui_peruser.page_y - 0.25, F(ui.activefilter[player_name]))
formspec[n] = string.format("label[%f,%f;%s: %s]",
ui_peruser.page_x, ui_peruser.page_y - 0.25,
F(S("Filter")), F(ui.activefilter[player_name]))
end
return table.concat(formspec, "")
end
@ -225,6 +281,13 @@ function ui.set_inventory_formspec(player, page)
end
end
local function valid_def(def)
return (not def.groups.not_in_creative_inventory
or def.groups.not_in_creative_inventory == 0)
and def.description
and def.description ~= ""
end
--apply filter to the inventory list (create filtered copy of full one)
function ui.apply_filter(player, filter, search_dir)
if not player then
@ -256,13 +319,30 @@ function ui.apply_filter(player, filter, search_dir)
end
end
ui.filtered_items_list[player_name]={}
for name, def in pairs(minetest.registered_items) do
if (not def.groups.not_in_creative_inventory
or def.groups.not_in_creative_inventory == 0)
and def.description
and def.description ~= ""
and ffilter(name, def) then
table.insert(ui.filtered_items_list[player_name], name)
local category = ui.current_category[player_name] or 'all'
if category == 'all' then
for name, def in pairs(minetest.registered_items) do
if valid_def(def)
and ffilter(name, def) then
table.insert(ui.filtered_items_list[player_name], name)
end
end
elseif category == 'uncategorized' then
for name, def in pairs(minetest.registered_items) do
if (not ui.find_category(name))
and valid_def(def)
and ffilter(name, def) then
table.insert(ui.filtered_items_list[player_name], name)
end
end
else
for name,exists in pairs(ui.registered_category_items[category]) do
local def = minetest.registered_items[name]
if exists and def
and valid_def(def)
and ffilter(name, def) then
table.insert(ui.filtered_items_list[player_name], name)
end
end
end
table.sort(ui.filtered_items_list[player_name])

View File

@ -9,3 +9,6 @@ unified_inventory_bags (Enable bags) bool true
#If enabled, the trash slot can be used by those without both creative
#and the give privilege.
unified_inventory_trash (Enable trash) bool true
unified_inventory_automatic_categorization (Items automatically added to categories) bool true

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B