This commit is contained in:
Lars Mueller 2023-05-30 10:26:17 +02:00 committed by Imre Péntek
parent bd60bfb549
commit 738e0d7292
2 changed files with 110 additions and 128 deletions

View File

@ -83,13 +83,16 @@ in `bones.player_inventory_lists`.
e.g. `table.insert(bones.player_inventory_lists, "backpack")` e.g. `table.insert(bones.player_inventory_lists, "backpack")`
Additionally, callbacks can be registered to transfer items into the bones on death: Additionally, callbacks can be registered to transfer items to the bones on death:
`bones.register_dead_player_inv_management(function(player){})` `bones.register_collect_items(callback)`
the registered function is supposed to return with a table of items to be transferred. Functions registered this way won't be called if `bones_mode` is `keep`.
please note that the inventory these items were taken from still need to be disposed of. `callback` is a `function(player)` which `return`s a table of items to be transferred.
Please note that this does not remove the items from the inventory they were taken of.
Disposing of the items in this inventory is the `callback`'s responsibility.
Creative API Creative API

View File

@ -7,9 +7,7 @@
local S = minetest.get_translator("bones") local S = minetest.get_translator("bones")
local bones_max_slots = minetest.settings:get("bones_max_slots") or 15 * 10 local bones_max_slots = minetest.settings:get("bones_max_slots") or 15 * 10
local dead_player_callbacks={} local min_inv_size = 4 * 8 -- display and provide at least this many slots
-- we're going to display no less than 4*8 slots, we'll also provide at least 4*8 slots in bones
local min_inv_size = 4 * 8
bones = {} bones = {}
@ -21,28 +19,24 @@ local function is_owner(pos, name)
return false return false
end end
local function get_bones_formspec_wh(cols,rows)
return
"size[" .. cols .. "," .. (rows + 5) .. "]" ..
"list[current_name;main;0,0.3;" .. cols .. "," .. rows .. ";]" ..
"list[current_player;main;" .. ((cols - 8) / 2) .. "," .. rows .. ".85;8,1;]" ..
"list[current_player;main;".. ((cols - 8) / 2) .. "," .. (rows + 2) .. ".08;8,3;8]" ..
"listring[current_name;main]" ..
"listring[current_player;main]" ..
default.get_hotbar_bg(0,4.85)
end
local function get_bones_formspec_for_size(numitems) local function get_bones_formspec_for_size(numitems)
--the absolute minimum is 4*8 local cols, rows
if numitems <= min_inv_size then if numitems <= min_inv_size then
return get_bones_formspec_wh(8, 4) cols, rows = 8, 4
elseif numitems <= 4 * 15 then
cols, rows = math.ceil(numitems / 4), 4
else
cols, rows = 15, math.ceil(numitems / 15)
end end
--if we're over 4*8, but below 4*15 we make it 4 rows and adjust the column count to make everything fit return table.concat{
if numitems <= 4 * 15 then "size[", cols, ",", rows + 5, "]",
return get_bones_formspec_wh(math.ceil(numitems / 4), 4) "list[current_name;main;0,0.3;", cols, ",", rows, ";]",
end "list[current_player;main;", (cols - 8) / 2, ",", rows + 0.85, ";8,1;]",
--if we're over 4*15 we'll make 15 columns and adjust the row count to make everything fit "list[current_player;main;", (cols - 8) / 2, ",", rows + 2.08, ";8,3;8]",
return get_bones_formspec_wh(15, math.ceil(numitems / 15)) "listring[current_name;main]",
"listring[current_player;main]",
default.get_hotbar_bg(0, 4.85)
}
end end
local share_bones_time = tonumber(minetest.settings:get("share_bones_time")) or 1200 local share_bones_time = tonumber(minetest.settings:get("share_bones_time")) or 1200
@ -202,95 +196,101 @@ local drop = function(pos, itemstack)
end end
end end
local player_inventory_lists = { "main", "craft" } bones.player_inventory_lists = { "main", "craft" }
bones.player_inventory_lists = player_inventory_lists
--functions registered this way won't be called if bones_mode is keep local collect_items_callbacks = {}
function bones.register_dead_player_inv_management(func)
table.insert(dead_player_callbacks, func) function bones.register_collect_items(func)
table.insert(collect_items_callbacks, func)
end end
local function player_dies_transfer_inventory(player) bones.register_collect_items(function(player)
local result = {} local items = {}
local player_inv = player:get_inventory() local player_inv = player:get_inventory()
for _, list_name in ipairs(player_inventory_lists) do for _, list_name in ipairs(bones.player_inventory_lists) do
for i = 1, player_inv:get_size(list_name) do table.insert_all(items, player_inv:get_list(list_name) or {})
table.insert(result, player_inv:get_stack(list_name, i))
end
player_inv:set_list(list_name, {}) player_inv:set_list(list_name, {})
end end
return result return items
end)
local function collect_items(player)
local items = {}
for _, cb in ipairs(collect_items_callbacks) do
table.insert_all(items, cb(player))
end
return items
end end
bones.register_dead_player_inv_management(player_dies_transfer_inventory) -- Try to find the closest space near the player to place bones
local function find_bones_pos(player)
local function collect_all_items(player) local rounded_player_pos = vector.round(player:get_pos())
local all_items = {} local bones_pos
for _, fun in ipairs(dead_player_callbacks) do if may_replace(rounded_player_pos, player) then
local items = fun(player) bones_pos = rounded_player_pos
table.insert_all(all_items, items)
end
return all_items
end
-- check if it's possible to place bones, if not find space near player
local function find_node_for_bones_on_player_death(player, player_pos)
local bones_pos = nil
local bones_inv = nil
local bones_meta = nil
local bones_mode = "bones"
bones_pos = player_pos
local air
if may_replace(bones_pos, player) then
air = bones_pos
else else
air = minetest.find_node_near(bones_pos, 1, {"air"}) bones_pos = minetest.find_node_near(rounded_player_pos, 1, {"air"})
end end
if minetest.is_protected(bones_pos, player:get_player_name()) then
if air and not minetest.is_protected(air, player_name) then
bones_pos = air
local param2 = minetest.dir_to_facedir(player:get_look_dir())
minetest.set_node(bones_pos, {name = "bones:bones", param2 = param2})
bones_meta = minetest.get_meta(bones_pos)
bones_inv = bones_meta:get_inventory()
--make it so big that anything reasonable will for sure fit inside
bones_inv:set_size("main", bones_max_slots)
else
bones_mode = "drop"
bones_pos = nil bones_pos = nil
end end
return bones_mode, bones_pos, bones_inv, bones_meta return bones_pos
end end
local function dump_into(bones_mode, bones_inv, bones_pos, all_inventory) local function place_bones(player, bones_pos, items)
local dropped = false local param2 = minetest.dir_to_facedir(player:get_look_dir())
for _, item in ipairs(all_inventory) do minetest.set_node(bones_pos, {name = "bones:bones", param2 = param2})
if bones_mode == "bones" and bones_inv:room_for_item("main", item) then local bones_meta = minetest.get_meta(bones_pos)
local bones_inv = bones_meta:get_inventory()
-- Make it big enough that anything reasonable will fit
bones_inv:set_size("main", bones_max_slots)
local leftover_items = {}
for _, item in ipairs(items) do
if bones_inv:room_for_item("main", item) then
bones_inv:add_item("main", item) bones_inv:add_item("main", item)
else else
drop(player_pos, item) table.insert(leftover_items, item)
dropped = true
end end
end end
return dropped local inv_size = bones_max_slots
for i = 1, bones_max_slots do
if bones_inv:get_stack("main", i):get_count() == 0 then
inv_size = i - 1
break
end
end
bones_inv:set_size("main", math.max(inv_size, min_inv_size))
bones_meta:set_string("formspec", get_bones_formspec_for_size(inv_size))
-- "Ownership"
local player_name = player:get_player_name()
bones_meta:set_string("owner", player_name)
if share_bones_time ~= 0 then
bones_meta:set_string("infotext", S("@1's fresh bones", player_name))
if share_bones_time_early == 0 or not minetest.is_protected(bones_pos, player_name) then
bones_meta:set_int("time", 0)
else
bones_meta:set_int("time", share_bones_time - share_bones_time_early)
end
minetest.get_node_timer(bones_pos):start(10)
else
bones_meta:set_string("infotext", S("@1's bones", player_name))
end
return leftover_items
end end
minetest.register_on_dieplayer(function(player) minetest.register_on_dieplayer(function(player)
local player_pos = vector.round(player:get_pos())
local bones_mode = minetest.settings:get("bones_mode") or "bones" local bones_mode = minetest.settings:get("bones_mode") or "bones"
if bones_mode ~= "bones" and bones_mode ~= "drop" and bones_mode ~= "keep" then if bones_mode ~= "bones" and bones_mode ~= "drop" and bones_mode ~= "keep" then
bones_mode = "bones" bones_mode = "bones"
end end
local player_name = player:get_player_name() local player_name = player:get_player_name()
local bones_inv = nil
local bones_pos = nil
local bones_meta = nil
local bones_position_message = minetest.settings:get_bool("bones_position_message") == true local bones_position_message = minetest.settings:get_bool("bones_position_message") == true
local pos_string = minetest.pos_to_string(player_pos) local pos_string = minetest.pos_to_string(player:get_pos())
-- return if keep inventory set or in creative mode local items = collect_items(player)
if bones_mode == "keep" or minetest.is_creative_enabled(player_name) then
if bones_mode == "keep" or minetest.is_creative_enabled(player_name) or #items == 0 then
minetest.log("action", player_name .. " dies at " .. pos_string .. minetest.log("action", player_name .. " dies at " .. pos_string ..
". No bones placed") ". No bones placed")
if bones_position_message then if bones_position_message then
@ -299,20 +299,32 @@ minetest.register_on_dieplayer(function(player)
return return
end end
local all_items = collect_all_items(player) local bones_placed, drop_bones = false, false
if bones_mode == "bones" and #all_items > 0 then if bones_mode == "bones" then
bones_mode, bones_pos, bones_inv, bones_meta = find_node_for_bones_on_player_death(player, player_pos) local bones_pos = find_bones_pos(player)
if bones_pos then
items = place_bones(player, bones_pos, items)
bones_placed, drop_bones = true, #items ~= 0
else
drop_bones = true
end
elseif bones_mode == "drop" then
drop_bones = true
end end
if bones_mode == "drop" and #all_items > 0 then if drop_bones then
table.insert(all_items, ItemStack("bones:bones")) if not bones_placed then
table.insert(items, ItemStack("bones:bones"))
end
for _, item in ipairs(items) do
drop(player:get_pos(), item)
end
end end
local dropped = dump_into(bones_mode, bones_inv, bones_pos, all_items)
local log_message local log_message
local chat_message local chat_message
if bones_pos then if bones_placed then
if dropped then if drop_bones then
log_message = "Inventory partially dropped" log_message = "Inventory partially dropped"
chat_message = "@1 died at @2, and partially dropped their inventory." chat_message = "@1 died at @2, and partially dropped their inventory."
else else
@ -320,8 +332,7 @@ minetest.register_on_dieplayer(function(player)
chat_message = "@1 died at @2, and bones were placed." chat_message = "@1 died at @2, and bones were placed."
end end
else else
drop(player_pos, ItemStack("bones:bones")) if drop_bones then
if dropped then
log_message = "Inventory dropped" log_message = "Inventory dropped"
chat_message = "@1 died at @2, and dropped their inventory." chat_message = "@1 died at @2, and dropped their inventory."
else else
@ -336,36 +347,4 @@ minetest.register_on_dieplayer(function(player)
end end
minetest.log("action", player_name .. " dies at " .. pos_string .. ". " .. log_message) minetest.log("action", player_name .. " dies at " .. pos_string .. ". " .. log_message)
if bones_inv then
local inv_size = bones_max_slots
for i = 1, bones_max_slots do
local stack = bones_inv:get_stack("main", i)
if stack:get_count() == 0 then
inv_size = i - 1
break
end
end
if inv_size <= min_inv_size then
bones_inv:set_size("main", min_inv_size)
else
bones_inv:set_size("main", inv_size)
end
bones_meta:set_string("formspec", get_bones_formspec_for_size(inv_size))
bones_meta:set_string("owner", player_name)
if share_bones_time ~= 0 then
bones_meta:set_string("infotext", S("@1's fresh bones", player_name))
if share_bones_time_early == 0 or not minetest.is_protected(bones_pos, player_name) then
bones_meta:set_int("time", 0)
else
bones_meta:set_int("time", (share_bones_time - share_bones_time_early))
end
minetest.get_node_timer(bones_pos):start(10)
else
bones_meta:set_string("infotext", S("@1's bones", player_name))
end
end
end) end)