d872119fd7
- Fix door place-craft discovery not triggering due to wrong label name - Fix many door witnesses not working because the door itself obstructs view of the action; "spread" the witness event to multiple positions and give the player credit if they can observe any part of the interacting nodes.
250 lines
7.0 KiB
Lua
250 lines
7.0 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)
|
|
for _, v in pairs(recipe.nodes) do
|
|
if v.x ~= 0 or v.y ~= 0 or v.z ~= 0 then
|
|
nodecore.witness(rel(v.x, v.y, v.z), discover)
|
|
end
|
|
end
|
|
end
|
|
local pname = data.crafter and data.crafter:get_player_name()
|
|
nodecore.log(pname and "action" or "info", (pname or "unknown")
|
|
.. " crafts \"" .. recipe.label .. "\" at " ..
|
|
minetest.pos_to_string(pos))
|
|
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
|