-- Magicalities Wands magicalities.wands = {} local transform_recipes = { ["mg_table"] = {result = "magicalities:arcane_table", requirements = nil} } local wandcaps = { full_punch_interval = 1.0, max_drop_level = 0, groupcaps = {}, damage_groups = {fleshy = 2}, } local function align(len) local str = "" for i = 1, len do str = str.."\t" end return str end function magicalities.wands.get_wand_focus(stack) local meta = stack:get_meta() if meta:get_string("focus") == "" then return nil end local focus = meta:get_string("focus") local itemdef = minetest.registered_items[focus] if not itemdef then return nil end return focus, itemdef end local function focus_requirements(stack, fdef) if fdef["_wand_requirements"] then return magicalities.wands.wand_has_contents(stack, fdef["_wand_requirements"]) end return true end local function focuses_formspec(available, focusname) local x = 0 local fsp = "" for focus in pairs(available) do if x < 5 then fsp = fsp .. "item_image_button["..x..",2.8;1,1;"..focus..";"..focus..";]" x = x + 1 end end local current = "" if not focusname then current = "label[2,1;No Focus]" else current = "item_image_button[2,0.5;1,1;"..focusname..";remove;Remove]".. "label[0,1.5;Current: "..minetest.registered_items[focusname].description.."]" end return "size[5,3.5]".. default.gui_bg.. default.gui_bg_img.. "label[0,0;Wand Focuses]".. current.. "label[0,2.4;Available]".. fsp end -- Update wand's description function magicalities.wands.update_wand_desc(stack) local meta = stack:get_meta() local data_table = minetest.deserialize(meta:get_string("contents")) local wanddata = minetest.registered_items[stack:get_name()] local description = wanddata.description local capcontents = wanddata["_cap_max"] or 15 local strbld = description.."\n" local longest_desc = 0 for _,data in pairs(magicalities.elements) do if not data.inheritance then local len = #data.description if len > longest_desc then longest_desc = len end end end local elems = {} for elem, amount in pairs(data_table) do local dataelem = magicalities.elements[elem] if amount > 0 then elems[#elems + 1] = minetest.colorize(dataelem.color, dataelem.description.." ").. align(longest_desc * 2 - #dataelem.description).. amount.."/"..capcontents end end local focus, def = magicalities.wands.get_wand_focus(stack) local focusstr = "No Wand Focus" if focus then focusstr = def.description end strbld = strbld .. focusstr if #elems > 0 then table.sort(elems) strbld = strbld .. "\n" .. table.concat(elems, "\n") end meta:set_string("description", strbld) end -- Ensure wand has contents function magicalities.wands.wand_has_contents(stack, requirements) local meta = stack:get_meta() local data_table = minetest.deserialize(meta:get_string("contents")) if not data_table then return false end for name, count in pairs(requirements) do if not data_table[name] or data_table[name] < count then return false end end return true end -- Take wand contents function magicalities.wands.wand_take_contents(stack, to_take) local meta = stack:get_meta() local data_table = minetest.deserialize(meta:get_string("contents")) for name, count in pairs(to_take) do if not data_table[name] or data_table[name] - count < 0 then return nil end data_table[name] = data_table[name] - count end local data_res = minetest.serialize(data_table) meta:set_string("contents", data_res) return stack end -- Add wand contents function magicalities.wands.wand_insert_contents(stack, to_put) local meta = stack:get_meta() local data_table = minetest.deserialize(meta:get_string("contents")) local cap = minetest.registered_items[stack:get_name()]["_cap_max"] local leftover = {} for name, count in pairs(to_put) do if data_table[name] then if data_table[name] + count > cap then data_table[name] = cap leftover[name] = (data_table[name] + count) - cap else data_table[name] = data_table[name] + count end end end local data_res = minetest.serialize(data_table) meta:set_string("contents", data_res) return stack, leftover end -- Can add wand contents function magicalities.wands.wand_insertable_contents(stack, to_put) local meta = stack:get_meta() local data_table = minetest.deserialize(meta:get_string("contents")) local cap = minetest.registered_items[stack:get_name()]["_cap_max"] local insertable = {} for name, count in pairs(to_put) do if data_table[name] then if data_table[name] + count < cap + 1 then insertable[name] = count end end end return insertable end -- Initialize wand metadata local function initialize_wand(stack) local data_table = {} for name, data in pairs(magicalities.elements) do if not data.inheritance then data_table[name] = 0 end end local meta = stack:get_meta() meta:set_string("contents", minetest.serialize(data_table)) end local function wand_action(itemstack, placer, pointed_thing) if not pointed_thing.type == "node" then return itemstack end local node = minetest.get_node(pointed_thing.under) local imeta = itemstack:get_meta() -- Initialize wand metadata if imeta:get_string("contents") == nil or imeta:get_string("contents") == "" then initialize_wand(itemstack) magicalities.wands.update_wand_desc(itemstack) end -- Replacement local to_replace = nil for grp, result in pairs(transform_recipes) do if minetest.get_item_group(node.name, grp) > 0 then to_replace = result break end end -- Call rightclick on the wand focus local focus, fdef = magicalities.wands.get_wand_focus(itemstack) if focus then if fdef["_wand_node"] and focus_requirements(itemstack, fdef) then itemstack = fdef["_wand_node"](pointed_thing.under, node, placer, itemstack, pointed_thing) return itemstack end end -- Call on_rightclick on the node instead if it cannot be replaced if not to_replace then local nodedef = minetest.registered_nodes[node.name] if nodedef.on_rightclick then itemstack = nodedef.on_rightclick(pointed_thing.under, node, placer, itemstack, pointed_thing) end return itemstack end if to_replace.requirements then if not magicalities.wands.wand_has_contents(itemstack, to_replace.requirements) then return itemstack end itemstack = magicalities.wands.wand_take_contents(itemstack, to_replace.requirements) magicalities.wands.update_wand_desc(itemstack) end minetest.swap_node(pointed_thing.under, {name = to_replace.result, param1 = node.param1, param2 = node.param2}) local spec = minetest.registered_nodes[to_replace.result] if spec.on_construct then spec.on_construct(pointed_thing.under) end return itemstack end local function use_wand(itemstack, user, pointed_thing) local imeta = itemstack:get_meta() -- Initialize wand metadata if imeta:get_string("contents") == nil or imeta:get_string("contents") == "" then initialize_wand(itemstack) magicalities.wands.update_wand_desc(itemstack) end -- Call use on the wand focus local focus, fdef = magicalities.wands.get_wand_focus(itemstack) if focus then if fdef["_wand_use"] and focus_requirements(itemstack, fdef) then itemstack = fdef["_wand_use"](itemstack, user, pointed_thing) return itemstack end end magicalities.wands.update_wand_desc(itemstack) return itemstack end local function wand_focuses(itemstack, user, pointed_thing) local focuses_found = {} local inv = user:get_inventory() local list = inv:get_list("main") local focusname, focusdef = magicalities.wands.get_wand_focus(itemstack) local meta = itemstack:get_meta() for _, stack in pairs(list) do if minetest.get_item_group(stack:get_name(), "wand_focus") > 0 then focuses_found[stack:get_name()] = true end end minetest.show_formspec(user:get_player_name(), "magicalities:wand_focuses", focuses_formspec(focuses_found, focusname)) minetest.register_on_player_receive_fields(function (player, formname, fields) if formname ~= "magicalities:wand_focuses" then return false end -- Make sure field is a valid item local f = "" if not fields["quit"] then if fields["remove"] then f = nil else for v in pairs(fields) do if minetest.registered_items[v] then f = v break end end end else return true end local was was = meta:get_string("focus") if was == "" and not f then return true elseif was ~= "" then was = ItemStack(was) if not inv:room_for_item("main", was) then return true end end minetest.close_formspec(player:get_player_name(), "magicalities:wand_focuses") local removed_focus = false local set = false -- Update itemstack for i, stack in pairs(list) do if set and (removed_focus or not f) then break end if not removed_focus and stack:get_name() == f then inv:set_stack("main", i, ItemStack(nil)) removed_focus = true -- Make sure to only remove one end if stack:get_name() == itemstack:get_name() and stack:get_meta() == itemstack:get_meta() and not set then if not f then meta:set_string("focus", "") magicalities.wands.update_wand_desc(itemstack) elseif f ~= "" then meta:set_string("focus", f) magicalities.wands.update_wand_desc(itemstack) end inv:set_stack("main", i, itemstack) set = true end end -- Give the removed focus back if was then inv:add_item("main", was) end return true end) return itemstack end function magicalities.wands.register_wand(name, data) local mod = minetest.get_current_modname() minetest.register_tool(mod..":wand_"..name, { description = data.description, inventory_image = data.image, tool_capabilities = wandcaps, stack_max = 1, _cap_max = data.wand_cap, on_use = use_wand, on_place = wand_action, on_secondary_use = wand_focuses, groups = {wand = 1} }) end