From aba2598e3209d2e075fa2b8572f6576e831e01cc Mon Sep 17 00:00:00 2001 From: Evert Date: Fri, 6 Apr 2018 13:22:15 +0300 Subject: [PATCH] Implement anvil mode for tool component replacement and repair --- fluidity/init.lua | 3 + metal_melter/caster.lua | 38 +---- metal_melter/init.lua | 3 +- tinkering/nodes/part_builder.lua | 1 - tinkering/nodes/tool_station.lua | 276 +++++++++++++++++++++++++++++-- tinkering/register.lua | 10 +- 6 files changed, 279 insertions(+), 52 deletions(-) diff --git a/fluidity/init.lua b/fluidity/init.lua index 4c74c55..b8c816b 100644 --- a/fluidity/init.lua +++ b/fluidity/init.lua @@ -1,3 +1,6 @@ +-- Fluidity for Minetest 0.5.0+ +-- Copyright (c) 2018 Evert "Diamond" Prants + fluidity = rawget(_G, "fluidity") or {} local mpath = minetest.get_modpath("fluidity") diff --git a/metal_melter/caster.lua b/metal_melter/caster.lua index 797cca4..b1b222c 100644 --- a/metal_melter/caster.lua +++ b/metal_melter/caster.lua @@ -1,7 +1,5 @@ -- Casts molten metals into a solid form -local have_ui = minetest.get_modpath("unified_inventory") - metal_caster = {} metal_caster.max_coolant = 8000 @@ -11,9 +9,9 @@ metal_caster.max_metal = 16000 metal_caster.spec = metal_melter.spec metal_caster.casts = { - ingot = {description = "Ingot", result = "ingot", cost = metal_caster.spec.ingot, typenames = {"ingot"}}, - lump = {description = "Lump", result = "lump", cost = metal_caster.spec.lump, typenames = {"lump"}}, - gem = {description = "Gem", result = "crystal", cost = metal_caster.spec.crystal, typenames = {"crystal", "gem"}} + ingot = {description = "Ingot", result = "ingot", cost = 2, typenames = {"ingot"}}, + lump = {description = "Lump", result = "lump", cost = 2, typenames = {"lump"}}, + gem = {description = "Gem", result = "crystal", cost = 2, typenames = {"crystal", "gem"}} } local metal_cache = {} @@ -491,33 +489,3 @@ minetest.register_node("metal_melter:metal_caster", { for i,v in pairs(metal_caster.casts) do metal_caster.register_cast(i, v) end - --- Support Unified Inventory, partially -if have_ui then - unified_inventory.register_craft_type("casting", { - description = "Casting", - width = 2, - height = 1, - }) - - unified_inventory.register_craft({ - type = "casting", - output = "metal_melter:ingot_cast", - items = {"fluidity:bucket_gold", "default:steel_ingot"}, - width = 0, - }) - - unified_inventory.register_craft({ - type = "casting", - output = "metal_melter:lump_cast", - items = {"fluidity:bucket_gold", "default:iron_lump"}, - width = 0, - }) - - unified_inventory.register_craft({ - type = "casting", - output = "metal_melter:gem_cast", - items = {"fluidity:bucket_gold", "default:mese_crystal"}, - width = 0, - }) -end diff --git a/metal_melter/init.lua b/metal_melter/init.lua index b9291f3..bce760a 100644 --- a/metal_melter/init.lua +++ b/metal_melter/init.lua @@ -1,4 +1,5 @@ --- Melterns mod +-- Metal Melter for Minetest 0.5.0+ +-- Copyright (c) 2018 Evert "Diamond" Prants local modpath = minetest.get_modpath("metal_melter") metal_melter = {} diff --git a/tinkering/nodes/part_builder.lua b/tinkering/nodes/part_builder.lua index 9247656..26954eb 100644 --- a/tinkering/nodes/part_builder.lua +++ b/tinkering/nodes/part_builder.lua @@ -61,7 +61,6 @@ local function find_material_match(type, stack) return match end - local function get_output(inv) local pattern = inv:get_stack("pattern", 1):get_name() local find_pattern = get_template_group(minetest.registered_items[pattern].groups) diff --git a/tinkering/nodes/tool_station.lua b/tinkering/nodes/tool_station.lua index eefaa0c..e4b25c1 100644 --- a/tinkering/nodes/tool_station.lua +++ b/tinkering/nodes/tool_station.lua @@ -59,21 +59,33 @@ function tool_station.get_types(list, tool_type) local result = {} local items_required = {} + local components = {} for _,stack in pairs(list) do + if not result then break end local stack_name = stack:get_name() for tt, ty in pairs(tool.components) do + if not result then break end local in_grp = minetest.get_item_group(stack_name, "tc_"..ty) > 0 if in_grp then - local mtg = get_metalgroup(minetest.registered_items[stack_name].groups) - if mtg ~= nil then - result[tt] = mtg - - if not items_required[stack_name] then - items_required[stack_name] = 0 - end + if components[tt] == nil then + local mtg = get_metalgroup(minetest.registered_items[stack_name].groups) + if mtg ~= nil then + result[tt] = mtg + + if not items_required[stack_name] then + items_required[stack_name] = 0 + end - items_required[stack_name] = items_required[stack_name] + 1 + items_required[stack_name] = items_required[stack_name] + 1 + components[tt] = true + end + else + -- Don't allow multiple components of the same type to avoid confusion + result = nil + items_required = nil + components = {} + break end end end @@ -82,14 +94,125 @@ function tool_station.get_types(list, tool_type) return result, items_required end +function tool_station.get_tool(list) + local tool_fnd = nil + local tool_type = nil + for _,stack in pairs(list) do + local stack_name = stack:get_name() + if minetest.get_item_group(stack_name, "tinker_tool") > 0 then + if tool_fnd == nil then + for t in pairs(tinkering.tools) do + if minetest.get_item_group(stack_name, "tinker_"..t) > 0 then + tool_type = t + break + end + end + tool_fnd = stack + else + -- Don't allow multiple tools in the repair grid at the same time to avoid confusion + tool_fnd = nil + break + end + end + end + return tool_fnd, tool_type +end + +local function decode_meta(s) + local t = {} + for k, v in string.gmatch(s, "(%w+)=(%w+)") do + t[k] = v + end + return t +end + +local function find_material(stack) + -- Meltables + for metal,list in pairs(metal_melter.melts) do + for type,stacks in pairs(list) do + for _,st in pairs(stacks) do + if st == stack then + return metal, type + end + end + end + end + + -- Grouped + for mat,iv in pairs(tinkering.materials) do + if iv.base == "group" and minetest.get_item_group(stack, iv.default) > 0 then + return mat, "block" + elseif stack == iv.default then + return mat, "ingot" + end + end + + return nil +end + +local function get_materials_in_list(list, skip) + local result = {} + for _,stack in pairs(list) do + local stack_name = stack:get_name() + if stack_name ~= "" and stack_name ~= skip then + local material, type = find_material(stack_name) + if material then + if result[material] then + result[material].count = result[material].count + stack:get_count() + else + result[material] = {stack = stack_name, type = type, count = stack:get_count()} + end + end + end + end + + return result +end + +local function match_materials(list1, materials) + local matches = {} + for name,type in pairs(materials) do + if list1[type] then + matches[type] = list1[type] + end + end + + -- Return nothing if there are materials not suitable + for name in pairs(list1) do + if not matches[name] then + matches = {} + break + end + end + return matches +end + +local function take_from_list(list, item, list2) + for _,stack in pairs(list) do + local stack_name = stack:get_name() + if stack_name == item then + stack:clear() + elseif list2[stack_name] then + if list2[stack_name] > stack:get_count() then + list2[stack_name] = list2[stack_name] - stack:get_count() + stack:clear() + else + stack:set_count(stack:get_count() - list2[stack_name]) + list2[stack_name] = 0 + end + end + end + return list +end + local function handle_take_output(pos, listname) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() local tooltype = meta:get_string("tool_type") + local list = inv:get_list(listname) if tooltype ~= "" then - local list = inv:get_list(listname) local types, items = tool_station.get_types(list, tooltype) if not types then return end local res = {} @@ -114,6 +237,71 @@ local function handle_take_output(pos, listname) end inv:set_list(listname, list) + else + local tool, tool_type_ = tool_station.get_tool(list) + if tool then + local comp_mats = tool:get_meta():get_string("materials") + if comp_mats and comp_mats ~= "" then + local materials = decode_meta(comp_mats) + -- Material list found, now we can start doing repair work or replacing a component + local mat_grid = get_materials_in_list(list, tool:get_name()) + + -- Find components to remove + local for_removal = {} + local removed_types = {} + local repair = true + local tool_comps = tinkering.tools[tool_type_].components + for mat, stat in pairs(mat_grid) do + for name, comp in pairs(tool_comps) do + if stat.type == comp and not removed_types[comp] then + for_removal[stat.stack] = 1 + removed_types[comp] = true + repair = false + end + end + end + + if not repair then + inv:set_list(listname, take_from_list(list, tool:get_name(), for_removal)) + end + + if tool:get_wear() ~= 0 and repair then + local matches = match_materials(mat_grid, materials) + local repair_cap = 0 + for mat, stat in pairs(matches) do + repair_cap = repair_cap + math.min(stat.count, 3) + end + + if repair_cap > 0 then + local _take = 1 + for i = 1, repair_cap do + local tool_wear = 65535 - tool:get_wear() + local repair_cnt = (0.33 * 65535) * i + local new_wear = 65535 - (tool_wear + repair_cnt) + if new_wear > 0 then + _take = _take + 1 + end + end + + local to_take = {} + local exch = _take + + for type, c in pairs(matches) do + if not to_take[c.stack] then to_take[c.stack] = 0 end + if c.count < exch then + to_take[c.stack] = to_take[c.stack] + c.count + exch = exch - 1 + else + to_take[c.stack] = to_take[c.stack] + exch + break + end + end + + inv:set_list(listname, take_from_list(list, tool:get_name(), to_take)) + end + end + end + end end end @@ -126,9 +314,9 @@ local function on_timer(pos, elapsed) -- Get selected tool type local tool_type = meta:get_string("tool_type") + local list = inv:get_list("input") if tool_type ~= "" then - local list = inv:get_list("input") local results = tool_station.get_types(list, tool_type) if results then -- Attempt to create the tool with the provided materials @@ -137,6 +325,74 @@ local function on_timer(pos, elapsed) output = tool_res end end + else + local tool, tool_type_ = tool_station.get_tool(list) + if tool then + local comp_mats = tool:get_meta():get_string("materials") + if comp_mats and comp_mats ~= "" then + local materials = decode_meta(comp_mats) + -- Material list found, now we can start doing repair work or replacing a component + local mat_grid = get_materials_in_list(list, tool:get_name()) + + -- Find components to replace + local comp_repl = {} + local repair = true + local tool_comps = tinkering.tools[tool_type_].components + for mat, stat in pairs(mat_grid) do + if comp_repl == nil then break end + for name, comp in pairs(tool_comps) do + if stat.type == comp then + if comp_repl[name] then + -- Dont allow multiple of the same component to avoid confusion + comp_repl = nil + break + else + comp_repl[name] = mat + end + repair = false + end + end + end + + if not repair and comp_repl then + -- Add non-replacement materials back + for i,v in pairs(materials) do + if not comp_repl[i] then + comp_repl[i] = v + end + end + + local tool_res = tinkering.create_tool(tool_type_, comp_repl, true, nil, {wear = tool:get_wear()}) + if tool_res then + output = tool_res + end + end + + -- Attempt to repair tool with provided items + if tool:get_wear() ~= 0 and repair then + local matches = match_materials(mat_grid, materials) + local repair_cap = 0 + for mat, stat in pairs(matches) do + repair_cap = repair_cap + math.min(stat.count, 3) + end + + if repair_cap > 0 then + local tool_wear = 65535 - tool:get_wear() + local repair_cnt = (0.33 * 65535) * repair_cap + local new_wear = 65535 - (tool_wear + repair_cnt) + + if new_wear < 0 then + new_wear = 0 + end + + local tool_res = tinkering.create_tool(tool_type_, materials, true, nil, {wear = new_wear}) + if tool_res then + output = tool_res + end + end + end + end + end end if output then diff --git a/tinkering/register.lua b/tinkering/register.lua index 37da8a7..020b864 100644 --- a/tinkering/register.lua +++ b/tinkering/register.lua @@ -304,18 +304,18 @@ function tinkering.tool_definition(tool_type, materials) local tool_tree = { description = name, tool_capabilities = capabilities, - groups = {tinker_tool = 1, ["metal_"..materials.main] = 1}, + groups = {tinker_tool = 1, ["mainly_"..materials.main] = 1, ["tinker_"..tool_type] = 1, not_in_creative_inventory = 1}, inventory_image = tinkering.compose_tool_texture(tool_type, materials.main, materials.rod) } -- Store materials to use in metadata local tink_mats = "" local i = 1 - for _, m in pairs(materials) do + for name, mat in pairs(materials) do if i == 1 then - tink_mats = m + tink_mats = name.."="..mat else - tink_mats = tink_mats..","..m + tink_mats = tink_mats..","..name.."="..mat end i = i + 1 end @@ -405,7 +405,7 @@ local num_tools = 0 -- Create base tools for m, s in pairs(tinkering.materials) do for t,_ in pairs(tinkering.tools) do - tinkering.create_tool(t, {main=m,binding="wood",rod="wood"}, false, nil, {groups={not_in_creative_inventory=1}}) + tinkering.create_tool(t, {main=m,binding="wood",rod="wood"}, false, nil) num_tools = num_tools + 1 end end