From b8a478c05bd4627dceb2f4ad2233ae49b1a96537 Mon Sep 17 00:00:00 2001 From: Juraj Vajda Date: Sun, 13 Nov 2022 11:00:16 -0500 Subject: [PATCH] Add group based enchantments and bow enchantments --- README.md | 84 ++++++++++++++++++++++ api.lua | 139 +++++++++++++++++++++++++++++++++---- types/xenchanting.type.lua | 9 +-- 3 files changed, 216 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index e9db8ca..45d72a9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Adds Enchanting Mechanics and API. * adds enchanting table * supports all registered tools with known tool groups: pickaxe, shovel, axe, sword, e.g. `groups = {pickaxe = 1}` +* support all bows with group `{bow = 1}` * supports `default:bookshelf` or anything with `group:bookshelf` * enchanting trade is for `default:mese_crystal` or anything with `groups = {enchanting_trade = 1}` * adds enchantability for all MT default tools, for custom tools the enchantability can be set in the item group, e.g. `groups = {enchantability = 15}` @@ -50,30 +51,113 @@ Items enchantibility from worst to best: Increases melee damage. +groups: sword + #### Fortune Increases the number and/or chances of specific item drops. This value is not used in the engine; it is the responsibility of the game/mod code to implement this. +groups: pickaxe, shovel, axe + #### Unbreaking Increases the item's durability. +groups: any + #### Efficiency Increases the player's mining speed. Also adds mining groupcaps to item, e.g. enchanted wood pickaxe can mine level 1 nodes (e.g. obsidian) after enchantment. +groups: pickaxe, shovel, axe + #### Silk Touch Causes certain blocks to drop themselves as items instead of their usual drops when mined. Mods can prevent this behaviour with adding group `{ no_silktouch = 1 }` to the nodes. +groups: pickaxe, shovel, axe + #### Curse of Vanishing Causes the item to disappear on death. +groups: any + #### Knockback Increases knockback (players only). +groups: sword + +#### Power + +Increases arrow damage. +Damage has to be calculated in the MOD where the bow comes from! + +groups: bow + +#### Punch + +Increases arrow knockback. +Knockback has to be calculated in the MOD where the bow comes from! + +This can be obtained from tool meta: + +groups: bow + +#### Infinity + +Prevents regular arrows from being consumed when shot. +One arrow is needed INSIDE QUIVER to use a bow enchanted with Infinity. +Fired arrows cannot be retrieved even if they are not fired from Quiver. +Only set in item meta, logic for this has to be in the MOD where the bow comes from! + +groups: bow + +### API + +`ItemStackMetaRef` + +* `get_float(key)`: Returns `0` if key not present. `key` can be enchantment id prefixed with `is_`, +e.g. enchantment `punch` would have stored meta as `is_punch`. If returned value is bigger than zero +then the value represents enchantment level bonus. See below fields for bow: + * `power` Increase percentage + * `punch` Multiplier + * `infinity` If `1` then it is infinity enchanted +* `get_string(key)`: Returns `""` if key not present. See below fields for all enchantments: + * `x_enchanting` Serialized table with key/value pairs where: `key` is enchantment `id` and `value` is `Enchantment` definition + +`Enchantment` definition + +* `value` number, Value of the enchantment based on level, e.g. multiplier, percentage/number increase... + +example: + +```lua +-- For simplicity assuming that all meta are present (biggger than zero or not "") +-- MODs have to add those checks individually + +local itemstack_meta = itemstack:get_meta() + +local power_value = itemstack_meta:get_float('is_power') +local punch_value = itemstack_meta:get_float('is_punch') +local infinity_value = itemstack_meta:get_float('is_infinity') + +-- Or for list of all enchantments +local x_enchanting = minetest.deserialize(itemstack_meta:get_string('x_enchanting')) or {} +local power_enchantment = enchantments.power +local punch_enchantment = enchantments.punch +local infinity_enchantment = enchantments.infinity + +-- Custom logic +local new_damage = damage + damage * (punch_enchantment.value / 100) +local new_knockback = knockback * punch_enchantment.value + +if enchantments.infinity.value > 0 then + -- Some logic for infinity +end +``` + ## Dependencies - none diff --git a/api.lua b/api.lua index c7ee84e..f2fddce 100644 --- a/api.lua +++ b/api.lua @@ -62,7 +62,10 @@ XEnchanting = { [4] = 5, [5] = 6.25, }, - weight = 10 + weight = 10, + groups = { + 'sword' + } }, fortune = { name = S('Fortune'), @@ -78,7 +81,12 @@ XEnchanting = { [2] = 2, [3] = 3 }, - weight = 2 + weight = 2, + groups = { + 'pickaxe', + 'shovel', + 'axe' + } }, unbreaking = { name = S('Unbreaking'), @@ -94,7 +102,9 @@ XEnchanting = { [2] = 200, [3] = 300 }, - weight = 5 + weight = 5, + -- all applicable + groups = nil }, efficiency = { name = S('Efficiency'), @@ -114,7 +124,12 @@ XEnchanting = { [4] = 40, [5] = 45, }, - weight = 10 + weight = 10, + groups = { + 'pickaxe', + 'shovel', + 'axe' + } }, silk_touch = { name = S('Silk Touch'), @@ -125,7 +140,12 @@ XEnchanting = { [1] = 1 }, weight = 1, - secondary = true + secondary = true, + groups = { + 'pickaxe', + 'shovel', + 'axe' + } }, curse_of_vanishing = { name = S('Curse of Vanishing'), @@ -136,7 +156,9 @@ XEnchanting = { [1] = 1 }, weight = 1, - secondary = true + secondary = true, + -- all applicable + groups = nil }, knockback = { name = S('Knockback'), @@ -149,7 +171,70 @@ XEnchanting = { [1] = 105, [2] = 190 }, - weight = 5 + weight = 5, + groups = { + 'sword' + } + }, + power = { + -- Increases arrow damage. + -- Damage has to be calculated in the MOD where the bow comes from! + name = S('Power'), + final_level_range = { + [1] = { 1, 16 }, + [2] = { 11, 26 }, + [3] = { 21, 36 }, + [4] = { 31, 46 }, + [5] = { 41, 56 } + }, + -- increase % + level_def = { + [1] = 50, + [2] = 75, + [3] = 100, + [4] = 125, + [5] = 150 + }, + weight = 10, + groups = { + 'bow' + } + }, + punch = { + -- Increases arrow knockback. + -- Knockback has to be calculated in the MOD where the bow comes from! + name = S('Punch'), + final_level_range = { + [1] = { 12, 37 }, + [2] = { 32, 57 } + }, + -- multiplier + level_def = { + [1] = 3, + [2] = 6 + }, + weight = 2, + groups = { + 'bow' + } + }, + infinity = { + -- Prevents regular arrows from being consumed when shot. + -- One arrow is needed to use a bow enchanted with Infinity. + -- Only set in item meta, logic for this has to be in the MOD where the bow comes from! + name = S('Infinity'), + final_level_range = { + [1] = { 20, 50 } + }, + -- will be set in meta as float + level_def = { + [1] = 1 + }, + weight = 1, + secondary = true, + groups = { + 'bow' + } }, }, form_context = {}, @@ -192,17 +277,22 @@ function XEnchanting.has_tool_group(self, name) return 'axe' elseif minetest.get_item_group(name, 'sword') > 0 then return 'sword' + elseif minetest.get_item_group(name, 'bow') > 0 then + return 'bow' end return false end function XEnchanting.set_tool_enchantability(self, tool_def) + if minetest.get_item_group(tool_def.name, 'enchantability') > 0 then + -- enchantability is already set, we dont need to override the item + return + end + local _enchantability = 1 - if minetest.get_item_group(tool_def.name, 'enchantability') > 0 then - _enchantability = minetest.get_item_group(tool_def.name, 'enchantability') - elseif self.tools_enchantability[tool_def.name] then + if self.tools_enchantability[tool_def.name] then _enchantability = self.tools_enchantability[tool_def.name] end @@ -390,16 +480,22 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name end local stack_meta = itemstack:get_meta() + ---@type table + local final_enchantments_meta = {} for i, enchantment in ipairs(final_enchantments) do stack_meta:set_float('is_' .. enchantment.id, enchantment.value) + -- store only necessary data, keeping the meta optimized + final_enchantments_meta[enchantment.id] = { + value = enchantment.value + } end stack_meta:set_tool_capabilities(capabilities) stack_meta:set_string('description', itemstack:get_description() .. '\n' .. description) stack_meta:set_string('short_description', S('Enchanted') .. ' ' .. itemstack:get_short_description()) stack_meta:set_int('is_enchanted', 1) - stack_meta:set_string('x_enchanting', minetest.serialize({ enchantments = final_enchantments })) + stack_meta:set_string('x_enchanting', minetest.serialize(final_enchantments_meta)) inv:set_stack('item', 1, itemstack) @@ -433,6 +529,25 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d _nr_of_bookshelfs = 15 end + ---- + -- Filter out enchantments compatible for this item group + ---- + + local group_enchantments = {} + + for enchantment_name, enchantment_def in pairs(self.enchantment_defs) do + if not enchantment_def.groups then + group_enchantments[enchantment_name] = enchantment_def + else + for i, group in ipairs(enchantment_def.groups) do + if minetest.get_item_group(tool_def.name, group) > 0 then + group_enchantments[enchantment_name] = enchantment_def + break + end + end + end + end + ---- -- 0 Show slots in formspec ---- @@ -474,7 +589,7 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d -- Get level -- If the modified level is within two overlapping ranges for the same -- enchantment type, the higher power value is used. - for enchantment_name, enchantment_def in pairs(self.enchantment_defs) do + for enchantment_name, enchantment_def in pairs(group_enchantments) do local levels = {} -- find matching levels diff --git a/types/xenchanting.type.lua b/types/xenchanting.type.lua index d234a0a..37a03b6 100644 --- a/types/xenchanting.type.lua +++ b/types/xenchanting.type.lua @@ -23,6 +23,7 @@ ---@field level_def table Level bonus. Value will be set in item meta as float. ---@field weight number Enchantment weight ---@field secondary boolean Will not appear in masked description and can be applied only as additional enchantment by probability chance. +---@field groups string[] | nil List of groups for items what are compatible with this enchantment. If `nil` then all groups are compatible. ---Form context @@ -36,10 +37,10 @@ ---@class Enchantment ----@field id string ----@field value number ----@field level number ----@field secondary boolean Will not appear in masked description and can be applied only as additional enchantment by probability chance. +---@field id string Unique ID of the enchantment +---@field value number Value of the enchantment based on level +---@field level number Level of the enchantment +---@field secondary boolean | nil Will not appear in masked description and can be applied only as additional enchantment by probability chance. ---@class EnchantmentDataSlot