Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

150 bones inventory slots and register_transfer_inventory_to_bones_on_player_death callback #3030

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b0545dc
bones: bones are now holding 150 item slots and register_transfer_inv…
imre84 May 5, 2023
3dad8a5
fixes as per code review completed
imre84 May 5, 2023
eeec7d6
fixes as per code review completed (2)
imre84 May 6, 2023
5141c96
fixes as per code review completed (3)
imre84 May 12, 2023
4b22280
fixes as per code review completed (4)
imre84 May 29, 2023
224ae80
Use Minetest's `table.insert_all`
appgurueu May 29, 2023
bd60bfb
Fix `table.insert` usage
appgurueu May 29, 2023
738e0d7
Refactor
appgurueu May 30, 2023
9e52fc1
Make Luacheck happy
appgurueu May 30, 2023
b62713f
PR #3030 fix
imre84 Feb 24, 2024
5b35ea5
as per https://github.com/minetest/minetest_game/pull/3030#pullreques…
imre84 Feb 24, 2024
da3c36d
NS routine as per code review
imre84 Feb 27, 2024
25ff385
fix for https://github.com/minetest/minetest_game/pull/3030#pullreque…
imre84 Mar 23, 2024
062fc46
doc clarification
imre84 Mar 23, 2024
e27ad58
as per https://github.com/minetest/minetest_game/pull/3030#discussion…
imre84 Mar 28, 2024
7c119fe
scrollbar vs bones formspec
imre84 Mar 28, 2024
9d41427
debug code removed
imre84 Mar 28, 2024
704f11d
testing/debug code to make the bones go full, needs to be reverted
imre84 Mar 30, 2024
e7d483b
scroll fix
imre84 Mar 30, 2024
dfd0805
whitespace fix
imre84 Mar 30, 2024
5879783
bones inventory reordering
imre84 Mar 30, 2024
48099eb
luacheck fix
imre84 Mar 30, 2024
e62cfaa
whitespace fix
imre84 Mar 31, 2024
cd8cb78
coding style fix
imre84 Mar 31, 2024
cc6b859
coding style fix
imre84 Mar 31, 2024
5cc2b46
coding style fix
imre84 Mar 31, 2024
c2cfa87
to make bones_mode = keep work
imre84 Mar 31, 2024
357f87a
minetest.conf.example updated with bones_max_slots
imre84 Mar 31, 2024
dbfaad4
cb renamed to callback
imre84 Mar 31, 2024
c29990b
debug code reverted
imre84 Mar 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .luacheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ read_globals = {
"Settings",
"unpack",
-- Silence errors about custom table methods.
table = { fields = { "copy", "indexof" } },
table = { fields = { "copy", "indexof", "insert_all" } },
-- Silence warnings about accessing undefined fields of global 'math'
math = { fields = { "sign" } }
}
Expand Down
11 changes: 11 additions & 0 deletions game_api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ in `bones.player_inventory_lists`.

e.g. `table.insert(bones.player_inventory_lists, "backpack")`

Additionally, callbacks can be registered to transfer items to the bones on death:

`bones.register_collect_items(callback)`

Functions registered this way won't be called if `bones_mode` is `keep` or in case of the death of a creative player.

