diff --git a/README.md b/README.md index db613c0..8d9a117 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,14 @@ # dinv -Duane's minetest inventory +Duane's Minetest Inventory + +This is a basic inventory mod with recipe list, bags, and armor. It lacks a lot of the features of similar mods. All worn items, including bags and armor, are combined into one secondary inventory. Equipped bags expand your main inventory, rather than opening up extra inventories. + + +The source is available on github. + +Code: LGPL2 +Textures: strictly CC0 + +Mod dependencies: default + +Download: https://github.com/duane-r/dinv/archive/master.zip diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..0753db2 --- /dev/null +++ b/init.lua @@ -0,0 +1,802 @@ +-- Dinv init.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) + + +mapgen = {} +local mod = mapgen +local mod_name = 'dinv' + +mod.version = '20190720' +mod.path = minetest.get_modpath(minetest.get_current_modname()) +mod.world = minetest.get_worldpath() +mod.dat = {} + + +local worn_inv = 'worn' +local sorted_items + +mod.form_size = 'size[11.25,7.75]' +mod.main_inventory = 'list[current_player;main;0,4;8,4;' +mod.craft_inventory = 'list[current_player;craft;3,0;3,3;]' +mod.craft_preview = 'list[current_player;craftpreview;6,1;1,1;]' +mod.main_inventory_scroll_up = 'image_button[8,4;1,1;transparent_button.png;dinv_main_inventory_up;Up]' +mod.main_inventory_scroll_down = 'image_button[8,7;1,1;transparent_button.png;dinv_main_inventory_down;Down]' +mod.recipe_buttons = 'image_button[3,3;1,1;transparent_button.png;dinv_recipe_back;Back]image_button[5,3;1,1;transparent_button.png;dinv_recipe_fore;Fore]' +mod.worn_items_inv = 'list[current_player;worn;9.25,4;2,4;]' + + +minetest.register_craftitem(mod_name..':bag_small', { + inventory_image = 'bags_small.png', + stack_max = 1, +}) + +minetest.register_craftitem(mod_name..':bag_medium', { + inventory_image = 'bags_medium.png', + stack_max = 1, +}) + +minetest.register_craftitem(mod_name..':bag_large', { + inventory_image = 'bags_large.png', + stack_max = 1, +}) + +minetest.register_tool(mod_name..':leather_armor', { + inventory_image = 'leather_armour1.png', + description = 'Leather Armor', + _dinv_armor = 0.9, + _dinv_location = 'body', +}) + +minetest.register_tool(mod_name..':boots', { + inventory_image = 'boots1_brown.png', + description = 'Sturdy Boots', + _dinv_armor = 0.9, + _dinv_location = 'feet', +}) + +minetest.register_tool(mod_name..':leather_cap', { + inventory_image = 'elven_leather_helm.png', + description = 'Leather Cap', + _dinv_armor = 0.9, + _dinv_location = 'head', +}) + +minetest.register_tool(mod_name..':steel_helmet', { + inventory_image = 'helmet1.png', + description = 'Steel Helmet', + _dinv_armor = 0.8, + _dinv_location = 'head', +}) + +minetest.register_tool(mod_name..':wood_shield', { + inventory_image = 'buckler1.png', + description = 'Wooden Shield', + _dinv_armor = 0.8, + _dinv_location = 'arm', +}) + +minetest.register_tool(mod_name..':steel_shield', { + inventory_image = 'lshield_dd_dk.png', + description = 'Steel Shield', + _dinv_armor = 0.7, + _dinv_location = 'arm', +}) + +minetest.register_tool(mod_name..':chain_armor', { + inventory_image = 'chain_mail1.png', + description = 'Chain Mail', + _dinv_armor = 0.75, + _dinv_location = 'body', +}) + +minetest.register_tool(mod_name..':plate_armor', { + inventory_image = 'plate1.png', + description = 'Plate Mail', + _dinv_armor = 0.6, + _dinv_location = 'body', +}) + +minetest.register_tool(mod_name..':diamond_plate_armor', { + inventory_image = 'crystal_plate2.png', + description = 'Diamond Plate Mail', + _dinv_armor = 0.45, + _dinv_location = 'body', +}) + +--print(dump(minetest.registered_tools[mod_name..':plate_armor'])) + +minetest.register_craft({ + output = mod_name..':bag_small', + recipe = { + {'', 'farming:cotton', ''}, + {'group:wool', 'group:wool', 'group:wool'}, + {'group:wool', 'group:wool', 'group:wool'}, + }, +}) + +minetest.register_craft({ + output = mod_name..':bag_medium', + recipe = { + {'', '', ''}, + {'farming:string', mod_name..':bag_small', 'farming:string'}, + {'', mod_name..':bag_small', ''}, + }, +}) + +minetest.register_craft({ + output = mod_name..':bag_large', + recipe = { + {'', '', ''}, + {'farming:string', mod_name..':bag_medium', 'farming:string'}, + {'', mod_name..':bag_medium', ''}, + }, +}) + +minetest.register_craft({ + output = mod_name..':leather_armor', + recipe = { + {'', 'mobs:leather', ''}, + {'mobs:leather', 'farming:string', 'mobs:leather'}, + {'mobs:leather', 'farming:string', 'mobs:leather'}, + }, +}) + +minetest.register_craft({ + output = mod_name..':leather_cap', + recipe = { + {'', 'mobs:leather', ''}, + {'', 'mobs:leather', ''}, + {'', 'farming:string', ''}, + }, +}) + +minetest.register_craft({ + output = mod_name..':steel_helmet', + recipe = { + {'', 'default:steel_ingot', ''}, + {'default:steel_ingot', 'mobs:leather', 'default:steel_ingot'}, + {'', 'mobs:fur', ''}, + }, +}) + +minetest.register_craft({ + output = mod_name..':boots', + recipe = { + {'', '', ''}, + {'', 'farming:string', ''}, + {'mobs:leather', 'farming:string', 'mobs:leather'}, + }, +}) + +minetest.register_craft({ + output = mod_name..':wood_shield', + recipe = { + {'', 'group:wood', ''}, + {'group:wood', 'mobs:leather', 'group:wood'}, + {'', 'group:wood', ''}, + }, +}) + +minetest.register_craft({ + output = mod_name..':steel_shield', + recipe = { + {'', mod_name..':steel_plate', ''}, + { mod_name..':steel_plate', 'mobs:leather', mod_name..':steel_plate'}, + {'', mod_name..':steel_plate', ''}, + }, +}) + +minetest.register_craftitem(mod_name..':steel_rings', { + inventory_image = 'dinv_steel_rings.png', +}) + +minetest.register_craftitem(mod_name..':steel_plate', { + inventory_image = 'dinv_steel_plate.png', +}) + +minetest.register_craftitem(mod_name..':diamond_plate', { + inventory_image = 'dinv_diamond_plate.png', +}) + +minetest.register_craft({ + output = mod_name..':steel_rings', + recipe = { + {'', '', ''}, + {'default:steel_ingot', 'default:coal_lump', 'default:steel_ingot'}, + {'', 'default:steel_ingot', ''}, + }, +}) + +minetest.register_craft({ + output = mod_name..':steel_plate', + recipe = { + {'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'}, + {'default:steel_ingot', 'default:coal_lump', 'default:steel_ingot'}, + {'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'}, + }, +}) + +minetest.register_craft({ + output = mod_name..':diamond_plate', + recipe = { + {'default:diamond', 'default:diamond', 'default:diamond'}, + {'default:diamond', 'default:mese_crystal', 'default:diamond'}, + {'default:diamond', 'default:diamond', 'default:diamond'}, + }, +}) + +minetest.register_craft({ + output = mod_name..':chain_armor', + recipe = { + {'', mod_name..':steel_rings', ''}, + {mod_name..':steel_rings', 'mobs:leather', mod_name..':steel_rings'}, + {mod_name..':steel_rings', 'mobs:leather', mod_name..':steel_rings'}, + }, +}) + +minetest.register_craft({ + output = mod_name..':plate_armor', + recipe = { + {'', mod_name..':steel_plate', ''}, + {mod_name..':steel_plate', mod_name..':chain_armor', mod_name..':steel_plate'}, + {mod_name..':steel_plate', 'default:steel_ingot', mod_name..':steel_plate'}, + }, +}) + +minetest.register_craft({ + output = mod_name..':diamond_plate_armor', + recipe = { + {'', mod_name..':diamond_plate', ''}, + {mod_name..':diamond_plate', mod_name..':chain_armor', mod_name..':diamond_plate'}, + {mod_name..':diamond_plate', 'default:mese', mod_name..':diamond_plate'}, + }, +}) + + +mod.bag_sizes = { + [mod_name..':bag_small'] = 8, + [mod_name..':bag_medium'] = 16, + [mod_name..':bag_large'] = 24, + [mod_name..':bag_huge'] = 32, + [mod_name..':bag_hole'] = 64, +} + + +mod.wearable = { } +do + for k, v in pairs(mod.bag_sizes) do + mod.wearable[k] = true + end + + for k, v in pairs(minetest.registered_items) do + if v._dinv_armor then + mod.wearable[k] = true + end + end +end + + +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 + + +function mod.make_inventory_spec(player) + if not player then + return + end + + local player_name = player:get_player_name() + local dat = mod.dat[player_name] + local scroll_main_to = dat['scroll_main_to'] or 0 + + local inventory = '' + inventory = inventory .. mod.form_size + inventory = inventory .. mod.main_inventory .. scroll_main_to .. ']' + inventory = inventory .. mod.craft_inventory + inventory = inventory .. 'listring[]' + inventory = inventory .. mod.recipe_buttons + inventory = inventory .. mod.craft_preview + inventory = inventory .. mod.main_inventory_scroll_up + inventory = inventory .. mod.main_inventory_scroll_down + inventory = inventory .. mod.worn_items_inv + inventory = inventory .. mod.recipe_grid(player) + inventory = inventory .. mod.recipe_list(player) + + return inventory +end + + +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 = minetest.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] + --print(dump(recipe)) + + local inv = '' + inv = inv .. 'container[3,0]' + + 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 + if item and item:find('^group') then + --print(item) + item = group_rep[item] + if item then + g_s[i] = true + end + end + if item then + inv = inv .. 'item_image[' .. (x - 1) .. ',' .. (y) .. ';1,1;' .. item .. ']' + end + end + end + inv = inv .. 'container_end[]' + + inv = inv .. 'container[3,0]' + 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[]' + + return inv +end + + +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 minetest.get_all_craft_recipes(k) then + local recs = minetest.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,0;4,3.5;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 + + +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 fs = player:get_inventory_formspec() + --print('.*' .. mod.main_inventory:gsub('%[', '%%[') .. '(%d+)%].*') + local scroll = fs:gsub('.*%' .. mod.main_inventory:gsub('%[', '%%[') .. '(%d+)%].*', '%1') + --print(scroll) + if scroll then + local scroll_n = tonumber(scroll) + if scroll_n and scroll_n % 8 == 0 then + scroll_n = (scroll_n + amount) + if scroll_n > max or scroll_n < 0 then + scroll_n = 0 + end + mod.dat[player_name].scroll_main_to = scroll_n + player:set_inventory_formspec(mod.make_inventory_spec(player)) + end + end +end + + +function mod.get_main_size_by_bags(player) + local isize = 32 + + for k, v in worn_items(player) do + local vs = v:get_name() + if mod.bag_sizes[vs] then + isize = isize + mod.bag_sizes[vs] + end + end + + return isize +end + +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 isize < prsize then + print('*** Preventing lost inventory from reducing bag sizes') + isize = prsize + end + --]] + if not pinv:set_size('main', isize) then + print('*** ERROR setting inventory size.') + end + + return isize +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) + player:set_inventory_formspec(mod.make_inventory_spec(player)) +end) + + +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 + + if field:find('CHG') then + local n = field:gsub('.*CHG:(%d+).*', '%1') + if n then + mod.dat[player_name].craft_item_number = tonumber(n) + mod.dat[player_name].recipe_number = 1 + player:set_inventory_formspec(mod.make_inventory_spec(player)) + end + end +end + + +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 + + --print(dat.recipe_number) + player:set_inventory_formspec(mod.make_inventory_spec(player)) +end + + +minetest.register_on_player_receive_fields(function(player, formname, fields) + 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_back'] then + mod.switch_recipe(player, -1) + elseif fields and fields['dinv_recipe_fore'] then + mod.switch_recipe(player, 1) + end +end) + + +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 + + --print(dump(inventory_info.from_list), dump(inventory_info.to_list)) + --print(dump(item_from_s), dump(item_to_s)) + --print(action) + + 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 mod.bag_sizes[item_from_s] then + local prsize = mod.get_main_size_by_bags(player) + local isize = prsize + if inventory_info.to_list == worn_inv then + isize = isize + mod.bag_sizes[item_from_s] + elseif inventory_info.from_list == worn_inv then + isize = isize - mod.bag_sizes[item_from_s] + 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) + + +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 + + +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] + --print(dump(it)) + if it._dinv_armor then + armor = armor * it._dinv_armor + end + end + + print('armor = ', armor) + + local armor_g = player:get_armor_groups() + if not (armor_g and armor_g.fleshy) then + return + end + + armor_g.fleshy = armor + --print(dump(armor_g)) + player:set_armor_groups(armor_g) +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) + end + end + end +end + + +minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage) + mod.damage_armor(player, damage) +end) + + +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) +end) + + +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 diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..757b93c --- /dev/null +++ b/mod.conf @@ -0,0 +1,2 @@ +name = dinv +depends = default diff --git a/textures/bags_large.png b/textures/bags_large.png new file mode 100644 index 0000000..44cdd4f Binary files /dev/null and b/textures/bags_large.png differ diff --git a/textures/bags_medium.png b/textures/bags_medium.png new file mode 100644 index 0000000..5c17541 Binary files /dev/null and b/textures/bags_medium.png differ diff --git a/textures/bags_small.png b/textures/bags_small.png new file mode 100644 index 0000000..a138f41 Binary files /dev/null and b/textures/bags_small.png differ diff --git a/textures/big_g.png b/textures/big_g.png new file mode 100644 index 0000000..b8fdc45 Binary files /dev/null and b/textures/big_g.png differ diff --git a/textures/boots1_brown.png b/textures/boots1_brown.png new file mode 100644 index 0000000..a60a652 Binary files /dev/null and b/textures/boots1_brown.png differ diff --git a/textures/buckler1.png b/textures/buckler1.png new file mode 100644 index 0000000..ad01d32 Binary files /dev/null and b/textures/buckler1.png differ diff --git a/textures/chain_mail1.png b/textures/chain_mail1.png new file mode 100644 index 0000000..ce4c0cc Binary files /dev/null and b/textures/chain_mail1.png differ diff --git a/textures/crystal_plate2.png b/textures/crystal_plate2.png new file mode 100644 index 0000000..174cbf2 Binary files /dev/null and b/textures/crystal_plate2.png differ diff --git a/textures/dinv_diamond_plate.png b/textures/dinv_diamond_plate.png new file mode 100644 index 0000000..f16c097 Binary files /dev/null and b/textures/dinv_diamond_plate.png differ diff --git a/textures/dinv_steel_plate.png b/textures/dinv_steel_plate.png new file mode 100644 index 0000000..554081c Binary files /dev/null and b/textures/dinv_steel_plate.png differ diff --git a/textures/dinv_steel_rings.png b/textures/dinv_steel_rings.png new file mode 100644 index 0000000..df12b29 Binary files /dev/null and b/textures/dinv_steel_rings.png differ diff --git a/textures/elven_leather_helm.png b/textures/elven_leather_helm.png new file mode 100644 index 0000000..9f6fcaf Binary files /dev/null and b/textures/elven_leather_helm.png differ diff --git a/textures/helmet1.png b/textures/helmet1.png new file mode 100644 index 0000000..a84beec Binary files /dev/null and b/textures/helmet1.png differ diff --git a/textures/leather_armour1.png b/textures/leather_armour1.png new file mode 100644 index 0000000..a63a108 Binary files /dev/null and b/textures/leather_armour1.png differ diff --git a/textures/lshield_dd_dk.png b/textures/lshield_dd_dk.png new file mode 100644 index 0000000..750d361 Binary files /dev/null and b/textures/lshield_dd_dk.png differ diff --git a/textures/plate1.png b/textures/plate1.png new file mode 100644 index 0000000..092c349 Binary files /dev/null and b/textures/plate1.png differ diff --git a/textures/transparent_button.png b/textures/transparent_button.png new file mode 100644 index 0000000..3e439c4 Binary files /dev/null and b/textures/transparent_button.png differ