-- X Enchanting -- by SaKeL local path = minetest.get_modpath('x_enchanting') local mod_start_time = minetest.get_us_time() dofile(path .. '/api.lua') dofile(path .. '/table.lua') ---Check if string X starts with string Y ---@param str string ---@param start string ---@return boolean local function starts_with(str, start) return str:sub(1, #start) == start end minetest.register_on_mods_loaded(function() -- Tools override for name, tool_def in pairs(minetest.registered_tools) do if XEnchanting:has_tool_group(name) then XEnchanting:set_tool_enchantability(tool_def) end end -- Ores override - Fortune for _, def in pairs(minetest.registered_ores) do if not XEnchanting.registered_ores[def.ore] then XEnchanting.registered_ores[def.ore] = true end end -- Entities override - Looting for name, def in pairs(minetest.registered_entities) do if starts_with(name, 'mobs_animal:') or starts_with(name, 'mobs_monster:') then if def.on_punch and def.drops then local prev_on_punch = def.on_punch ---@param self table ---@param puncher ObjectRef|nil ---@param time_from_last_punch number|integer|nil ---@param tool_capabilities ToolCapabilitiesDef|nil ---@param dir Vector ---@param damage number|integer ---@return boolean|nil def.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) if not self or not self.object or not self.object:get_luaentity() or not puncher or not tool_capabilities then return prev_on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) end local wield_stack = puncher:get_wielded_item() local wield_stack_meta = wield_stack:get_meta() local looting = wield_stack_meta:get_float('is_looting') if looting == 0 then return prev_on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) end local pos = self.object:get_pos() prev_on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) if self.health and self.health <= 0 then local death_by_player = self.cause_of_death and self.cause_of_death.puncher and self.cause_of_death.puncher:is_player() if death_by_player and pos then for _, drop in ipairs(def.drops) do if math.random(10, 100) / 100 < looting / (looting + 1) then local drop_min = drop.min or 0 local drop_max = drop.max or 0 local count = math.random(drop_min, drop_max) local stack = ItemStack({ name = drop.name, count = count }) local chance = math.random(1, tool_capabilities.max_drop_level) stack:set_count(stack:get_count() * chance) if stack:get_count() > 0 then minetest.item_drop(stack, puncher, pos) end end end end end end end end end end) minetest.register_on_joinplayer(function(player, last_login) XEnchanting.form_context[player:get_player_name()] = nil if not XEnchanting.player_seeds[player:get_player_name()] then XEnchanting.player_seeds[player:get_player_name()] = XEnchanting.get_randomseed() end end) minetest.register_on_leaveplayer(function(player, timed_out) XEnchanting.form_context[player:get_player_name()] = nil end) local old_handle_node_drops = minetest.handle_node_drops function minetest.handle_node_drops(pos, drops, digger) if not digger or not digger:is_player() then return old_handle_node_drops(pos, drops, digger) end local node = minetest.get_node(pos) local wield_stack = digger:get_wielded_item() local wield_stack_meta = wield_stack:get_meta() -- Fortune local fortune = wield_stack_meta:get_float('is_fortune') if fortune > 0 then local new_drops = {} for _, itemstring in ipairs(drops) do if XEnchanting.registered_ores[node.name] or minetest.get_item_group(node.name, 'stone') > 0 or minetest.get_item_group(node.name, 'soil') > 0 or minetest.get_item_group(node.name, 'sand') > 0 or minetest.get_item_group(node.name, 'snowy') > 0 or minetest.get_item_group(node.name, 'slippery') > 0 or minetest.get_item_group(node.name, 'tree') > 0 or minetest.get_item_group(node.name, 'leaves') > 0 then local tool_capabilities = wield_stack:get_tool_capabilities() local stack = ItemStack(itemstring) local chance = math.random(1, tool_capabilities.max_drop_level) stack:set_count(stack:get_count() * chance) if stack:get_count() > 0 then table.insert(new_drops, stack) end end end if #new_drops > 0 then return old_handle_node_drops(pos, new_drops, digger) end return old_handle_node_drops(pos, drops, digger) end -- Silk Touch local silk_touch = wield_stack_meta:get_float('is_silk_touch') if silk_touch > 0 and minetest.get_item_group(node.name, 'no_silktouch') == 0 then -- drop raw item/node return old_handle_node_drops(pos, { ItemStack(node.name) }, digger) end return old_handle_node_drops(pos, drops, digger) end minetest.register_on_player_hpchange(function(player, hp_change, reason) -- Curse of Vanishing if (player:get_hp() + hp_change) <= 0 then -- Going to die local player_inv = player:get_inventory() --[[@as InvRef]] local player_inventory_lists = { 'main', 'craft' } for _, list_name in ipairs(player_inventory_lists) do if not player_inv:is_empty(list_name) then for i = 1, player_inv:get_size(list_name) do local stack = player_inv:get_stack(list_name, i) local stack_meta = stack:get_meta() if stack_meta:get_float('is_curse_of_vanishing') > 0 then player_inv:set_stack(list_name, i, ItemStack('')) end end end end end return hp_change end, true) -- Knockback (only for players) local old_calculate_knockback = minetest.calculate_knockback function minetest.calculate_knockback(player, hitter, time_from_last_punch, tool_capabilities, dir, distance, damage) if hitter and hitter:is_player() then local hitter_wield_stack = hitter:get_wielded_item() local hitter_wield_stack_meta = hitter_wield_stack:get_meta() local ench_knockback = hitter_wield_stack_meta:get_float('is_knockback') if ench_knockback > 0 then local orig_knockback = old_calculate_knockback( player, hitter, time_from_last_punch, tool_capabilities, dir, distance, damage ) orig_knockback = orig_knockback + 1 local new_knockback = orig_knockback + (orig_knockback * (ench_knockback / 100)) player:add_velocity(vector.new(0, new_knockback, 0)) return new_knockback end end return old_calculate_knockback(player, hitter, time_from_last_punch, tool_capabilities, dir, distance, damage) end local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000 print('[Mod] x_enchanting loaded.. [' .. mod_end_time .. 's]')