64462f7265
This reverts commit 03f8d32967cc35e418d8ffb46b00f65eaf34e685. Apparently this causes item destruction: when stack nodes are secondarily dug by a rake, their items are not given to the player.
127 lines
3.9 KiB
Lua
127 lines
3.9 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local ipairs, math, minetest, nodecore, pairs, table, vector
|
|
= ipairs, math, minetest, nodecore, pairs, table, vector
|
|
local math_abs, math_max, table_sort
|
|
= math.abs, math.max, table.sort
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
-- To register a tool as a rake, provie a callback:
|
|
-- on_rake(pos, node, user) returns volume, checkfunc
|
|
-- volume: ordered array of relative positions to be dug by rake
|
|
-- checkfunc(pos, node, rel): determine if item can be dug
|
|
-- rel: relative vector taken from volume array
|
|
-- returns true to dig, nil to not dig, false to abort loop
|
|
|
|
local volcache = {}
|
|
function nodecore.rake_volume(dxmax, dymax, dzmax)
|
|
dzmax = dzmax or dxmax
|
|
local key = minetest.pos_to_string({x = dxmax, y = dymax, z = dzmax})
|
|
local rakepos = volcache[key]
|
|
if rakepos then return rakepos end
|
|
rakepos = {}
|
|
for dy = -dymax, dymax do
|
|
for dx = -dxmax, dxmax do
|
|
for dz = -dzmax, dzmax do
|
|
local v = {x = dx, y = dy, z = dz}
|
|
v.d = vector.length(v)
|
|
v.rxz = math_max(math_abs(dx), math_abs(dz))
|
|
v.ry = math_abs(dy)
|
|
rakepos[#rakepos + 1] = v
|
|
end
|
|
end
|
|
end
|
|
table_sort(rakepos, function(a, b) return a.d < b.d end)
|
|
volcache[key] = rakepos
|
|
return rakepos
|
|
end
|
|
|
|
function nodecore.rake_index(filterfunc)
|
|
local rakable = {}
|
|
minetest.after(0, function()
|
|
for k, v in pairs(minetest.registered_nodes) do
|
|
if filterfunc(v, k) then
|
|
rakable[k] = true
|
|
end
|
|
end
|
|
end)
|
|
return function(_, node) return rakable[node.name] end
|
|
end
|
|
|
|
local function deferfall(func, ...)
|
|
local oldfall = minetest.check_for_falling
|
|
minetest.check_for_falling = nodecore.fallcheck
|
|
local function helper(...)
|
|
minetest.check_for_falling = oldfall
|
|
return ...
|
|
end
|
|
return helper(func(...))
|
|
end
|
|
|
|
local laststack
|
|
local lastraking
|
|
local old_node_dig = minetest.node_dig
|
|
minetest.node_dig = function(pos, node, user, ...)
|
|
laststack = nodecore.stack_get(pos)
|
|
local wield = user and user:is_player() and user:get_wielded_item()
|
|
lastraking = wield and (wield:get_definition() or {}).on_rake
|
|
if lastraking then return deferfall(old_node_dig, pos, node, user, ...) end
|
|
return old_node_dig(pos, node, user, ...)
|
|
end
|
|
|
|
local stackonly = {}
|
|
minetest.after(0, function()
|
|
for k, v in pairs(minetest.registered_nodes) do
|
|
if v.groups.is_stack_only then stackonly[k] = true end
|
|
end
|
|
end)
|
|
local function matching(_, na, pb, nb)
|
|
if stackonly[na.name] then
|
|
if not stackonly[nb.name] then return end
|
|
return (laststack and nodecore.stack_family(laststack))
|
|
== nodecore.stack_family(nodecore.stack_get(pb))
|
|
end
|
|
return nodecore.stack_family(na.name) == nodecore.stack_family(nb.name)
|
|
end
|
|
|
|
local function dorake(volume, check, pos, node, user, ...)
|
|
local sneak = user:get_player_control().sneak
|
|
local objpos = {}
|
|
for _, rel in ipairs(volume) do
|
|
local p = vector.add(pos, rel)
|
|
local n = minetest.get_node(p)
|
|
local allow = (rel.d > 0 or nil) and check(p, n, rel)
|
|
if allow == false then break end
|
|
if allow and ((not sneak) or matching(pos, node, p, n)) then
|
|
minetest.node_dig(p, n, user, ...)
|
|
objpos[minetest.hash_node_position(p)] = true
|
|
end
|
|
end
|
|
for _, lua in pairs(minetest.luaentities) do
|
|
if lua.name == "__builtin:item" then
|
|
local p = lua.object and lua.object:get_pos()
|
|
if p and objpos[minetest.hash_node_position(
|
|
vector.round(p))] then
|
|
lua.object:set_pos(pos)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local rakelock = {}
|
|
|
|
nodecore.register_on_dignode("rake handling", function(pos, node, user, ...)
|
|
local nowraking = lastraking
|
|
if not nowraking then return end
|
|
lastraking = nil
|
|
|
|
if not (pos and node and user and user:is_player()) then return end
|
|
local volume, check = nowraking(pos, node, user, ...)
|
|
if not (volume and check) then return end
|
|
|
|
local pname = user:get_player_name()
|
|
if rakelock[pname] then return end
|
|
rakelock[pname] = true
|
|
deferfall(dorake, volume, check, pos, node, user, ...)
|
|
rakelock[pname] = nil
|
|
end)
|