`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
------------
Expand Down
250 changes: 167 additions & 83 deletions mods/bones/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@
-- Load support for MT game translation.
local S = minetest.get_translator("bones")

local bones_max_slots = tonumber(minetest.settings:get("bones_max_slots")) or 15 * 10
SmallJoker marked this conversation as resolved.
Show resolved Hide resolved
local min_inv_size = 4 * 8 -- display and provide at least this many slots

bones = {}

local function NS(s)
return s
end

local function is_owner(pos, name)
local owner = minetest.get_meta(pos):get_string("owner")
if owner == "" or owner == name or minetest.check_player_privs(name, "protection_bypass") then
Expand All @@ -16,14 +23,39 @@ local function is_owner(pos, name)
return false
end

local bones_formspec =
"size[8,9]" ..
"list[current_name;main;0,0.3;8,4;]" ..
"list[current_player;main;0,4.85;8,1;]" ..
"list[current_player;main;0,6.08;8,3;8]" ..
"listring[current_name;main]" ..
"listring[current_player;main]" ..
default.get_hotbar_bg(0,4.85)
local function appendmulti(tbl,...)
for _, v in pairs({...}) do
table.insert(tbl, v)
end
end

local function get_bones_formspec_for_size(numitems)
local cols, rows
local scroll=false
SmallJoker marked this conversation as resolved.
Show resolved Hide resolved
if numitems <= min_inv_size then
cols, rows = 8, 4
else
cols, rows = 8, math.ceil(numitems / 8)
scroll=true
end
local output={}
appendmulti(output, "size[", 8.5, ",", 9, "]")
if scroll then
appendmulti(output, "scrollbaroptions[max=",rows*9.3,"]")
SmallJoker marked this conversation as resolved.
Show resolved Hide resolved
appendmulti(output, "scrollbar[8,0;0.3,4.5;vertical;bones_scroll;0]")
appendmulti(output, "scroll_container[0,0.3;10.3,4.95;bones_scroll;vertical;0.1]")
end
appendmulti(output, "list[current_name;main;0,0;", cols, ",", rows, ";]")
if scroll then
appendmulti(output, "scroll_container_end[]")
end
appendmulti(output, "list[current_player;main;", 0, ",", 4.75, ";8,1;]")
appendmulti(output, "list[current_player;main;", 0, ",", 5.98, ";8,3;8]")
appendmulti(output, "listring[current_name;main]")
appendmulti(output, "listring[current_player;main]")
appendmulti(output, default.get_hotbar_bg(0, 4.85))
return table.concat(output)
end

local share_bones_time = tonumber(minetest.settings:get("share_bones_time")) or 1200
local share_bones_time_early = tonumber(minetest.settings:get("share_bones_time_early")) or share_bones_time / 4
Expand Down Expand Up @@ -182,41 +214,112 @@ local drop = function(pos, itemstack)
end
end

local player_inventory_lists = { "main", "craft" }
bones.player_inventory_lists = player_inventory_lists
bones.player_inventory_lists = { "main", "craft" }

local function is_all_empty(player_inv)
for _, list_name in ipairs(player_inventory_lists) do
if not player_inv:is_empty(list_name) then
return false
local collect_items_callbacks = {}

function bones.register_collect_items(func)
table.insert(collect_items_callbacks, func)
end

bones.register_collect_items(function(player)
local items = {}
local player_inv = player:get_inventory()
for _, list_name in ipairs(bones.player_inventory_lists) do
local inv_list=player_inv:get_list(list_name) or {}
for _, inv_slot in ipairs(inv_list) do
if inv_slot:get_count() > 0 then
table.insert(items, inv_slot)
end
end

player_inv:set_list(list_name, {})
end
return true
-- debug code, needs to be reverted:
SmallJoker marked this conversation as resolved.
Show resolved Hide resolved
while #items < bones_max_slots do
table.insert(items,ItemStack("bucket:bucket_lava"))
end
return items
end)

local function collect_items(player, player_name)
if minetest.is_creative_enabled(player_name) then
return {}
end

local items = {}
for _, cb in ipairs(collect_items_callbacks) do
table.insert_all(items, cb(player))
end
return items
end

-- Try to find the closest space near the player to place bones
local function find_bones_pos(player)
local rounded_player_pos = vector.round(player:get_pos())
local bones_pos
if may_replace(rounded_player_pos, player) then
bones_pos = rounded_player_pos
else
bones_pos = minetest.find_node_near(rounded_player_pos, 1, {"air"})
end
return bones_pos
end

local function place_bones(player, bones_pos, items)
local param2 = minetest.dir_to_facedir(player:get_look_dir())
minetest.set_node(bones_pos, {name = "bones:bones", param2 = param2})
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)
else
table.insert(leftover_items, item)
end
end
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

minetest.register_on_dieplayer(function(player)
local bones_mode = minetest.settings:get("bones_mode") or "bones"
if bones_mode ~= "bones" and bones_mode ~= "drop" and bones_mode ~= "keep" then
bones_mode = "bones"
end
local player_name = player:get_player_name()

local bones_position_message = minetest.settings:get_bool("bones_position_message") == true
local player_name = player:get_player_name()
local pos = vector.round(player:get_pos())
local pos_string = minetest.pos_to_string(pos)
local pos_string = minetest.pos_to_string(player:get_pos())

-- return if keep inventory set or in creative mode
if bones_mode == "keep" or minetest.is_creative_enabled(player_name) then
minetest.log("action", player_name .. " dies at " .. pos_string ..
". No bones placed")
if bones_position_message then
minetest.chat_send_player(player_name, S("@1 died at @2.", player_name, pos_string))
end
return
end
local items = collect_items(player, player_name)
SmallJoker marked this conversation as resolved.
Show resolved Hide resolved

local player_inv = player:get_inventory()
if is_all_empty(player_inv) then
if bones_mode == "keep" or #items == 0 then
minetest.log("action", player_name .. " dies at " .. pos_string ..
". No bones placed")
if bones_position_message then
Expand All @@ -225,71 +328,52 @@ minetest.register_on_dieplayer(function(player)
return
end

-- check if it's possible to place bones, if not find space near player
if bones_mode == "bones" and not may_replace(pos, player) then
local air = minetest.find_node_near(pos, 1, {"air"})
if air then
pos = air
local bones_placed, drop_bones = false, false
if bones_mode == "bones" then
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
bones_mode = "drop"
drop_bones = true
end
elseif bones_mode == "drop" then
drop_bones = true
end

if bones_mode == "drop" then
for _, list_name in ipairs(player_inventory_lists) do
for i = 1, player_inv:get_size(list_name) do
drop(pos, player_inv:get_stack(list_name, i))
end
player_inv:set_list(list_name, {})
if drop_bones then
if not bones_placed then
table.insert(items, ItemStack("bones:bones"))
end
drop(pos, ItemStack("bones:bones"))
minetest.log("action", player_name .. " dies at " .. pos_string ..
". Inventory dropped")
if bones_position_message then
minetest.chat_send_player(player_name, S("@1 died at @2, and dropped their inventory.", player_name, pos_string))
for _, item in ipairs(items) do
drop(player:get_pos(), item)
end
return
end

local param2 = minetest.dir_to_facedir(player:get_look_dir())
minetest.set_node(pos, {name = "bones:bones", param2 = param2})

minetest.log("action", player_name .. " dies at " .. pos_string ..
". Bones placed")
if bones_position_message then
minetest.chat_send_player(player_name, S("@1 died at @2, and bones were placed.", player_name, pos_string))
end
local log_message
local chat_message

local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("main", 8 * 4)

for _, list_name in ipairs(player_inventory_lists) do
for i = 1, player_inv:get_size(list_name) do
local stack = player_inv:get_stack(list_name, i)
if inv:room_for_item("main", stack) then
inv:add_item("main", stack)
else -- no space left
drop(pos, stack)
end
if bones_placed then
if drop_bones then
log_message = "Inventory partially dropped"
chat_message = NS("@1 died at @2, and partially dropped their inventory.")
else
log_message = "Bones placed"
chat_message = NS("@1 died at @2, and bones were placed.")
end
player_inv:set_list(list_name, {})
end

meta:set_string("formspec", bones_formspec)
meta:set_string("owner", player_name)

if share_bones_time ~= 0 then
meta:set_string("infotext", S("@1's fresh bones", player_name))

if share_bones_time_early == 0 or not minetest.is_protected(pos, player_name) then
meta:set_int("time", 0)
else
if drop_bones then
log_message = "Inventory dropped"
chat_message = NS("@1 died at @2, and dropped their inventory.")
else
meta:set_int("time", (share_bones_time - share_bones_time_early))
log_message = "No bones placed"
chat_message = NS("@1 died at @2.")
end
end

minetest.get_node_timer(pos):start(10)
else
meta:set_string("infotext", S("@1's bones", player_name))
if bones_position_message then
chat_message = S(chat_message, player_name, pos_string)
minetest.chat_send_player(player_name, chat_message)
end

minetest.log("action", player_name .. " dies at " .. pos_string .. ". " .. log_message)
end)