-- the math basically needs to be rewritten from scratch by someone who isn't
-- dyscalculic but
local L = sorcery.lib
local M = function(i) return sorcery.itemclass.get(i, 'material') end
sorcery.lathe = {
techs = {
cut = {dmg = true};
intaglio = {
consume = true;
toolpred = function(tool)
if minetest.get_item_group(tool, 'sorcery_powder') == 0 then return false end
local cl = sorcery.itemclass.get(tool, 'metal')
return cl.data.hardness >= 3
end;
validate = function(tool, wkpc)
return M(tool).data.hardness >= M(wkpc).data.hardness
end;
};
};
tools = {
['group:sword'] = 'cut', ['group:knife'] = 'cut', ['group:blade'] = 'cut';
['group:sorcery_intaglio_powder'] = 'intaglio';
};
recipes = {};
register = function(def)
local recipes = sorcery.lathe.recipes
if not recipes[def.input] then recipes[def.input] = {} end
local rs = recipes[def.input][def.tech]
if not rs
then recipes[def.input][def.tech] = { def }
else rs[#rs+1] = def
end
end;
register_metal = function(def)
local parts = sorcery.data.metals[def.metal].parts
local out = ItemStack(def.output)
for _, ty in pairs {'ingot', 'block', 'fragment'} do
local pt = parts[ty]
local ptc = sorcery.itemclass.get(pt, 'metal')
if ptc and ptc.value then
if def.mass <= ptc.value then
local mass
local vfc = ptc.value / def.mass
if math.floor(vfc) ~= vfc then
for i = 1, 50 do
local v = vfc * i
if math.floor(v) == v then
mass = i
vfc = v
break
end
end
else
mass = 1
end
if not mass then
log.err('tried to register a lathe metal recipe for',def.output,'but the mass ratio',vfc,'for part',ty,'has too many digits to the right of the decimal place')
return false
end
sorcery.lathe.register {
input = pt, mass = mass;
tech = def.tech, cost = def.cost;
output = {
name = out:get_name();
count = out:get_count() * vfc;
};
leftover = def.leftover;
}
end
end
end
end;
tooltech = function(tool)
if type(tool) ~= 'string' then tool = tool:get_name() end
local ts = sorcery.lathe.tools
if ts[tool] then return ts[tool] end
for id,t in pairs(ts) do
local q, g = L.str.beginswith(id, 'group:')
if q and minetest.get_item_group(tool, g) ~= 0 then
return t
end
end
for tech, t in pairs(sorcery.lathe.techs) do
if t.toolpred then
if t.toolpred(tool) then return tech end
end
end
return nil
end;
}
local R = sorcery.lathe.recipes
sorcery.lathe.get = function(pos,idx,howmany)
local inv = minetest.get_meta(pos):get_inventory()
local tool = inv:get_stack('tool',1)
local tech = sorcery.lathe.tooltech(tool)
if not tech then return nil end
local wkpc = inv:get_stack('workpiece',1)
local rec = R[wkpc:get_name()][tech][idx]
local outn = ItemStack(rec.output):get_count()
howmany = howmany or math.floor(wkpc:get_count()/(rec.mass or 1))*outn
local ntimes = math.floor(howmany / outn)
local tmat = sorcery.itemclass.get(tool,'material')
local wmat = sorcery.itemclass.get(wkpc,'material')
local dur = 100
local lfac = 1
if tmat then
local dur = tmat.data.durability or dur
lfac = (wmat and wmat.data.level or 1) /
(tmat.data.maxlevel or tmat.data.level or 1)
end
local ch = 65535 / dur
local wear = (ch * rec.cost * ntimes * lfac)
return {
tool = tool, wkpc = wkpc;
cost = rec.cost * ntimes;
wear = wear;
ntimes = ntimes;
tqty = ntimes * (rec.mass or 1), outn = outn;
tech = tech;
rec = rec;
inv = inv;
}
end
sorcery.lathe.update = function(pos)
local inv = minetest.get_meta(pos):get_inventory()
local tool = inv:get_stack('tool',1)
local wkpc = inv:get_stack('workpiece',1)
if tool:is_empty() or wkpc:is_empty() then
if not inv:is_empty('preview') then
for i=1, inv:get_size('preview') do
inv:set_stack('preview',i,ItemStack())
end
end
return
end
local tmat = sorcery.itemclass.get(tool:get_name(),'material')
local wmat = sorcery.itemclass.get(wkpc:get_name(),'material')
-- obey level restrictions. TODO honor Rend
if (wmat and wmat.data.level or 0) > (tmat and (tmat.data.maxlevel or tmat.data.level) or 0) then
return
end
local tech = sorcery.lathe.tooltech(tool)
local rec = R[wkpc:get_name()][tech]
if not rec then
for g,v in pairs(s_wkpc:get_definition().groups) do
local gs = R['group:'..g..'='..tostring(v)]
local gg = R['group:'..g]
rec = (gs and gs[tech]) or (gg and gg[tech])
end
end
tech = sorcery.lathe.techs[tech]
-- fill in the preview slots
local j = 1
for i=1, inv:get_size 'preview' do
local stk = ItemStack()
local os = rec[i] and ItemStack(rec[i].output)
if rec[i] and minetest.registered_items[os:get_name()] and (rec[i].mass == nil or rec[i].mass <= wkpc:get_count()) then
local l = sorcery.lathe.get(pos, i)
local max = l.ntimes
--math.floor(wkpc:get_count() / (rec[i].mass or 1))
if tech.dmg then
local lw = l.wear
while lw + tool:get_wear() > 65535 do
max = max - 1
if max == 0 then break end
lw = sorcery.lathe.get(pos, i, max).wear
end
elseif tech.consume then
max = math.min(max, tool:get_count())
end
if max > 0 then
stk = ItemStack(rec[i].output)
local ct = math.min(stk:get_count() * max, stk:get_stack_max())
ct = ct - (ct % os:get_count())
stk:set_count(ct)
end
end
inv:set_stack('preview',i,stk)
j = j + 1
end
-- make sure remaining slots are clear
for i = j, inv:get_size('preview') do
inv:set_stack('preview',i,ItemStack())
end
end
local box = {
type='fixed';
fixed = {
-0.7, -0.5, -0.3;
0.57, 0.2, 0.3;
}
}
minetest.register_node('sorcery:lathe', {
description = 'Lathe';
drawtype = 'mesh';
mesh = 'sorcery-lathe.obj';
sunlight_propagates = true;
paramtype = 'light';
paramtype2 = 'facedir';
selection_box = box;
collision_box = box;
groups = { cracky = 2; sorcery_tech = 1; attached_node = 1 }; -- 2=liquid
after_dig_node = sorcery.lib.node.purge_only { 'workpiece', 'tool' };
tiles = {
'default_wood.png';
'default_steel_block.png';
'default_bronze_block.png';
};
on_construct = function(pos)
local m = minetest.get_meta(pos)
local i = m:get_inventory()
i:set_size('workpiece', 1);
i:set_size('tool', 1);
i:set_size('preview', 8);
m:set_string('formspec', [[
formspec_version[3] size[10.25,8]
list[context;tool;1.25,1;1,1]
list[context;workpiece;3,1;1,1]
list[context;preview;5.25,0.25;4,2]
list[current_player;main;0.25,3;8,4]
listring[current_player;main] listring[context;workpiece]
listring[current_player;main] listring[context;tool]
listring[current_player;main] listring[context;preview]
listring[current_player;main]
]])
end;
allow_metadata_inventory_move = function() return 0 end;
allow_metadata_inventory_put = function(pos, list, idx, stack, user)
local inv = minetest.get_meta(pos):get_inventory()
if list == 'tool' then
local s_wkpc = inv:get_stack('workpiece', 1)
local tech = sorcery.lathe.tooltech(stack)
if not tech then return 0 end
local vdtr = sorcery.lathe.techs[tech].validate
if tech and (s_wkpc:is_empty()
or (R[s_wkpc:get_name()] ~= nil and
R[s_wkpc:get_name()][tech] ~= nil and
(vdtr == nil or vdtr(stack,s_wkpc) )))
then return stack:get_count() end
for g,v in pairs(s_wkpc:get_definition().groups) do
local gs = R['group:'..g..'='..tostring(v)]
local gg = R['group:'..g]
if (gs and gs[tech])
or (gg and gg[tech]) then
if vdtr == nil or vdtr(stack, s_wkpc) then
return stack:get_count()
end
end
end
elseif list == 'workpiece' then
local s_tool = inv:get_stack('tool', 1)
if R[stack:get_name()] then
if s_tool:is_empty() then return stack:get_count() end
local tech = sorcery.lathe.tooltech(s_tool)
if tech and R[stack:get_name()][tech] then
local vdtr = sorcery.lathe.techs[tech].validate
if vdtr == nil or vdtr(s_tool, stack) then
return stack:get_count()
end
end
end
end
return 0
end;
allow_metadata_inventory_take = function(pos, list, idx, stack, user)
if list == 'preview' then
local l = sorcery.lathe.get(pos,idx,stack:get_count())
if stack:get_count() % l.outn == 0 then
return stack:get_count()
else return 0 end
else return stack:get_count() end
end;
on_metadata_inventory_put = sorcery.lathe.update;
on_metadata_inventory_take = function(pos, list, idx, stack, user)
if list == 'preview' then
local l = sorcery.lathe.get(pos,idx,stack:get_count())
if sorcery.lathe.techs[l.tech].consume then
l.tool:take_item(l.cost)
elseif sorcery.lathe.techs[l.tech].dmg then
l.tool:add_wear(l.wear)
end
l.wkpc:take_item(l.tqty)
l.inv:set_stack('tool', 1, l.tool)
l.inv:set_stack('workpiece', 1, l.wkpc)
if l.rec.leftover then
sorcery.lib.node.insert(ItemStack(l.rec.leftover), 'workpiece', pos, user, l.inv)
end
minetest.sound_play('sorcery_clank', { pos = pos, gain = 0.9 })
end
sorcery.lathe.update(pos)
end;
})
minetest.register_craft {
output = 'sorcery:lathe';
recipe = {
{'default:stick','basic_materials:gear_steel','default:steel_ingot'};
{'default:bronze_ingot','basic_materials:steel_bar','default:bronze_ingot'};
{'group:wood','','group:wood'};
};
}