af5375cf0c
This is necessary when the last step is placing a tool or other item with wear, as the precheck apparently doesn't check wear.
243 lines
6.9 KiB
Lua
243 lines
6.9 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local ItemStack, error, ipairs, math, minetest, nodecore, pairs, type
|
|
= ItemStack, error, ipairs, math, minetest, nodecore, pairs, type
|
|
local math_random
|
|
= math.random
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
local function addgroups(sum, pos)
|
|
local node = minetest.get_node(pos)
|
|
local def = minetest.registered_items[node.name] or {}
|
|
if not def.groups then return end
|
|
for k, v in pairs(def.groups) do
|
|
sum[k] = (sum[k] or 0) + v
|
|
end
|
|
end
|
|
|
|
local function craftcheck(recipe, pos, node, data, xx, xz, zx, zz)
|
|
local function rel(x, y, z)
|
|
return {
|
|
x = pos.x + xx * x + zx * z,
|
|
y = pos.y + y,
|
|
z = pos.z + xz * x + zz * z
|
|
}
|
|
end
|
|
data.rel = rel
|
|
data.wield = ItemStack(data.wield or data.crafter and data.crafter:get_wielded_item())
|
|
if recipe.check and not recipe.check(pos, data) then return end
|
|
if recipe.wield and (not data.wield or not nodecore.match(
|
|
{stack = data.wield}, recipe.wield)) then return end
|
|
if recipe.normal then
|
|
if data.pointed.type ~= "node" or
|
|
recipe.normal.y ~= data.pointed.above.y - data.pointed.under.y then return end
|
|
local rx = recipe.normal.x * xx + recipe.normal.z * zx
|
|
if rx ~= data.pointed.above.x - data.pointed.under.x then return end
|
|
local rz = recipe.normal.x * xz + recipe.normal.z * zz
|
|
if rz ~= data.pointed.above.z - data.pointed.under.z then return end
|
|
end
|
|
for _, v in pairs(recipe.nodes) do
|
|
if v.match then
|
|
local p = rel(v.x, v.y, v.z)
|
|
if not nodecore.match(p, v.match) then return end
|
|
end
|
|
end
|
|
if recipe.touchgroups then
|
|
local sum = {}
|
|
addgroups(sum, rel(1, 0, 0))
|
|
addgroups(sum, rel(-1, 0, 0))
|
|
addgroups(sum, rel(0, 1, 0))
|
|
addgroups(sum, rel(0, -1, 0))
|
|
addgroups(sum, rel(0, 0, 1))
|
|
addgroups(sum, rel(0, 0, -1))
|
|
if data.touchgroupmodify then
|
|
data.touchgroupmodify(sum)
|
|
end
|
|
for k, v in pairs(recipe.touchgroups) do
|
|
local w = sum[k] or 0
|
|
if v > 0 and w < v then return end
|
|
if v <= 0 and w > -v then return end
|
|
end
|
|
end
|
|
local mindur = recipe.duration or 0
|
|
if type(mindur) == "function" then mindur = mindur(recipe, pos, node, data) end
|
|
if recipe.toolgroups then
|
|
local dg = data.toolgroupcaps or (data.wield
|
|
and data.wield:get_tool_capabilities().groupcaps)
|
|
if not dg then return end
|
|
local t
|
|
for gn, lv in pairs(recipe.toolgroups) do
|
|
local gt = dg[gn]
|
|
gt = gt and gt.times
|
|
gt = gt and gt[lv]
|
|
if gt and (gt <= 4) and (not t or t > gt) then t = gt end
|
|
end
|
|
if not t then return end
|
|
mindur = mindur + t
|
|
end
|
|
mindur = mindur / recipe.rate_adjust
|
|
if mindur > 0 then
|
|
if not data.duration then return end
|
|
local dur = data.duration
|
|
if type(dur) == "function" then dur = dur(pos, data) end
|
|
if not dur or dur < mindur then
|
|
if data.inprogress then data.inprogress(pos, data) end
|
|
return false
|
|
end
|
|
end
|
|
return function()
|
|
if data.before then data.before(pos, data) end
|
|
if recipe.before then recipe.before(pos, data) end
|
|
for _, v in ipairs(recipe.nodes) do
|
|
if v.replace and ((not v.chance)
|
|
or (math_random(1, v.chance) == 1)) then
|
|
local p = rel(v.x, v.y, v.z)
|
|
local r = v.replace
|
|
while type(r) == "function" do
|
|
r = r(p, v)
|
|
end
|
|
if r and type(r) == "string" then
|
|
r = {name = r}
|
|
end
|
|
if v.match.excess then
|
|
local s = nodecore.stack_get(p)
|
|
local x = s:get_count() - (v.match.count or 1)
|
|
if x > 0 then
|
|
s:set_count(x)
|
|
nodecore.item_eject(p, s)
|
|
end
|
|
nodecore.stack_set(p, ItemStack(""))
|
|
end
|
|
if r then
|
|
local n = minetest.get_node(p)
|
|
r.param2 = n.param2
|
|
nodecore.set_loud(p, r)
|
|
nodecore.fallcheck(p)
|
|
end
|
|
end
|
|
if v.dig then
|
|
local p = rel(v.x, v.y, v.z)
|
|
minetest.node_dig(p, minetest.get_node(p))
|
|
end
|
|
end
|
|
if recipe.items then
|
|
for _, v in pairs(recipe.items) do
|
|
nodecore.item_eject(rel(v.x or 0, v.y or 0, v.z or 0),
|
|
v.name, v.scatter, v.count, v.velocity)
|
|
end
|
|
end
|
|
if recipe.consumewield then
|
|
nodecore.consume_wield(data.crafter, recipe.consumewield)
|
|
elseif recipe.toolgroups and recipe.toolwear and data.crafter then
|
|
nodecore.wear_wield(data.crafter, recipe.toolgroups, recipe.toolwear)
|
|
end
|
|
if recipe.after then recipe.after(pos, data) end
|
|
if data.after then data.after(pos, data) end
|
|
local discover = {recipe.action, recipe.label, data.discover, recipe.discover}
|
|
nodecore.player_discover(data.crafter, discover, "craft:")
|
|
if data.witness or recipe.witness then nodecore.witness(pos, discover) end
|
|
local pname = data.crafter and data.crafter:get_player_name()
|
|
nodecore.log(pname and "action" or "info", (pname or "unknown")
|
|
.. " completed recipe \"" .. recipe.label .. "\" at " ..
|
|
minetest.pos_to_string(pos) .. " upon " .. node.name)
|
|
end
|
|
end
|
|
|
|
local function tryall(rc, pos, node, data)
|
|
local function go(xx, xz, zx, zz)
|
|
return craftcheck(rc, pos, node, data, xx, xz, zx, zz)
|
|
end
|
|
local r = go(1, 0, 0, 1)
|
|
if r then return r end
|
|
if not rc.norotate then
|
|
r = go(0, -1, 1, 0)
|
|
or go(-1, 0, 0, -1)
|
|
or go(0, 1, -1, 0)
|
|
if r then return r end
|
|
if not rc.nomirror then
|
|
r = go(-1, 0, 0, 1)
|
|
or go(0, 1, 1, 0)
|
|
or go(1, 0, 0, -1)
|
|
or go(0, -1, -1, 0)
|
|
end
|
|
end
|
|
return r
|
|
end
|
|
|
|
local craftidx, rebuildidx = nodecore.item_matching_index(
|
|
nodecore.registered_recipes,
|
|
function(i) return i.indexkeys or {true} end,
|
|
"register_craft",
|
|
true,
|
|
function(n, i) return i.action .. "|" .. n end
|
|
)
|
|
|
|
do
|
|
local oldreg = nodecore.register_craft
|
|
local function rebuildhelper(...)
|
|
rebuildidx()
|
|
return ...
|
|
end
|
|
function nodecore.register_craft(...)
|
|
return rebuildhelper(oldreg(...))
|
|
end
|
|
end
|
|
|
|
local function checkall(pos, node, data, set)
|
|
for _, rc in ipairs(set) do
|
|
if data.action == rc.action
|
|
and nodecore.match(node, rc.root.match) then
|
|
data.recipe = rc
|
|
if data.rootmatch then data.rootmatch(data) end
|
|
local r = tryall(rc, pos, node, data)
|
|
if r == false then return end
|
|
if r then return r end
|
|
end
|
|
end
|
|
end
|
|
|
|
function nodecore.craft_search(pos, node, data)
|
|
if not data or not data.action then
|
|
return error("craft_check without data.action")
|
|
end
|
|
|
|
node.x = pos.x
|
|
node.y = pos.y
|
|
node.z = pos.z
|
|
data.pos = pos
|
|
data.node = node
|
|
|
|
local seen = {}
|
|
if node and node.name then
|
|
local key = data.action .. "|" .. node.name
|
|
local set = craftidx[key]
|
|
if set then
|
|
local found = checkall(pos, node, data, set)
|
|
if found then return found end
|
|
for _, i in pairs(set) do seen[i] = true end
|
|
end
|
|
end
|
|
|
|
local stack = pos and nodecore.stack_get(pos)
|
|
if not stack:is_empty() then
|
|
local key = data.action .. "|" .. stack:get_name()
|
|
local set = craftidx[key]
|
|
if set then
|
|
local unique = {}
|
|
for _, i in ipairs(set) do
|
|
if not seen[i] then unique[#unique + 1] = i end
|
|
end
|
|
if #unique > 0 then
|
|
local found = checkall(pos, node, data, unique)
|
|
if found then return found end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function nodecore.craft_check(...)
|
|
local commit = nodecore.craft_search(...)
|
|
if not commit then return end
|
|
commit()
|
|
return true
|
|
end
|