nodecore-cd2025/mods/nc_doors/craft_catapult.lua
Aaron Suen bd35a4df55 Standardize full protection bypass for automation
Protection mechanics are fully disabled for all actions carried
out by world mechanics itself, including machine digging/placing
and entity/fluid displacement.

This may create new opportunities for players to abuse and bypass
protection mechanics.  This is an acceptable loss; the integrity
of game mechanics is more important, or else players are forced to
remove protections anyway in order to get their builds to actually
work.
2023-06-05 07:20:55 -04:00

220 lines
6.0 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local ItemStack, minetest, nodecore, pairs, vector
= ItemStack, minetest, nodecore, pairs, vector
-- LUALOCALS > ---------------------------------------------------------
local presstoolcaps = {}
minetest.after(0, function()
for name, def in pairs(minetest.registered_items) do
local caps
if def.tool_capabilities then
caps = {dig = true, groups = def.tool_capabilities.groupcaps}
elseif def.tool_head_capabilities then
caps = {groups = def.tool_head_capabilities.groupcaps}
end
if minetest.get_item_group(name, "nc_doors_pummel_first") > 0 then
caps.pummelfirst = true
end
presstoolcaps[name] = caps
end
end)
local function checktarget(data, stack)
local target = vector.subtract(vector.multiply(
data.pointed.under, 2), data.pointed.above)
data.presstarget = target
local node = minetest.get_node(target)
local def = minetest.registered_items[node.name] or {walkable = true}
-- Inject item into available storebox
if def.groups and def.groups.visinv and (def.groups.is_stack_only
or def.storebox_access and def.storebox_access({
type = "node",
above = target,
under = vector.subtract(vector.multiply(
target, 2), data.pointed.under)
})) then
-- Never insert from bottom, so we can always dig
local dir = vector.subtract(data.pointed.under, data.pointed.above)
if dir.y <= 0 then
local one = ItemStack(stack:to_string())
one:set_count(1)
local tstack = nodecore.stack_get(target)
if tstack:item_fits(one) then
data.intostorebox = target
return true
end
end
end
local stackname = stack:get_name()
local caps = presstoolcaps[stackname]
local function pummelcheck()
if not caps then return end
local pumdata = {
action = "pummel",
pos = target,
pointed = {
type = "node",
above = data.pointed.under,
under = target
},
node = node,
nodedef = def,
duration = 3600,
presstoolpos = data.pointed.under,
wield = stack,
toolgroupcaps = caps.groups
}
-- if pummelfirst doesn't find anything, can't find
-- anything on second pass
pummelcheck = function() end
return nodecore.craft_search(target, node, pumdata)
end
if caps and caps.pummelfirst then
data.presscommit = pummelcheck()
if data.presscommit then return data.presscommit end
end
-- Try to dig item
if caps and caps.dig and def and def.groups
and nodecore.tool_digs(stack, def.groups) then
data.pressdig = {
pos = target,
tool = stack,
toolpos = data.pointed.under
}
return true
end
-- Eject item as entity
if not def.walkable then return true end
data.presscommit = pummelcheck()
return data.presscommit
end
local hashpos = minetest.hash_node_position
local toolfxqueue
local function toolfx(toolpos, actpos)
nodecore.node_sound(toolpos, "dig")
if not toolfxqueue then
toolfxqueue = {}
minetest.after(0, function()
for _, ent in pairs(minetest.luaentities) do
local target = ent.is_stack and ent.poskey
and toolfxqueue[ent.poskey]
if target then
local obj = ent.object
local pos = ent.pos
obj:set_pos(target)
minetest.after(0.1, function()
obj:move_to(pos)
end)
end
end
toolfxqueue = nil
end)
end
toolfxqueue[hashpos(toolpos)] = actpos
end
local function doitemeject(pos, data)
if data.pressdig then
toolfx(pos, data.presstarget)
nodecore.machine_digging = data.pressdig
nodecore.protection_bypass(minetest.dig_node, data.pressdig.pos)
nodecore.machine_digging = nil
nodecore.witness({pos, data.pointed.above, data.presstarget}, "door dig")
return
end
if data.presscommit then
toolfx(pos, data.presstarget)
data.presscommit()
nodecore.witness({pos, data.pointed.above, data.presstarget}, "door pummel")
return
end
local stack = nodecore.stack_get(pos)
if (not stack) or stack:is_empty() then return end
local one = ItemStack(stack:to_string())
one:set_count(1)
if data.intostorebox then
nodecore.witness({pos, data.pointed.above, data.intostorebox}, "door store")
one = nodecore.stack_add(data.intostorebox, one)
nodecore.stack_sounds(data.intostorebox, "place")
if not one:is_empty() then return end
else
local ctr = {
x = data.axis.x ~= 0 and data.axis.x or pos.x,
y = data.axis.y ~= 0 and data.axis.y or pos.y,
z = data.axis.z ~= 0 and data.axis.z or pos.z
}
local vel = vector.add(
vector.subtract(pos, ctr),
vector.subtract(data.pointed.under, data.pointed.above)
)
local doorlv = minetest.get_item_group(minetest.get_node(
data.pointed.above).name, "door") or 0
nodecore.item_eject(
vector.add(pos, vector.multiply(vel, 0.25)),
one, 0, 1, vector.multiply(vel, 2 + doorlv)
)
end
nodecore.stack_sounds(pos, "dig")
stack:take_item(1)
if stack:is_empty() and nodecore.node_group("is_stack_only", pos) then
return minetest.remove_node(pos)
end
nodecore.stack_set(pos, stack)
return nodecore.witness({pos, data.pointed.above}, "door catapult")
end
nodecore.register_craft({
action = "press",
label = "eject item",
priority = 2,
nodes = {
{match = {stacked = true, count = false}}
},
check = function(pos, data)
local stack = nodecore.stack_get(pos)
if (not stack) or stack:is_empty() then return end
return checktarget(data, stack)
end,
after = doitemeject
})
nodecore.register_craft({
action = "press",
label = "eject from storebox",
priority = -1,
nodes = {
{match = {groups = {storebox = true}}}
},
check = function(pos, data)
local stack = nodecore.stack_get(pos)
if (not stack) or stack:is_empty() then return end
if not checktarget(data, stack) then return end
local pt = data.pointed
local node = minetest.get_node(pt.under)
local def = minetest.registered_items[node.name] or {}
if not def.storebox_access then return end
local access = {
type = "node",
above = vector.add(vector.multiply(
vector.subtract(pt.above, pt.under),
-1), pt.under),
under = pt.under
}
return def.storebox_access(access)
end,
after = doitemeject
})