8bfdc211a3
We were using action log level for a lot of things because by default MT does not seem to capture info logs to stderr. On "production" servers though this makes too much noise and makes it hard to find actual player actions. Servers that want info logging will just have to configure/compile it in.
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 ~= recipe.root and 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
|