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)