melterns/tinkering/tool.lua

465 lines
12 KiB
Lua

tinkering.tools = {
pick = {
description = "Pickaxe",
groups = {"cracky"},
fleshy_decrement = 1,
components = {
main = "pickaxe_head",
binding = "tool_binding",
rod = "tool_rod"
},
textures = {
main = "tinkering_pickaxe_head.png",
second = "tinkering_overlay_handle_pickaxe.png",
offset = "1,-1"
}
},
axe = {
description = "Axe",
groups = {"choppy"},
fleshy_increment = 1,
components = {
main = "axe_head",
binding = "tool_binding",
rod = "tool_rod"
},
textures = {
main = "tinkering_axe_head.png",
second = "tinkering_overlay_handle_axe.png",
offset = "1,-3"
}
},
sword = {
description = "Sword",
groups = {"snappy"},
fleshy_decrement = 0,
components = {
main = "sword_blade",
binding = "tool_binding",
rod = "tool_rod"
},
textures = {
main = "tinkering_sword_blade.png",
second = "tinkering_overlay_handle_sword.png",
offset = "0,0"
}
},
shovel = {
description = "Shovel",
groups = {"crumbly"},
fleshy_decrement = 1,
components = {
main = "shovel_head",
binding = "tool_binding",
rod = "tool_rod"
},
textures = {
main = "tinkering_shovel_head.png",
second = "tinkering_overlay_handle_shovel.png",
offset = "3,-3"
}
},
}
tinkering.components = {
pickaxe_head = {description = "%s Pickaxe Head", material_cost = 2, image = tinkering.tools.pick.textures.main},
axe_head = {description = "%s Axe Head", material_cost = 2, image = tinkering.tools.axe.textures.main},
sword_blade = {description = "%s Sword Blade", material_cost = 2, image = tinkering.tools.sword.textures.main},
shovel_head = {description = "%s Shovel Head", material_cost = 2, image = tinkering.tools.shovel.textures.main},
tool_rod = {description = "%s Tool Rod", material_cost = 1, image = "tinkering_tool_rod.png"},
tool_binding = {description = "%s Tool Binding", material_cost = 2, image = "tinkering_tool_binding.png"}
}
-- Create component for material
function tinkering.create_material_component(data)
local desc = data.description
local name = data.name
local mod = data.mod_name or minetest.get_current_modname()
local groups = {tinker_component = 1}
groups["tc_"..data.component] = 1
groups["material_"..data.metal] = 1
minetest.register_craftitem(mod..":"..name, {
description = desc,
groups = groups,
inventory_image = data.image
})
end
-- Register a tool type
--
--data = {
-- description = "Pickaxe", -- Name (description) of the tool
-- groups = {"cracky"}, -- Group caps that apply
-- mod = "tinkering", -- The mod you're registering this tool from
-- fleshy_decrement = 1, -- Amount removed from base damage group "fleshy". Negative value adds.
-- components = {
-- main = "pickaxe_head", -- Name of the primary component
-- binding = "tool_binding", -- Second component
-- rod = "tool_rod" -- Mandatory rod component
-- },
-- textures = {
-- main = "tinkering_pickaxe_head.png", -- Head (main) Texture
-- second = "tinkering_overlay_handle_pickaxe.png", -- Overlay (typically a handle)
-- offset = "1,-1" -- Head's offset on the texture
-- }
--}
--
function tinkering.register_tool_type(name, data)
if not data.mod then
data.mod = minetest.get_current_modname()
end
tinkering.tools[name] = data
end
-- Create groups based on materials
local function apply_modifiers(materials, basegroup, dgroup)
local tags = {}
local groups = {}
local incr = 0.00
local uses = 0
local dmg = {}
-- Apply material modifiers
for m, v in pairs(materials) do
local material = tinkering.materials[v]
local mod = material.modifier
if m ~= "main" then
if mod[m] then
local mp = mod[m]
if mp.increase then
incr = incr + mp.increase
end
if mp.uses then
uses = uses + mp.uses
end
if mp.damage then
for g,mod in pairs(mp.damage) do
if dmg[g] == nil or dmg[g] < mod then
dmg[g] = mod
end
end
end
end
end
-- Apply tags
if mod.tags then
for _,t in pairs(mod.tags) do
if tags[t.name] == nil then
tags[t.name] = t.description
end
end
end
end
-- Apply modified to base groups
for grp, d in pairs(basegroup) do
groups[grp] = d
for id,val in pairs(d.times) do
groups[grp].times[id] = val + (incr / id)
end
groups[grp].uses = d.uses + uses
end
-- Apply damage group modifications
for g,l in pairs(dgroup) do
if dmg[g] == nil or dmg[g] < l then
dmg[g] = l
end
end
return groups, dmg, tags
end
-- Generate a tool texture based on tool type, main material (head) and rod material (handle).
function tinkering.compose_tool_texture(tooltype, main, rod)
local mat_main = tinkering.materials[main]
local mat_rod = tinkering.materials[rod]
local tool_data = tinkering.tools[tooltype].textures
return tinkering.combine_textures(tool_data.main, tool_data.second, mat_main.color, mat_rod.color, tool_data.offset)
end
local function quickcopy(t)
local res = {}
for i, v in pairs(t) do
res[i] = v
end
return res
end
-- Generate tool capabilities based on tool type and materials
function tinkering.get_tool_capabilities(tool_type, materials)
if not materials["main"] or not materials["rod"] then
return nil
end
-- Get main material
local main = tinkering.materials[materials.main]
if not main then return nil end
-- Tool data
local tool_data = tinkering.tools[tool_type]
-- Name of the tool
local name = tool_data.description or "Tool"
-- Group copies
local groups = {}
local dgroups = {}
-- Copy the damage groups
for g,v in pairs(main.modifier.damagegroups) do
-- Decrement/increment damage group if tool wants it
if tool_data[g.."_decrement"] then
dgroups[g] = v - tool_data[g.."_decrement"]
elseif tool_data[g.."_increment"] then
dgroups[g] = v + tool_data[g.."_increment"]
else
dgroups[g] = v
end
end
-- Type specific groups and modifiers
for _,v in pairs(tool_data.groups) do
if main.modifier[v] then
groups[v] = quickcopy(main.modifier[v])
end
end
-- Apply all modifiers
local fg, fd, tags = apply_modifiers(materials, groups, dgroups)
local tool_caps = {
full_punch_interval = 1.0,
max_drop_level = 0,
groupcaps = fg,
damage_groups = fd,
}
-- Construct the name
name = main.name.." "..name
return tool_caps, name, tags
end
-- Replace the itemstack with a broken tool instead of destroying it
local function after_use_handler(itemstack, user, node, digparams)
local wear_add = digparams.wear
local wear_before = itemstack:get_wear()
if wear_before + wear_add < 65536 then
itemstack:add_wear(wear_add)
return itemstack
end
-- TODO: Consider creating an itemdef field for the broken counterpart name
local tool_broken = ItemStack(itemstack:get_name().."_broken")
local tool_broken_meta = tool_broken:get_meta()
local meta = itemstack:get_meta()
tool_broken_meta:from_table(meta:to_table())
local description = meta:get_string("description")
tool_broken_meta:set_string("description_non_broken", description)
tool_broken_meta:set_string("description", description .. "\n" .. minetest.colorize("#BB1111", "Broken"))
tool_broken_meta:set_string("capabilities_non_broken", minetest.serialize(itemstack:get_tool_capabilities()))
itemstack:replace(tool_broken)
meta:set_tool_capabilities({})
return itemstack
end
-- Return tool definition
function tinkering.tool_definition(tool_type, materials)
if not materials["main"] or not materials["rod"] then
return nil
end
local capabilities, name, tags = tinkering.get_tool_capabilities(tool_type, materials)
if not capabilities then return nil end
local tool_tree = {
description = name,
tool_capabilities = capabilities,
groups = {tinker_tool = 1, ["mainly_"..materials.main] = 1, ["tinker_"..tool_type] = 1, not_in_creative_inventory = 1},
after_use = after_use_handler,
_is_broken = false,
inventory_image = tinkering.compose_tool_texture(tool_type, materials.main, materials.rod)
}
return tool_tree, tags
end
-- Compare provided components to the required components of this tool
local function compare_components_required(tool_spec, materials)
local all_match = true
for i, v in pairs(tool_spec) do
if not materials[i] then
all_match = false
end
end
for i, v in pairs(materials) do
if not tool_spec[i] then
all_match = false
end
end
return all_match
end
-- Create a new tool based on parameters specified.
function tinkering.create_tool(tool_type, materials, want_tool, custom_name, overrides)
-- TODO: Add texture as metadata (https://github.com/minetest/minetest/issues/5686)
-- Not a valid tool type
if not tinkering.tools[tool_type] then return nil end
local tool_data = tinkering.tools[tool_type]
-- Check if the components are correct
if not compare_components_required(tool_data.components, materials) then return nil end
-- Get tool definition and other metadata
local tool_def, tags = tinkering.tool_definition(tool_type, materials)
if not tool_def then return nil end
local mod_name = tool_data.mod or "tinkering"
-- Apply overrides
if overrides then
for i, v in pairs(overrides) do
tool_def[i] = v
end
end
-- Use custom name
if custom_name ~= nil and custom_name ~= "" then
tool_def.description = custom_name
end
-- Create internal name
local internal_name = mod_name..":"..materials.main.."_"..tool_type
-- Register base tool if it doesnt exist already
if not minetest.registered_items[internal_name] and minetest.get_current_modname() then
minetest.register_tool(internal_name, tool_def)
local tool_def_broken = table.copy(tool_def)
tool_def_broken.tool_capabilities = nil
tool_def_broken.description = tool_def_broken.description.." (Broken)"
tool_def_broken.after_use = nil
tool_def_broken._is_broken = true
tool_def_broken._unbroken_name = internal_name
minetest.register_tool(internal_name.."_broken", tool_def_broken)
end
if not want_tool then return nil end
-- Store materials to use in metadata
local mat_names = ""
local i = 1
for name, mat in pairs(materials) do
if i == 1 then
mat_names = name.."="..mat
else
mat_names = mat_names..","..name.."="..mat
end
i = i + 1
end
-- Add components to description
local description = tool_def.description
description = description.."\n"
for cmp, mat in pairs(materials) do
local mat = tinkering.materials[mat]
local comp = tool_data.components[cmp]
local desc = tinkering.components[comp].description:format(mat.name)
description = description .. "\n" .. minetest.colorize(mat.color, desc)
end
-- Add tags to description
description = description.."\n"
for _,tag in pairs(tags) do
description = description .. "\n" .. tag
end
-- Create a new tool instance and apply metadata
local tool = ItemStack(internal_name)
local meta = tool:get_meta()
if tool_def["initial_metadata"] then
-- For the mods that add additional fields to tool metadata, e. g. toolranks
meta:from_table(tool_def.initial_metadata)
end
meta:set_string("description", description)
meta:set_string("texture_string", tool_def.inventory_image) -- NOT IMPLEMENTED YET!
meta:set_tool_capabilities(tool_def.tool_capabilities)
meta:set_string("materials", mat_names)
if tool_def["wear"] then
tool:set_wear(tool_def.wear)
end
return tool
end
-- Register new tool material
function tinkering.register_material_tool(material)
for t,_ in pairs(tinkering.tools) do
tinkering.create_tool(t, {main=material,binding="wood",rod="wood"}, false, nil)
end
end
-- Register a new tool component
function tinkering.register_component(name, data)
local mod = data.mod_name or minetest.get_current_modname()
if not tinkering.components[name] then
tinkering.components[name] = data
end
local comp_desc = data.description:sub(4)
-- Register cast
metal_melter.register_melt_value(name, metal_caster.spec.cast)
metal_caster.register_cast(name, {
description = comp_desc,
mod_name = mod,
result = name,
cost = data.material_cost,
typenames = {name}
})
-- Register pattern
tinkering.register_pattern(name, {
description = comp_desc,
cost = data.material_cost,
mod_name = mod
})
-- Register components for all materials
for m, s in pairs(tinkering.materials) do
local component = m.."_"..name
tinkering.create_material_component({
name = component,
component = name,
metal = m,
mod_name = mod,
description = data.description:format(s.name),
image = tinkering.color_filter(data.image, s.color)
})
-- Make all components meltable
fluidity.register_melt(mod..":"..component, m, name)
end
end