Implement anvil mode for tool component replacement and repair

This commit is contained in:
Evert 2018-04-06 13:22:15 +03:00
parent 1a1bf721b6
commit aba2598e32
No known key found for this signature in database
GPG Key ID: 1688DA83D222D0B5
6 changed files with 279 additions and 52 deletions

@ -1,3 +1,6 @@
-- Fluidity for Minetest 0.5.0+
-- Copyright (c) 2018 Evert "Diamond" Prants <evert@lunasqu.ee>
fluidity = rawget(_G, "fluidity") or {} fluidity = rawget(_G, "fluidity") or {}
local mpath = minetest.get_modpath("fluidity") local mpath = minetest.get_modpath("fluidity")

@ -1,7 +1,5 @@
-- Casts molten metals into a solid form -- Casts molten metals into a solid form
local have_ui = minetest.get_modpath("unified_inventory")
metal_caster = {} metal_caster = {}
metal_caster.max_coolant = 8000 metal_caster.max_coolant = 8000
@ -11,9 +9,9 @@ metal_caster.max_metal = 16000
metal_caster.spec = metal_melter.spec metal_caster.spec = metal_melter.spec
metal_caster.casts = { metal_caster.casts = {
ingot = {description = "Ingot", result = "ingot", cost = metal_caster.spec.ingot, typenames = {"ingot"}}, ingot = {description = "Ingot", result = "ingot", cost = 2, typenames = {"ingot"}},
lump = {description = "Lump", result = "lump", cost = metal_caster.spec.lump, typenames = {"lump"}}, lump = {description = "Lump", result = "lump", cost = 2, typenames = {"lump"}},
gem = {description = "Gem", result = "crystal", cost = metal_caster.spec.crystal, typenames = {"crystal", "gem"}} gem = {description = "Gem", result = "crystal", cost = 2, typenames = {"crystal", "gem"}}
} }
local metal_cache = {} local metal_cache = {}
@ -491,33 +489,3 @@ minetest.register_node("metal_melter:metal_caster", {
for i,v in pairs(metal_caster.casts) do for i,v in pairs(metal_caster.casts) do
metal_caster.register_cast(i, v) metal_caster.register_cast(i, v)
end 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

@ -1,4 +1,5 @@
-- Melterns mod -- Metal Melter for Minetest 0.5.0+
-- Copyright (c) 2018 Evert "Diamond" Prants <evert@lunasqu.ee>
local modpath = minetest.get_modpath("metal_melter") local modpath = minetest.get_modpath("metal_melter")
metal_melter = {} metal_melter = {}

@ -61,7 +61,6 @@ local function find_material_match(type, stack)
return match return match
end end
local function get_output(inv) local function get_output(inv)
local pattern = inv:get_stack("pattern", 1):get_name() local pattern = inv:get_stack("pattern", 1):get_name()
local find_pattern = get_template_group(minetest.registered_items[pattern].groups) local find_pattern = get_template_group(minetest.registered_items[pattern].groups)

@ -59,21 +59,33 @@ function tool_station.get_types(list, tool_type)
local result = {} local result = {}
local items_required = {} local items_required = {}
local components = {}
for _,stack in pairs(list) do for _,stack in pairs(list) do
if not result then break end
local stack_name = stack:get_name() local stack_name = stack:get_name()
for tt, ty in pairs(tool.components) do 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 local in_grp = minetest.get_item_group(stack_name, "tc_"..ty) > 0
if in_grp then if in_grp then
local mtg = get_metalgroup(minetest.registered_items[stack_name].groups) if components[tt] == nil then
if mtg ~= nil then local mtg = get_metalgroup(minetest.registered_items[stack_name].groups)
result[tt] = mtg if mtg ~= nil then
result[tt] = mtg
if not items_required[stack_name] then
items_required[stack_name] = 0 if not items_required[stack_name] then
end 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 end
end end
@ -82,14 +94,125 @@ function tool_station.get_types(list, tool_type)
return result, items_required return result, items_required
end 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 function handle_take_output(pos, listname)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
local tooltype = meta:get_string("tool_type") local tooltype = meta:get_string("tool_type")
local list = inv:get_list(listname)
if tooltype ~= "" then if tooltype ~= "" then
local list = inv:get_list(listname)
local types, items = tool_station.get_types(list, tooltype) local types, items = tool_station.get_types(list, tooltype)
if not types then return end if not types then return end
local res = {} local res = {}
@ -114,6 +237,71 @@ local function handle_take_output(pos, listname)
end end
inv:set_list(listname, list) 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
end end
@ -126,9 +314,9 @@ local function on_timer(pos, elapsed)
-- Get selected tool type -- Get selected tool type
local tool_type = meta:get_string("tool_type") local tool_type = meta:get_string("tool_type")
local list = inv:get_list("input")
if tool_type ~= "" then if tool_type ~= "" then
local list = inv:get_list("input")
local results = tool_station.get_types(list, tool_type) local results = tool_station.get_types(list, tool_type)
if results then if results then
-- Attempt to create the tool with the provided materials -- Attempt to create the tool with the provided materials
@ -137,6 +325,74 @@ local function on_timer(pos, elapsed)
output = tool_res output = tool_res
end end
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 end
if output then if output then

@ -304,18 +304,18 @@ function tinkering.tool_definition(tool_type, materials)
local tool_tree = { local tool_tree = {
description = name, description = name,
tool_capabilities = capabilities, 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) inventory_image = tinkering.compose_tool_texture(tool_type, materials.main, materials.rod)
} }
-- Store materials to use in metadata -- Store materials to use in metadata
local tink_mats = "" local tink_mats = ""
local i = 1 local i = 1
for _, m in pairs(materials) do for name, mat in pairs(materials) do
if i == 1 then if i == 1 then
tink_mats = m tink_mats = name.."="..mat
else else
tink_mats = tink_mats..","..m tink_mats = tink_mats..","..name.."="..mat
end end
i = i + 1 i = i + 1
end end
@ -405,7 +405,7 @@ local num_tools = 0
-- Create base tools -- Create base tools
for m, s in pairs(tinkering.materials) do for m, s in pairs(tinkering.materials) do
for t,_ in pairs(tinkering.tools) 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 num_tools = num_tools + 1
end end
end end