blockgame/mods/bg_crafting/pummel.lua
trans_soup 7df5d33d02 refactor: extract repeated code into api function.
move function `starts_with`, which was declared twice in separate files,
to the api, and make those files use that function instead.
2023-10-17 14:58:15 +02:00

104 lines
3.2 KiB
Lua

local api = blockgame.crafting
-- pummeling is heavily based on nodecore by Warr1024 (both the mechanic itself and the code here implementing it).
local pummel_recipes = {}
function api.register_pummel_recipe (def)
local def = def or {}
-- TODO: require `def.name` to be specified and unique.
def.label = def.label or "unlabeled pummel recipe"
-- TODO: throw errors when these defs are invalid instead of just returning.
if not def.used then return false end
if not def.target then return false end
if not type(def.on_success) == "function" then return false end
def["type"] = "pummel"
table.insert(pummel_recipes, def)
table.insert(api.registered_recipes, def)
end
-- TODO: extract this function into general api
local function item_matches (nodename, options)
return blockgame.any(options, function (option)
if nodename == option then return true end
if blockgame.starts_with(option, "group:") then
local group = string.sub(option, 7)
return minetest.get_item_group(nodename, group) > 0
end
return false
end)
end
-- TODO: add support for pummel recipes using groups instead of just specific node names.
function api.pummel_check (pos, used, target_node)
for _, def in pairs(pummel_recipes) do
if item_matches(used, def.used) and item_matches(target_node.name, def.target) then
if type(def.check) == "function" and not def.check(pos, used, target_node) then
return false
end
def.on_success(pos, used, target_node)
return true
end
end
end
local pummeling = {}
-- NOTE: might wanna change this to somehow store pummels per-node instead of per-player?
blockgame.register_on_dignode(function (_, _, digger)
if not (digger and digger:is_player()) then return end
pummeling[digger:get_player_name()] = nil
end)
blockgame.register_on_punchnode(function (pos, node, puncher, pointed)
if (not puncher:is_player()) or puncher:get_player_control().sneak then return end
local player_name = puncher:get_player_name()
-- if not minetest.interact(player_name) then return end
if not minetest.check_player_privs(player_name, "interact") then return end
node = node or minetest.get_node(pos)
local node_def = minetest.registered_items[node.name] or {}
if not node_def.pointable then return end
local wield = puncher:get_wielded_item()
local now = minetest.get_us_time() / 1000000
local pummel_data = {
action = "pummel",
crafter = puncher,
crafter_name = player_name,
pos = pos,
pointed = pointed,
node = node,
node_def = node_def,
start = now,
wield = wield:get_name() .. " " .. wield:get_count(),
count = 0,
}
local old_data = pummeling[player_name]
local hash = minetest.hash_node_position
if old_data and hash(old_data.pos) == hash(pummel_data.pos)
and hash(old_data.pointed.above) == hash(pummel_data.pointed.above)
and hash(old_data.pointed.under) == hash(pummel_data.pointed.under)
and old_data.wield == pummel_data.wield
and old_data.last >= (now - 3)
then pummel_data = old_data
end
pummel_data.count = pummel_data.count + 1
pummel_data.last = now
pummel_data.duration = now - pummel_data.start - 1
pummeling[player_name] = pummel_data
if pummel_data.count < 2 then return end
if api.pummel_check(pos, wield:get_name(), minetest.get_node(pointed.under)) then
pummeling[player_name] = nil
end
end)