Add group based enchantments and bow enchantments

This commit is contained in:
Juraj Vajda 2022-11-13 11:00:16 -05:00
parent 399f533731
commit b8a478c05b
3 changed files with 216 additions and 16 deletions

View File

@ -8,6 +8,7 @@ Adds Enchanting Mechanics and API.
* adds enchanting table * adds enchanting table
* supports all registered tools with known tool groups: pickaxe, shovel, axe, sword, e.g. `groups = {pickaxe = 1}` * 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` * supports `default:bookshelf` or anything with `group:bookshelf`
* enchanting trade is for `default:mese_crystal` or anything with `groups = {enchanting_trade = 1}` * 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}` * 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. Increases melee damage.
groups: sword
#### Fortune #### 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. 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 #### Unbreaking
Increases the item's durability. Increases the item's durability.
groups: any
#### Efficiency #### 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. 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 #### 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. 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 #### Curse of Vanishing
Causes the item to disappear on death. Causes the item to disappear on death.
groups: any
#### Knockback #### Knockback
Increases knockback (players only). 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 ## Dependencies
- none - none

139
api.lua
View File

@ -62,7 +62,10 @@ XEnchanting = {
[4] = 5, [4] = 5,
[5] = 6.25, [5] = 6.25,
}, },
weight = 10 weight = 10,
groups = {
'sword'
}
}, },
fortune = { fortune = {
name = S('Fortune'), name = S('Fortune'),
@ -78,7 +81,12 @@ XEnchanting = {
[2] = 2, [2] = 2,
[3] = 3 [3] = 3
}, },
weight = 2 weight = 2,
groups = {
'pickaxe',
'shovel',
'axe'
}
}, },
unbreaking = { unbreaking = {
name = S('Unbreaking'), name = S('Unbreaking'),
@ -94,7 +102,9 @@ XEnchanting = {
[2] = 200, [2] = 200,
[3] = 300 [3] = 300
}, },
weight = 5 weight = 5,
-- all applicable
groups = nil
}, },
efficiency = { efficiency = {
name = S('Efficiency'), name = S('Efficiency'),
@ -114,7 +124,12 @@ XEnchanting = {
[4] = 40, [4] = 40,
[5] = 45, [5] = 45,
}, },
weight = 10 weight = 10,
groups = {
'pickaxe',
'shovel',
'axe'
}
}, },
silk_touch = { silk_touch = {
name = S('Silk Touch'), name = S('Silk Touch'),
@ -125,7 +140,12 @@ XEnchanting = {
[1] = 1 [1] = 1
}, },
weight = 1, weight = 1,
secondary = true secondary = true,
groups = {
'pickaxe',
'shovel',
'axe'
}
}, },
curse_of_vanishing = { curse_of_vanishing = {
name = S('Curse of Vanishing'), name = S('Curse of Vanishing'),
@ -136,7 +156,9 @@ XEnchanting = {
[1] = 1 [1] = 1
}, },
weight = 1, weight = 1,
secondary = true secondary = true,
-- all applicable
groups = nil
}, },
knockback = { knockback = {
name = S('Knockback'), name = S('Knockback'),
@ -149,7 +171,70 @@ XEnchanting = {
[1] = 105, [1] = 105,
[2] = 190 [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 = {}, form_context = {},
@ -192,17 +277,22 @@ function XEnchanting.has_tool_group(self, name)
return 'axe' return 'axe'
elseif minetest.get_item_group(name, 'sword') > 0 then elseif minetest.get_item_group(name, 'sword') > 0 then
return 'sword' return 'sword'
elseif minetest.get_item_group(name, 'bow') > 0 then
return 'bow'
end end
return false return false
end end
function XEnchanting.set_tool_enchantability(self, tool_def) 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 local _enchantability = 1
if minetest.get_item_group(tool_def.name, 'enchantability') > 0 then if self.tools_enchantability[tool_def.name] then
_enchantability = minetest.get_item_group(tool_def.name, 'enchantability')
elseif self.tools_enchantability[tool_def.name] then
_enchantability = self.tools_enchantability[tool_def.name] _enchantability = self.tools_enchantability[tool_def.name]
end end
@ -390,16 +480,22 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
end end
local stack_meta = itemstack:get_meta() local stack_meta = itemstack:get_meta()
---@type table<string, {["value"]: number}>
local final_enchantments_meta = {}
for i, enchantment in ipairs(final_enchantments) do for i, enchantment in ipairs(final_enchantments) do
stack_meta:set_float('is_' .. enchantment.id, enchantment.value) 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 end
stack_meta:set_tool_capabilities(capabilities) stack_meta:set_tool_capabilities(capabilities)
stack_meta:set_string('description', itemstack:get_description() .. '\n' .. description) 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_string('short_description', S('Enchanted') .. ' ' .. itemstack:get_short_description())
stack_meta:set_int('is_enchanted', 1) 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) 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 _nr_of_bookshelfs = 15
end 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 -- 0 Show slots in formspec
---- ----
@ -474,7 +589,7 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
-- Get level -- Get level
-- If the modified level is within two overlapping ranges for the same -- If the modified level is within two overlapping ranges for the same
-- enchantment type, the higher power value is used. -- 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 = {} local levels = {}
-- find matching levels -- find matching levels

View File

@ -23,6 +23,7 @@
---@field level_def table<number, number> Level bonus. Value will be set in item meta as float. ---@field level_def table<number, number> Level bonus. Value will be set in item meta as float.
---@field weight number Enchantment weight ---@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 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 ---Form context
@ -36,10 +37,10 @@
---@class Enchantment ---@class Enchantment
---@field id string ---@field id string Unique ID of the enchantment
---@field value number ---@field value number Value of the enchantment based on level
---@field level number ---@field level number Level of the enchantment
---@field secondary boolean Will not appear in masked description and can be applied only as additional enchantment by probability chance. ---@field secondary boolean | nil Will not appear in masked description and can be applied only as additional enchantment by probability chance.
---@class EnchantmentDataSlot ---@class EnchantmentDataSlot