minetest-mod-dinv/inventory.lua

1228 lines
29 KiB
Lua

-- Dinv inventory.lua
-- Copyright Duane Robertson (duane@duanerobertson.com), 2019
-- Distributed under the LGPLv2.1 (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)
local mod = dinv
local mod_name = 'dinv'
local sorted_items
local ADD_SCROLL_TO_DEFAULT_CHEST = true
local PROTECT_INVENTORY = false -- Prevent ANY inventory loss.
local WORN_INV = 'worn'
local force_rep = {
['wood'] = 'default:wood',
['leaves'] = 'default:leaves',
['stone'] = 'default:stone',
['wool'] = 'wool:white',
}
-- This tables looks up groups that aren't already stored.
mod.group_rep = setmetatable({}, {
__index = function(t, k)
if not (t and k and type(k) == 'string' and type(t) == 'table') then
return
end
local k = k:gsub('^group:', '')
local r
if force_rep[k] then
r = force_rep[k]
else
for a, b in pairs(minetest.registered_items) do
if b.groups and b.groups[k] then
r = a
break
end
end
end
if r then
t[k] = r
return t[k]
else
return
end
end
})
local group_rep = mod.group_rep
-- iterator over worn inventory
function mod.worn_items(player)
if not player then
return
end
local pinv = player:get_inventory()
if not pinv then
return
end
local flist = pinv:get_list(WORN_INV)
return pairs(flist)
end
local worn_items = mod.worn_items
-- Formspec definitions are confusing.
mod.form_size = 'size[11.25,7.25]'
mod.main_inventory = 'list[current_player;main;2.25,3.5;8,4;'
mod.craft_inventory = 'list[current_player;craft;3.25,0;3,3;]'
mod.craft_preview = 'list[current_player;craftpreview;6.25,2;1,1;]'
mod.main_inventory_scroll_up = 'image_button[10.25,3.5;1,1;transparent_button.png;dinv_main_inventory_up;Up]'
mod.main_inventory_scroll_down = 'image_button[10.25,6.5;1,1;transparent_button.png;dinv_main_inventory_down;Down]'
mod.recipe_button = 'image_button[6.25,1;1,1;transparent_button.png;dinv_recipe_next;Next]'
mod.worn_items_inv = 'list[current_player;worn;0,3.5;2,4;]'
mod.empty_button = 'image_button[2.25,2;1,1;transparent_button.png;dinv_empty_craft;Empty]'
mod.fill_button = 'image_button[2.25,1;1,1;transparent_button.png;dinv_fill_craft;Fill]'
-- backguard compatibility for older default mod, game pre 5.1 release
if not minetest.global_exists("default.chest") then
default.chest = {}
default.chest.get_chest_formspec = default.get_chest_formspec
minetest.log("warning", "[dinv] using older chest api from default mod")
end
-- All this is necessary to add scroll buttons to chests.
if ADD_SCROLL_TO_DEFAULT_CHEST then
function default.chest.get_chest_formspec(pos, player)
local scroll_to = 8
if player and player.get_player_name then
local player_name = player:get_player_name()
if mod.dat[player_name].scroll_main_to then
scroll_to = mod.dat[player_name].scroll_main_to
end
end
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
local formspec =
'size[9,9]' ..
'list[nodemeta:' .. spos .. ';main;0,0.3;8,4;]' ..
'list[current_player;main;0,4.85;8,1;]' ..
'list[current_player;main;0,6.08;8,3;' .. scroll_to .. ']' ..
'listring[nodemeta:' .. spos .. ';main]' ..
'listring[current_player;main]' ..
'image_button[8,6.08;1,1;transparent_button.png;dinv_chest_main_inventory_up;Up]' ..
'image_button[8,8.08;1,1;transparent_button.png;dinv_chest_main_inventory_down;Down]' ..
default.get_hotbar_bg(0,4.85)
return formspec
end
local original_chest_functions = {}
for _, nd in pairs({ 'default:chest', 'default:chest_locked' }) do
original_chest_functions[nd] = {}
original_chest_functions[nd].on_rightclick = minetest.registered_items[nd].on_rightclick
minetest.override_item(nd, {
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if clicker.get_player_name then
local player_name = clicker:get_player_name()
local dat = mod.dat[player_name]
if dat then
dat.chest_opened = pos
end
end
original_chest_functions[nd].on_rightclick(pos, node, clicker, itemstack, pointed_thing)
end
})
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= 'default:chest' then
return
end
if not (player and fields) then
return
end
if not (
fields['dinv_chest_main_inventory_up']
or fields['dinv_chest_main_inventory_down']
) then
return
end
local player_name = player:get_player_name()
local dat = mod.dat[player_name] or {}
local pos = dat.chest_opened
if not pos then
return
end
local pinv = player:get_inventory()
local main_inventory_size = pinv:get_size('main')
if fields['dinv_chest_main_inventory_up'] then
dat['scroll_main_to'] = math.max(8, (dat['scroll_main_to'] or 8) - 16)
minetest.show_formspec(player_name, 'default:chest', default.chest.get_chest_formspec(pos, player))
elseif fields['dinv_chest_main_inventory_down'] then
dat['scroll_main_to'] = (dat['scroll_main_to'] or 8) + 16
if dat['scroll_main_to'] >= main_inventory_size then
dat['scroll_main_to'] = 8
end
minetest.show_formspec(player_name, 'default:chest', default.chest.get_chest_formspec(pos, player))
end
end)
end
function mod.damage_armor(player, damage)
local wear = (damage + 1) * 100
for k, v in worn_items(player) do
local vs = v:get_name()
local it = minetest.registered_items[vs]
if it._dinv_armor then
local ow = v:get_wear()
v:add_wear(wear)
player:get_inventory():set_stack(WORN_INV, k, v)
if ow + wear > 65535 then
mod.set_armor(player)
mod.set_armor_textures(player)
end
end
end
end
function mod.empty_craft(player)
local pinv = player:get_inventory()
local craft_size = pinv:get_size('craft')
for i = 1, craft_size do
local st = pinv:get_stack('craft', i)
if pinv:room_for_item('main', st) then
pinv:add_item('main', st)
pinv:set_stack('craft', i, nil)
else
return
end
end
return true
end
function mod.empty_trash(player)
local pos
if player and player.get_player_name then
local player_name = player:get_player_name()
if mod.dat[player_name].trash_opened then
pos = mod.dat[player_name].trash_opened
end
end
if not pos then
return
end
local meta = minetest.get_meta(pos)
if not meta then
return
end
local inv = meta:get_inventory()
if not inv then
return
end
local size = inv:get_size('main')
for i = 1, size do
--local st = inv:get_stack('main', i)
inv:set_stack('main', i, nil)
end
end
function mod.get_all_craft_recipes(craft_item)
local recipes = {}
for k, v in pairs(minetest.get_all_craft_recipes(craft_item) or {}) do
if v.type ~= 'cooking' then
table.insert(recipes, v)
end
end
if #recipes < 1 then
return
end
return recipes
end
-- Calculate how big the main inventory should be.
function mod.get_main_size_by_bags(player)
local isize = 32
for k, v in worn_items(player) do
local vs = v:get_name()
local it = minetest.registered_items[vs]
if it._dinv_storage_size then
isize = isize + it._dinv_storage_size
end
end
return isize
end
function mod.get_trashcan_formspec(pos, player)
local scroll_to = 0
if player and player.get_player_name then
local player_name = player:get_player_name()
if mod.dat[player_name].scroll_main_to then
scroll_to = mod.dat[player_name].scroll_main_to
end
end
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
local formspec = 'size[9,8]' ..
'list[nodemeta:' .. spos .. ';main;0,0.3;8,2;]' ..
'list[current_player;main;0,4;8,4;' .. scroll_to .. ']' ..
'listring[]' ..
'image_button[3,2.5;2,1;transparent_button.png;dinv_trash_empty;Empty Trash]' ..
'image_button[8,4;1,1;transparent_button.png;dinv_trash_inventory_up;Up]' ..
'image_button[8,6;1,1;transparent_button.png;dinv_trash_inventory_down;Down]'
return formspec
end
-- Any warmth over zero is sufficient at the moment.
function mod.get_warmth(player)
if not player then
return
end
local warmth = 0
for k, v in worn_items(player) do
local vs = v:get_name()
local it = minetest.registered_items[vs]
if it._dinv_warmth then
warmth = warmth + it._dinv_warmth
end
end
return warmth
end
-- Check if there are any stacks in the given inventory,
-- at the given positions. This is important to avoid losing
-- those items when a bag is removed.
function mod.items_at_range(inv, name, i, j)
if not (inv and name and i and j) then
return
end
local a = inv:get_list(name)
if not a then
return
end
for ind = i + 1, j do
if a[ind] and a[ind]:get_name() ~= '' then
return true
end
end
end
-- The main formspec...
function mod.make_inventory_spec_sfinv(this, player, context)
if not player then
return
end
local player_name = player:get_player_name()
if not mod.dat[player_name] then
mod.dat[player_name] = {}
end
local dat = mod.dat[player_name]
local scroll_main_to = dat['scroll_main_to'] or 0
local inventory = ''
inventory = inventory .. mod.worn_items_inv
inventory = inventory .. mod.empty_button
inventory = inventory .. mod.fill_button
inventory = inventory .. mod.main_inventory .. scroll_main_to .. ']'
inventory = inventory .. mod.craft_inventory
inventory = inventory .. 'listring[]'
inventory = inventory .. mod.craft_preview
inventory = inventory .. mod.main_inventory_scroll_up
inventory = inventory .. mod.main_inventory_scroll_down
inventory = inventory .. mod.recipe_grid(player)
inventory = inventory .. mod.recipe_list(player)
local size = 'size[11.25,7.25]'
local show_inv = false
return sfinv.make_formspec(player, context, inventory, show_inv, size)
end
-- Change one or more privileges. Set a privilege value
-- to 0, in the input table, to remove it.
-----------------------------------------------
-- Note that this can remove granted privileges.
-----------------------------------------------
function mod.modify_privs(player, p)
local player_name = player:get_player_name()
local privs = minetest.get_player_privs(player_name) or {}
for k, v in pairs(p) do
if v == 0 then
privs[k] = nil
else
privs[k] = v
end
end
minetest.set_player_privs(player_name, privs)
end
-- Quick table of all wearable items.
-----------------------------------------------------
-- Populating this after the game starts might cause
-- problems, so I do it twice, just in case.
-----------------------------------------------------
mod.wearable = { }
function mod.populate_wearable_table()
for k, v in pairs(minetest.registered_items) do
if v._dinv_armor or v._dinv_storage_size
or v._dinv_on_wear or v._dinv_on_remove
or v._dinv_while_worn then
mod.wearable[k] = true
end
end
end
mod.populate_wearable_table()
minetest.after(0, function()
mod.populate_wearable_table()
end)
function mod.receive_sfinv_fields(self, player, context, fields)
if not (player and fields) then
return
end
if not (
fields['dinv_main_inventory_up']
or fields['dinv_main_inventory_down']
or fields['dinv_recipe_list']
or fields['dinv_recipe_next']
or fields['dinv_empty_craft']
or fields['dinv_fill_craft']
) then
return
end
local pinv = player:get_inventory()
local main_inventory_size = pinv:get_size('main')
if fields and fields['dinv_main_inventory_up'] then
mod.scroll_main(player, -16, main_inventory_size)
elseif fields and fields['dinv_main_inventory_down'] then
mod.scroll_main(player, 16, main_inventory_size)
elseif fields and fields['dinv_recipe_list'] then
mod.show_recipe(player, fields['dinv_recipe_list'])
elseif fields and fields['dinv_recipe_next'] then
mod.switch_recipe(player, 1)
elseif fields and fields['dinv_empty_craft'] then
mod.empty_craft(player)
elseif fields and fields['dinv_fill_craft'] then
mod.recipe_fill(player)
end
end
function mod.recipe_fill(player)
local player_name = player:get_player_name()
local dat = mod.dat[player_name]
local craft_item_number = dat['craft_item_number']
local recipe_number = dat['recipe_number'] or 1
if not craft_item_number then
return
end
local craft_item = sorted_items[craft_item_number]
if not craft_item then
return
end
local recipes = mod.get_all_craft_recipes(craft_item)
if not recipes then
return
end
local pinv = player:get_inventory()
local main_size = pinv:get_size('main')
if recipe_number > #recipes then
recipe_number = 1
dat['recipe_number'] = recipe_number
end
local recipe = recipes[recipe_number]
local w = recipe.width or 3
if w == 0 or w > 3 then
w = 3
end
local items = {}
local totals = {}
local g_s = {}
local i = 0
for y = 0, 2 do
for x = 1, w do
i = i + 1
local item, group
if recipe.type == 'normal' then
item = recipe.items[(y * w) + x]
end
if item then
items[i] = item
totals[item] = (totals[item] or 0) + 1
end
end
end
local avail = {}
local groups = {}
local stack_max = {}
local only_one
local max = 999999
for k, v in pairs(totals) do
if k:find('^group:') then
local group = k:gsub('^group:', '')
for i = 1, main_size do
local st = pinv:get_stack('main', i)
local name = st:get_name()
--[[
print(name)
if name == 'bones:bones' then
pinv:set_stack('main', i, nil)
end
--]]
local it = minetest.registered_items[name]
if not it then
--print(mod_name..': Cannot find item: ' .. name)
return
end
if it.groups and it.groups[group] then
local ct = st:get_count()
avail[k] = (avail[k] or 0) + ct
groups[k] = (groups[k] or {})
groups[k][name] = (groups[k][name] or 0) + ct
stack_max[name] = it.stack_max or 99
end
end
local gmax
for l, w in pairs(groups[k] or {}) do
if not gmax then
gmax = w / v
end
gmax = math.min(math.max(gmax, w / v), stack_max[l])
end
if gmax then
max = math.min(max, gmax)
end
else
for i = 1, main_size do
local st = pinv:get_stack('main', i)
local name = st:get_name()
if k == name then
avail[k] = (avail[k] or 0) + st:get_count()
local it = minetest.registered_items[k]
if not it then
--print(mod_name..': Cannot find item: ' .. name)
return
end
stack_max[name] = it.stack_max or 99
if stack_max[name] < 2 then
only_one = true
end
end
end
if max and avail[k] and stack_max[k] then
max = math.min(math.min(max, avail[k] / v), stack_max[k])
end
end
end
for k, v in pairs(totals) do
if not (avail[k] and avail[k] >= v) then
--print(mod_name..': Cannot find ' .. k .. ' in inventory.')
return
end
end
if only_one then
max = 1
else
max = math.floor(max)
end
if max < 1 then
return
end
if not mod.empty_craft(player) then
--print(mod_name..': Cannot empty craft inventory')
return
end
for k, v in pairs(items) do
if v:find('^group:') then
for l in pairs(groups[v]) do
local w = groups[v][l]
if w >= max then
local st = pinv:remove_item('main', l .. ' ' .. max)
groups[v][l] = groups[v][l] - st:get_count()
pinv:set_stack('craft', k, st)
break
end
end
else
local st = pinv:remove_item('main', v .. ' ' .. max)
pinv:set_stack('craft', k, st)
end
end
end
-- Return a 3 x 3 grid of images matching the selected recipe.
-- This appears over then actual craft grid, but won't interfere
-- with it.
function mod.recipe_grid(player)
local player_name = player:get_player_name()
local dat = mod.dat[player_name]
local craft_item_number = dat['craft_item_number']
local recipe_number = dat['recipe_number'] or 1
if not craft_item_number then
return ''
end
local craft_item = sorted_items[craft_item_number]
if not craft_item then
return ''
end
local recipes = mod.get_all_craft_recipes(craft_item)
if not recipes then
return ''
end
if recipe_number > #recipes then
recipe_number = 1
dat['recipe_number'] = recipe_number
end
local recipe = recipes[recipe_number]
local inv = ''
inv = inv .. 'container[3.25,0]'
do
local tooltip = craft_item
inv = inv .. 'item_image[3,2;1,1;' .. craft_item .. ']'
tooltip = minetest.registered_items[craft_item].description or tooltip
inv = inv .. 'tooltip[3,2;0.8,0.8;' .. 'recipe: ' .. tooltip .. ']'
end
local w = recipe.width or 3
if w == 0 or w > 3 then
w = 3
end
local g_s = {}
local i = 0
for y = 0, 2 do
for x = 1, w do
i = i + 1
local item, group
if recipe.type == 'normal' then
item = recipe.items[(y * w) + x]
end
local tooltip = item
if item and item:find('^group') then
--print(item)
item = group_rep[item]
if item then
g_s[i] = true
end
elseif item then
tooltip = minetest.registered_items[item].description or tooltip
end
if item then
inv = inv .. 'item_image[' .. (x - 1) .. ',' .. (y) .. ';1,1;' .. item .. ']'
inv = inv .. 'tooltip[' .. (x - 1) .. ',' .. (y) .. ';0.8,0.8;' .. 'recipe: ' .. tooltip .. ']'
end
end
end
for i = 1, 9 do
if g_s[i] then
local x = (i - 1) % w
local y = math.floor((i - 1) / w)
inv = inv .. 'image[' .. (x - 0) .. ',' .. (y) .. ';1,1;big_g.png]'
end
end
inv = inv .. 'container_end[]'
if #recipes > 1 then
inv = inv .. mod.recipe_button
end
return inv
end
-- Return a listbox filled with items that can be crafted.
function mod.recipe_list(player)
if not sorted_items then
sorted_items = {}
for k, v in pairs(minetest.registered_items) do
if k and k ~= '' and mod.get_all_craft_recipes(k) then
local recs = mod.get_all_craft_recipes(k)
for a, b in pairs(recs) do
if b.type ~= 'cooking' then
table.insert(sorted_items, k)
break
end
end
end
end
table.sort(sorted_items)
end
local inv = ''
inv = inv .. 'textlist[7.25,0;3.75,2.9;dinv_recipe_list;'
local t
for k, v in pairs(sorted_items) do
if t then
inv = inv .. ','
end
inv = inv .. v
--print(v)
t = true
end
inv = inv .. ';'
return inv
end
-- Recreate the inventory formspec when the player scrolls
-- up or down in the main inventory.
function mod.scroll_main(player, amount, max)
if not (player and amount) then
return
end
local player_name = player:get_player_name()
if not player_name then
return
end
local scroll = mod.dat[player_name].scroll_main_to or 0
if scroll and scroll % 8 == 0 then
scroll = (scroll + amount)
if scroll > max - 1 or scroll < 0 then
scroll = 0
end
mod.dat[player_name].scroll_main_to = scroll
sfinv.set_player_inventory_formspec(player)
end
end
function mod.scroll_trash(player, amount, max)
if not (player and amount) then
return
end
local player_name = player:get_player_name()
if not player_name then
return
end
local dat = mod.dat[player_name] or {}
local pos = dat.trash_opened
if not pos then
return
end
local scroll = mod.dat[player_name].scroll_main_to or 0
if scroll and scroll % 8 == 0 then
scroll = (scroll + amount)
if scroll > max - 1 or scroll < 0 then
scroll = 0
end
mod.dat[player_name].scroll_main_to = scroll
minetest.show_formspec(player_name, mod_name..':trashcan', mod.get_trashcan_formspec(pos, player))
end
end
function mod.set_armor(player)
if not player then
return
end
local armor = 100
for k, v in worn_items(player) do
local vs = v:get_name()
local it = minetest.registered_items[vs]
if it._dinv_armor then
armor = armor * it._dinv_armor
end
end
local player_name = player:get_player_name()
if player_name then
minetest.chat_send_player(player_name, 'Your armor: ' .. armor)
end
local armor_g = player:get_armor_groups()
if not (armor_g and armor_g.fleshy) then
return
end
armor_g.fleshy = armor
player:set_armor_groups(armor_g)
end
-----------------------------------------------
-- Todo: Handle different character textures.
-----------------------------------------------
function mod.set_armor_textures(player)
local prop = player:get_properties()
local textures = prop.textures
--local tex = (textures and textures[1] or 'character.png')
local tex = 'character.png'
local pile = {}
for k, v in worn_items(player) do
local vs = v:get_name()
local it = minetest.registered_items[vs]
if it._dinv_location and it._dinv_texture then
pile[it._dinv_location] = it._dinv_texture
end
end
for _, loc in pairs({ 'body', 'feet', 'head', 'arm', 'back' }) do
if pile[loc] then
tex = tex .. '^' .. pile[loc]
end
end
textures = { tex }
if minetest.get_modpath("player_api") then
player_api.set_textures(player, textures)
else
default.player_set_textures(player, textures)
end
end
-- Set the size of the main inventory.
function mod.set_main_size_by_bags(player)
if not player then
return
end
local pinv = player:get_inventory()
if not pinv then
return
end
local prsize = pinv:get_size('main')
local isize = mod.get_main_size_by_bags(player)
if PROTECT_INVENTORY and isize < prsize then
print(mod_name..': *** Preventing lost inventory from reducing bag sizes')
isize = prsize
end
if not pinv:set_size('main', isize) then
print(mod_name..': *** ERROR setting inventory size.')
end
return isize
end
-- Recreate the inventory formspec to show a recipe given
-- by the return value from the recipe list.
function mod.show_recipe(player, field)
if not (player and field) then
return
end
local player_name = player:get_player_name()
if not player_name then
return
end
local t = minetest.explode_textlist_event(field)
if t.type == 'CHG' and t.index then
mod.dat[player_name].craft_item_number = tonumber(t.index)
mod.dat[player_name].recipe_number = 1
sfinv.set_player_inventory_formspec(player)
end
end
-- Recreate the inventory formspec to show a different recipe
-- for the current item (if it has more than one).
function mod.switch_recipe(player, amount)
if not (player and amount) then
return
end
local player_name = player:get_player_name()
if not player_name then
return
end
local dat = mod.dat[player_name]
if not dat.recipe_number then
dat.recipe_number = 1
end
dat.recipe_number = dat.recipe_number + amount
if dat.recipe_number < 1 then
dat.recipe_number = 1
end
sfinv.set_player_inventory_formspec(player)
end
function mod.trashcan_can_dig(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
return inv:is_empty('main')
end
function mod.trashcan_construct(pos)
local meta = minetest.get_meta(pos)
meta:set_string('infotext', 'Trashcan')
local inv = meta:get_inventory()
inv:set_size('main', 8*2)
end
function mod.trashcan_rightclick(pos, node, player, itemstack, pointed_thing)
local player_name
if player and player.get_player_name then
player_name = player:get_player_name()
end
if not player_name then
return
end
local dat = mod.dat[player_name]
if dat then
dat.trash_opened = pos
end
minetest.show_formspec(player_name, mod_name..':trashcan', mod.get_trashcan_formspec(pos, player))
end
-- Return true if there's an item at that body location.
function mod.wearing_on_location(player, loc)
for k, v in worn_items(player) do
local vs = v:get_name()
local it = minetest.registered_items[vs]
if it._dinv_location == loc then
return true
end
end
end
-- Check for BAD THINGS if a player moves/adds/removes this item.
minetest.register_allow_player_inventory_action(function(player, action, inventory, inventory_info)
if not (player and action and inventory and inventory_info) then
return
end
if not (inventory_info.from_list == WORN_INV or inventory_info.to_list == WORN_INV or inventory_info.listname == WORN_INV) then
return
end
local item_from, item_from_s
if action == 'move' and inventory_info.from_list then
item_from = inventory:get_stack(inventory_info.from_list, inventory_info.from_index)
else
item_from = inventory_info.stack
end
if item_from and item_from.get_name then
item_from_s = item_from:get_name()
end
local item_to, item_to_s
if action == 'move' and inventory_info.to_list then
item_to = inventory:get_stack(inventory_info.to_list, inventory_info.to_index)
end
if item_to and item_to.get_name then
item_to_s = item_to:get_name()
end
if action == 'move' then
if not mod.wearable[item_from_s] then
if inventory_info.to_list == WORN_INV then
return 0
else
return
end
end
if item_to_s and item_to_s ~= '' then
return 0
end
if inventory_info.to_list == inventory_info.from_list then
return
end
local item_from_it = minetest.registered_items[item_from_s]
if item_from_it._dinv_location and inventory_info.to_list == WORN_INV
and mod.wearing_on_location(player, item_from_it._dinv_location) then
return 0
end
if item_from_it._dinv_storage_size then
local prsize = mod.get_main_size_by_bags(player)
local isize = prsize
if inventory_info.to_list == WORN_INV then
isize = isize + item_from_it._dinv_storage_size
elseif inventory_info.from_list == WORN_INV then
isize = isize - item_from_it._dinv_storage_size
end
if isize < prsize and mod.items_at_range(inventory, 'main', isize, prsize) then
return 0
elseif inventory_info.to_index > isize then
return 0
end
end
elseif action == 'take' and mod.wearable[item_from_s] then
return 0
elseif action == 'put' then
return 0
end
end)
-- Damage items that can only be worn for a limited time.
local last_wear_check = 0
minetest.register_globalstep(function(dtime)
local time = minetest.get_gametime()
if type(time) ~= 'number' then
return
end
if time - last_wear_check < 5 then
return
end
local players = minetest.get_connected_players()
for i = 1, #players do
local player = players[i]
for k, v in worn_items(player) do
local vs = v:get_name()
local it = minetest.registered_items[vs]
if it._dinv_wears_out then
local ow = v:get_wear()
local wear = it._dinv_wears_out
v:add_wear(wear)
player:get_inventory():set_stack(WORN_INV, k, v)
if ow + wear > 65535 then
if it._dinv_on_remove then
it._dinv_on_remove(player)
end
--[[
mod.set_armor(player)
mod.set_armor_textures(player)
--]]
end
if it._dinv_while_worn then
it._dinv_while_worn(player)
end
end
end
end
last_wear_check = minetest.get_gametime()
end)
minetest.register_on_joinplayer(function(player)
local player_name = player:get_player_name()
--[[
if not mod.dat[player_name] then
mod.dat[player_name] = {}
end
--]]
local pinv = player:get_inventory()
pinv:set_size(WORN_INV, 8)
mod.set_main_size_by_bags(player)
mod.set_armor(player)
mod.set_armor_textures(player)
end)
--sfinv.register_page(mod_name..':inventory', {
sfinv.override_page('sfinv:crafting', {
title = 'Inventory',
get = mod.make_inventory_spec_sfinv,
on_player_receive_fields = mod.receive_sfinv_fields,
})
-- Handle inventory moves (change armor, etc.).
minetest.register_on_player_inventory_action(function(player, action, inventory, inventory_info)
if not (player and action and inventory and inventory_info) then
return
end
if not (inventory_info.from_list == WORN_INV or inventory_info.to_list == WORN_INV or inventory_info.listname == WORN_INV) then
return
end
local item
if action == 'move' then
if not (inventory_info.to_list and inventory_info.to_index) then
return
end
item = inventory:get_stack(inventory_info.to_list, inventory_info.to_index)
else
item = inventory_info.stack
end
if not item then
return
end
local item_s = item:get_name()
if not mod.wearable[item_s] then
return
end
mod.set_main_size_by_bags(player)
mod.set_armor(player)
mod.set_armor_textures(player)
local it = minetest.registered_items[item_s]
if inventory_info.to_list == inventory_info.from_list then
-- nop
elseif it._dinv_on_wear and inventory_info.to_list == WORN_INV then
it._dinv_on_wear(player)
elseif it._dinv_on_remove and inventory_info.from_list == WORN_INV then
it._dinv_on_remove(player)
end
end)
-- Get input from the trash formspec buttons.
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= mod_name..':trashcan' then
return
end
if not (player and fields) then
return
end
if not (
fields['dinv_trash_inventory_up']
or fields['dinv_trash_inventory_down']
or fields['dinv_trash_empty']
) then
return
end
local pinv = player:get_inventory()
local main_inventory_size = pinv:get_size('main')
if fields and fields['dinv_trash_inventory_up'] then
mod.scroll_trash(player, -16, main_inventory_size)
elseif fields and fields['dinv_trash_inventory_down'] then
mod.scroll_trash(player, 16, main_inventory_size)
elseif fields and fields['dinv_trash_empty'] then
mod.empty_trash(player)
end
end)
-- Every time the player gets punched, armor is damaged,
-- even if no damage is done to the player.
minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
mod.damage_armor(player, damage)
end)
if not minetest.get_modpath('bones') then
print(mod_name..': Player gear will suffer when the player dies.')
minetest.register_on_dieplayer(function(player, reason)
if not player then
return
end
local pinv = player:get_inventory()
if not pinv then
return
end
local wear = 20000
local main_size = pinv:get_size('main')
for i = 1, main_size do
local st = pinv:get_stack('main', i)
local name = st:get_name()
if minetest.registered_tools[name] then
local ow = st:get_wear()
st:add_wear(wear)
pinv:set_stack('main', i, st)
end
end
mod.damage_armor(player, 199)
mod.set_armor(player)
mod.set_armor_textures(player)
end)
end