1
0
mirror of https://bitbucket.org/minetest_gamers/x_enchanting.git synced 2025-06-28 22:06:17 +02:00

16 Commits

Author SHA1 Message Date
4303c5f8bf Dont show enchanted item enchantments when take/put in inventory 2022-11-16 16:09:48 -05:00
1e5a76ed18 Add looting support to animalia 2022-11-16 15:53:17 -05:00
96b8369500 Add particles on enchant, formspec gui adjustments for small screens 2022-11-16 14:15:01 -05:00
03ad38cfb3 Update formspec gui 2022-11-15 23:49:09 -05:00
cad216468a Add dynamic looting chance 2022-11-14 23:41:19 -05:00
ab3cb43705 Add looting support for mobs_monster and mobs_animal 2022-11-14 22:46:48 -05:00
11fc35b049 Dont remove all inventory with cursed item 2022-11-14 18:23:57 -05:00
27c35f551d Add looting and make fortune drop more stuff, implement incompatible enchantments 2022-11-14 14:29:15 -05:00
b9e5fcb3fe update readme 2022-11-13 11:49:40 -05:00
b8a478c05b Add group based enchantments and bow enchantments 2022-11-13 11:00:16 -05:00
399f533731 update types 2022-11-12 21:14:49 -05:00
210a213ad7 update readme 2022-11-11 22:34:13 -05:00
4ee4e17925 update readme 2022-11-11 21:18:16 -05:00
4b4d38801c add knockback, adjust enchantment probability algorithm, add new description logic, fix type errors, add player relative randomseed 2022-11-11 21:00:41 -05:00
1266b0db80 Add Curse of Vanishing enchantment 2022-11-10 21:42:28 -05:00
dd04f68777 adjust sounds and licensing 2022-11-10 15:52:53 -05:00
37 changed files with 838 additions and 172 deletions

116
README.md
View File

@ -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,22 +51,125 @@ 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.
Increases the number and/or chances of specific item drops. Works with groups: stone, soil, sand, snowy, slippery, tree, leaves and all registered ores.
Incompatible: Silk Touch
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.
Incompatible: Fortune
groups: pickaxe, shovel, axe
#### Curse of Vanishing
Causes the item to disappear on death.
groups: any
#### Knockback
Increases knockback (players only).
groups: sword
#### Looting
Cause mobs to drop more items. This value is not used in the engine; it is the responsibility of the game/mod code to implement this.
Supported: mobs_monster, mobs_animal
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
@ -122,7 +226,11 @@ GNU Lesser General Public License v2.1 or later (see included LICENSE file)
- x_enchanting_scroll_mesh.png -- Derived from a texture TumeniNodes (CC BY-SA 3.0)
- x_enchanting_scroll_particle.png
- x_enchanting_table.png
- x_enchanting_trade_slot.png
- x_enchanting_gui_slot_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC BY-SA 4.0)
- x_enchanting_gui_cloth_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC BY-SA 4.0)
- x_enchanting_gui_cloth_trade_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC BY-SA 4.0)
- x_enchanting_gui_formbg.png -- Derived from texture Pixel Perfection by XSSheep (CC BY-SA 4.0)
- x_enchanting_gui_hb_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC BY-SA 4.0)
### Models
@ -133,11 +241,11 @@ GNU Lesser General Public License v2.1 or later (see included LICENSE file)
### Sounds
**Creative Commons License, Kostas17**, https://freesound.org
**CC-BY-3.0, Kostas17**, https://freesound.org
- x_enchanting_enchant.ogg
**Creative Commons License, jammaj**, https://freesound.org
**CC0-1.0, Zeinel**, https://freesound.org
- x_enchanting_scroll.1.ogg
- x_enchanting_scroll.2.ogg

508
api.lua
View File

@ -1,5 +1,3 @@
default = minetest.global_exists('default') and default --[[@as MtgDefault]]
local S = minetest.get_translator(minetest.get_current_modname())
---@type XEnchanting
@ -62,7 +60,29 @@ XEnchanting = {
[4] = 5,
[5] = 6.25,
},
weight = 10
weight = 10,
groups = {
'sword'
}
},
looting = {
name = S('Looting'),
-- what level should be taken, `level = min/max values`
final_level_range = {
[1] = { 15, 65 },
[2] = { 24, 74 },
[3] = { 33, 83 }
},
-- level definition, `level = number to add`
level_def = {
[1] = 1,
[2] = 2,
[3] = 3
},
weight = 2,
groups = {
'sword'
}
},
fortune = {
name = S('Fortune'),
@ -78,7 +98,13 @@ XEnchanting = {
[2] = 2,
[3] = 3
},
weight = 2
weight = 2,
groups = {
'pickaxe',
'shovel',
'axe'
},
incompatible = { 'silk_touch' }
},
unbreaking = {
name = S('Unbreaking'),
@ -94,7 +120,9 @@ XEnchanting = {
[2] = 200,
[3] = 300
},
weight = 5
weight = 5,
-- all applicable
groups = nil
},
efficiency = {
name = S('Efficiency'),
@ -114,7 +142,12 @@ XEnchanting = {
[4] = 40,
[5] = 45,
},
weight = 10
weight = 10,
groups = {
'pickaxe',
'shovel',
'axe'
}
},
silk_touch = {
name = S('Silk Touch'),
@ -122,19 +155,116 @@ XEnchanting = {
[1] = { 15, 65 }
},
level_def = {
[1] = 'silk_touch'
[1] = 1
},
weight = 1
}
weight = 1,
secondary = true,
groups = {
'pickaxe',
'shovel',
'axe'
},
incompatible = { 'fortune' }
},
curse_of_vanishing = {
name = S('Curse of Vanishing'),
final_level_range = {
[1] = { 25, 50 }
},
level_def = {
[1] = 1
},
weight = 1,
secondary = true,
-- all applicable
groups = nil
},
knockback = {
name = S('Knockback'),
final_level_range = {
[1] = { 5, 55 },
[2] = { 25, 75 }
},
-- increase %
level_def = {
[1] = 105,
[2] = 190
},
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'
}
},
},
randomseed = os.time(),
form_context = {},
player_seeds = {},
scroll_animations = {
scroll_open = { { x = 1, y = 40 }, 80, 0, false },
scroll_close = { { x = 45, y = 84 }, 80, 0, false },
scroll_open_idle = { { x = 41, y = 42 }, 0, 0, false },
scroll_closed_idle = { { x = 43, y = 44 }, 0, 0, false }
}
},
registered_ores = {}
}
---Merge two tables with key/value pair
@ -146,6 +276,18 @@ local function mergeTables(t1, t2)
return t1
end
---Gets length of hashed table
---@param table any
---@return integer
local function get_table_length(table)
local length = 0
for _ in pairs(table) do
length = length + 1
end
return length
end
function XEnchanting.has_tool_group(self, name)
if minetest.get_item_group(name, 'pickaxe') > 0 then
return 'pickaxe'
@ -155,17 +297,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
@ -177,8 +324,6 @@ end
function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantments)
local tool_stack = ItemStack({ name = tool_def.name })
local tool_capabilities = tool_stack:get_tool_capabilities()
local enchantments_desc = {}
local enchantments_desc_masked = {}
for i, enchantment in ipairs(enchantments) do
-- Efficiency
@ -246,18 +391,6 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
tool_capabilities.full_punch_interval = new_fpi
end
if tool_capabilities.groupcaps or tool_capabilities.full_punch_interval then
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
if #enchantments_desc_masked == 0 then
enchantments_desc_masked[#enchantments_desc_masked + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
end
end
end
-- Unbreaking
@ -282,18 +415,6 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
tool_capabilities.punch_attack_uses = new_uses
end
if tool_capabilities.groupcaps or tool_capabilities.punch_attack_uses then
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
if #enchantments_desc_masked == 0 then
enchantments_desc_masked[#enchantments_desc_masked + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
end
end
end
-- Sharpness
@ -304,52 +425,57 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
tool_capabilities.damage_groups[group_name] = new_damage
end
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
if #enchantments_desc_masked == 0 then
enchantments_desc_masked[#enchantments_desc_masked + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
end
end
-- Fortune
if enchantment.id == 'fortune' and tool_capabilities.max_drop_level then
if enchantment.id == 'fortune' or enchantment.id == 'looting' and tool_capabilities.max_drop_level then
local old_max_drop_level = tool_capabilities.max_drop_level
local new_max_drop_level = old_max_drop_level + enchantment.value
tool_capabilities.max_drop_level = new_max_drop_level
end
end
return tool_capabilities
end
function XEnchanting.get_randomseed(self)
return tonumber(tostring(os.time()):reverse():sub(1, 9)) --[[@as integer]]
end
function XEnchanting.get_enchanted_descriptions(self, enchantments)
local enchantments_desc = {}
local enchantments_desc_masked = {}
for i, enchantment in ipairs(enchantments) do
local add_roman_numbers = true
if get_table_length(self.enchantment_defs[enchantment.id].final_level_range) == 1 then
add_roman_numbers = false
end
if add_roman_numbers then
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
if #enchantments_desc_masked == 0 then
enchantments_desc_masked[#enchantments_desc_masked + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
end
else
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
end
-- Silk Touch
if enchantment.id == 'silk_touch' then
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
if #enchantments_desc_masked == 0 and not enchantment.secondary then
enchantments_desc_masked[#enchantments_desc_masked + 1] = self.enchantment_defs[enchantment.id].name
if #enchantments_desc_masked == 0 then
enchantments_desc_masked[#enchantments_desc_masked + 1] = self.enchantment_defs[enchantment.id].name
if add_roman_numbers then
enchantments_desc_masked[#enchantments_desc_masked + 1] = ' ' .. self.roman_numbers[enchantment.level]
end
end
end
enchantments_desc = '\n' .. minetest.colorize('#AE81FF', S('Enchanted'))
.. '\n' .. table.concat(enchantments_desc, '\n')
enchantments_desc_masked = table.concat(enchantments_desc_masked, '') .. '...?'
enchantments_desc_masked = table.concat(enchantments_desc_masked, '') .. '..?'
return {
tool_capabilities = tool_capabilities,
enchantments_desc = enchantments_desc,
enchantments_desc_masked = enchantments_desc_masked
}
@ -357,8 +483,13 @@ end
function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name)
local data = self.form_context[player_name].data
local capabilities = data.slots[level].tool_cap_data.tool_capabilities
local description = data.slots[level].tool_cap_data.enchantments_desc
if not data then
return
end
local capabilities = data.slots[level].tool_cap_data
local description = data.slots[level].descriptions.enchantments_desc
local final_enchantments = data.slots[level].final_enchantments
local inv = minetest.get_meta(pos):get_inventory()
local tool_def = minetest.registered_tools[itemstack:get_name()]
@ -369,24 +500,22 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
end
local stack_meta = itemstack:get_meta()
local is_silk_touch = 0
---@type table<string, {["value"]: number}>
local final_enchantments_meta = {}
for i, val in ipairs(final_enchantments) do
if val.id == 'silk_touch' then
is_silk_touch = 1
break
end
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 }))
if is_silk_touch > 0 then
stack_meta:set_int('is_silk_touch', 1)
end
stack_meta:set_string('x_enchanting', minetest.serialize(final_enchantments_meta))
inv:set_stack('item', 1, itemstack)
@ -395,7 +524,7 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
inv:set_stack('trade', 1, trade_stack)
-- set new seed
self.randomseed = tonumber(tostring(os.time()):reverse():sub(1, 9)) --[[@as number]]
self.player_seeds[player_name] = self:get_randomseed()
local formspec = self:get_formspec(pos, player_name)
node_meta:set_string('formspec', formspec)
@ -405,9 +534,64 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
pos = pos,
max_hear_distance = 10
}, true)
-- particles
local particlespawner_def = {
amount = 50,
time = 0.5,
minpos = { x = pos.x - 1, y = pos.y + 1, z = pos.z - 1 },
maxpos = { x = pos.x + 1, y = pos.y + 1.5, z = pos.z + 1 },
minvel = { x = -0.1, y = -0.5, z = -0.1 },
maxvel = { x = 0.1, y = -1.5, z = 0.1 },
minacc = { x = -0.1, y = -0.5, z = -0.1 },
maxacc = { x = 0.1, y = -1.5, z = 0.1 },
minexptime = 0.5,
maxexptime = 1,
minsize = 0.5,
maxsize = 1,
texture = 'x_enchanting_scroll_particle.png^[colorize:#A179E9:256',
glow = 1
}
if minetest.has_feature({ dynamic_add_media_table = true, particlespawner_tweenable = true }) then
-- new syntax, after v5.6.0
particlespawner_def = {
amount = 50,
time = 0.5,
size = {
min = 0.5,
max = 1,
},
exptime = 2,
pos = {
min = vector.new({ x = pos.x - 1.5, y = pos.y + 1, z = pos.z - 1.5 }),
max = vector.new({ x = pos.x + 1.5, y = pos.y + 1.5, z = pos.z + 1.5 }),
},
attract = {
kind = 'point',
strength = 2,
origin = vector.new({ x = pos.x, y = pos.y + 0.65, z = pos.z }),
die_on_contact = true
},
texture = {
name = 'x_enchanting_scroll_particle.png^[colorize:#A179E9:256',
alpha_tween = {
0.5, 1,
style = 'fwd',
reps = 1
}
},
glow = 1
}
end
minetest.add_particlespawner(particlespawner_def)
end
function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_def)
local p_name = player:get_player_name()
local randomseed = self.player_seeds[p_name] or self:get_randomseed()
math.randomseed(randomseed)
local _nr_of_bookshelfs = nr_of_bookshelfs
local data = {
slots = {}
@ -417,12 +601,30 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
_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
----
-- Base enchantment
math.randomseed(self.randomseed)
local base = math.random(1, 8) + math.floor(_nr_of_bookshelfs / 2) + math.random(0, _nr_of_bookshelfs)
local top_slot_base_level = math.floor(math.max(base / 3, 1))
local middle_slot_base_level = math.floor((base * 2) / 3 + 1)
@ -437,12 +639,10 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
-- Applying modifiers to the enchantment level
local enchantability = minetest.get_item_group(tool_def.name, 'enchantability')
-- Generate a random number between 1 and 1+(enchantability/2), with a triangular distribution
math.randomseed(self.randomseed)
local rand_enchantability = 1 + math.random(enchantability / 4 + 1) + math.random(enchantability / 4 + 1)
-- Choose the enchantment level
local k = chosen_enchantment_level + rand_enchantability
-- A random bonus, between .85 and 1.15
math.randomseed(self.randomseed)
local rand_bonus_percent = 1 + ((math.random(0, 99) / 100) + (math.random(0, 99) / 100) - 1) * 0.15
-- Finally, we calculate the level
local final_level = math.round(k * rand_bonus_percent)
@ -455,14 +655,16 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
-- 2 Find possible enchantments
----
---@type Enchantment[]
local possible_enchantments = {}
-- 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
for level, final_level_range in ipairs(enchantment_def.final_level_range) do
local min = final_level_range[1]
local max = final_level_range[2]
@ -472,13 +674,16 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
end
end
-- pick the highest level
local level = levels[#levels]
if level then
table.insert(possible_enchantments, {
id = enchantment_name,
value = enchantment_def.level_def[level],
level = level
level = level,
secondary = enchantment_def.secondary,
incompatible = enchantment_def.incompatible
})
end
end
@ -487,6 +692,7 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
-- 3 Select a set of enchantments from the list
----
---@type Enchantment[]
local final_enchantments = {}
local total_weight = 0
@ -495,18 +701,77 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
total_weight = total_weight + self.enchantment_defs[enchantment.id].weight
end
math.randomseed(self.randomseed)
-- Pick a random integer in the half range [0; total_weight / 2] as a number `rand_weight`
local rand_weight = math.random(0, total_weight / 2)
-- local probability = (final_level + 1) / 50
local probability_level = final_level
---@type Enchantment[]
local possible_enchantments_excl_secodnary = {}
-- select final enchantments
for _, enchantment in pairs(possible_enchantments) do
if not enchantment.secondary then
table.insert(possible_enchantments_excl_secodnary, enchantment)
end
end
-- Select final enchantments
-- Iterate through each enchantment in the list, subtracting its weight from `rand_weight`.
-- If `rand_weight` is now negative, select the current enchantment.
for j = 1, #possible_enchantments, 1 do
math.randomseed(self.randomseed)
local rand_ench_idx = math.random(1, #possible_enchantments)
local rand_ench = possible_enchantments[rand_ench_idx]
table.remove(possible_enchantments, rand_ench_idx)
if j == 1 then
-- First pick
-- Dont add cursed/secondary enchantment as first pick
rand_ench_idx = math.random(1, #possible_enchantments_excl_secodnary)
rand_ench = possible_enchantments_excl_secodnary[rand_ench_idx]
table.insert(final_enchantments, rand_ench)
for idx, value in pairs(possible_enchantments) do
if rand_ench.id == value.id then
table.remove(possible_enchantments, idx)
end
-- remove incomaptible enchantments
if rand_ench.incompatible
and table.indexof(rand_ench.incompatible, value.id) ~= -1
then
table.remove(possible_enchantments, idx)
end
end
else
local probability = (probability_level + 1) / 50
table.insert(final_enchantments, rand_ench)
table.remove(possible_enchantments, rand_ench_idx)
for idx, value in pairs(possible_enchantments) do
-- remove incomaptible enchantments
if rand_ench.incompatible
and table.indexof(rand_ench.incompatible, value.id) ~= -1
then
table.remove(possible_enchantments, idx)
end
end
-- With probability (`final_level` + 1) / 50, keep going. Otherwise, stop picking bonus enchantments.
local rand_probability = math.random()
if rand_probability < probability then
-- Divide the `final_level` in half, rounded down
-- (this does not affect the possible enchantments themselves,
-- because they were all pre-calculated in Step Two).
break
end
-- Repeat from the beginning.
probability_level = probability_level / 2
end
rand_weight = rand_weight - self.enchantment_defs[rand_ench.id].weight
table.insert(final_enchantments, rand_ench)
-- If `rand_weight` is now negative, select the current enchantment and stop.
if rand_weight < 0 then
@ -515,17 +780,52 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
end
local tool_cap_data = self:get_enchanted_tool_capabilities(tool_def, final_enchantments)
local descriptions = self:get_enchanted_descriptions(final_enchantments)
table.insert(data.slots, i, {
level = slot_lvl,
final_enchantments = final_enchantments,
tool_cap_data = tool_cap_data
tool_cap_data = tool_cap_data,
descriptions = descriptions
})
end
return data
end
local function get_hotbar_bg(x, y)
local out = ''
for i = 0, 7, 1 do
out = out .. 'image[' .. x + i .. ',' .. y .. ';1,1;x_enchanting_gui_hb_bg.png]'
end
return out
end
local function get_list_bg(x, y)
local out = ''
for row = 0, 2, 1 do
for i = 0, 7, 1 do
out = out .. 'image[' .. x + i .. ',' .. y + row .. ';1,1;x_enchanting_gui_slot_bg.png]'
end
end
return out
end
local function get_formspec_bg(player_name)
local info = minetest.get_player_information(player_name)
local bg = 'background[5,5;1,1;x_enchanting_gui_formbg.png;true]'
if info.formspec_version > 1 then
bg = 'background9[5,5;1,1;x_enchanting_gui_formbg.png;true;10]'
end
return bg
end
function XEnchanting.get_formspec(self, pos, player_name, data)
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
local inv = minetest.get_meta(pos):get_inventory()
@ -537,21 +837,29 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
local formspec = {
'size[8,9]',
'bgcolor[#080808BB;true]',
'listcolors[#FFFFFF00;#FFFFFF1A;#FFFFFF00;#30434C;#FFF]',
get_formspec_bg(player_name),
'style_type[label;font=mono,bold]',
'style[slot_1,slot_2,slot_3;font=mono,bold;textcolor=#4D413A]',
'label[0, 0;' .. S('Enchant') .. ']',
-- item
'list[nodemeta:' .. spos .. ';item;0, 2.5;1, 1;]',
'image[1,2.5;1,1;x_enchanting_trade_slot.png;]',
'image[0, 2.5;1,1;x_enchanting_gui_cloth_bg.png]',
-- trade
'list[nodemeta:' .. spos .. ';trade;1, 2.5;1, 1;]',
'image[1, 2.5;1,1;x_enchanting_gui_cloth_trade_bg.png]',
-- inventories
'list[current_player;main;0, 4.85;8, 1;]',
'list[current_player;main;0, 6.08;8, 3;8]',
'listring[nodemeta:' .. spos .. ';trade]',
'listring[current_player;main]',
'listring[nodemeta:' .. spos .. ';item]',
'listring[current_player;main]'
'listring[current_player;main]',
}
if default then
formspec[#formspec + 1] = default.get_hotbar_bg(0, 4.85)
end
formspec[#formspec + 1] = get_hotbar_bg(0, 4.85)
formspec[#formspec + 1] = get_list_bg(0, 6.08)
-- data
if data then
@ -561,17 +869,17 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
if inv:get_stack('trade', 1):get_count() >= i then
---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button.png;slot_' .. i .. ';' .. slot.tool_cap_data.enchantments_desc_masked .. ' ' .. minetest.colorize('#FFFF00', S('level') .. ': ' .. slot.level) .. ']'
formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button.png;slot_' .. i .. ';' .. slot.descriptions.enchantments_desc_masked .. minetest.formspec_escape(' [' .. slot.level .. ']') .. ']'
else
---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';' .. slot.tool_cap_data.enchantments_desc_masked .. ' ' .. minetest.colorize('#FFFF00', S('level') .. ': ' .. slot.level) .. ']'
formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';' .. slot.descriptions.enchantments_desc_masked .. minetest.formspec_escape(' [' .. slot.level .. ']') .. ']'
end
formspec[#formspec + 1] = 'image[2.5,' .. -0.5 + i .. ';1,1;x_enchanting_image_trade_' .. i .. '.png;]'
formspec[#formspec + 1] = 'image[2.3,' .. -0.5 + i .. ';1,1;x_enchanting_image_trade_' .. i .. '.png;]'
else
-- disabled buttons
---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';]'
formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';]'
end
end
@ -580,7 +888,7 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
for i = 1, 3, 1 do
-- disabled buttons
---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';]'
formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';]'
end
model_scroll_is_open = false

Binary file not shown.

Binary file not shown.

266
init.lua
View File

@ -7,23 +7,167 @@ 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
elseif starts_with(name, 'animalia:') then
if def.death_func and def.drops then
local prev_death_func = def.death_func
---@param self table
def.death_func = function(self)
local puncher = self._puncher
if not self
or not self.object
or not self.object:get_luaentity()
or not puncher
or not puncher:is_player()
or self._looting_dropped
then
return prev_death_func(self)
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_death_func(self)
end
local pos = self.object:get_pos()
prev_death_func(self)
local death_by_player = puncher and puncher:is_player()
if death_by_player and pos then
local tool_capabilities = wield_stack:get_tool_capabilities()
self._looting_dropped = true
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)
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)
-- Silk Touch
local old_handle_node_drops = minetest.handle_node_drops
function minetest.handle_node_drops(pos, drops, digger)
@ -33,35 +177,115 @@ function minetest.handle_node_drops(pos, drops, digger)
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()
if wield_stack_meta:get_int('is_silk_touch') == 0 then
-- 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
local wield_stack_name = wield_stack:get_name()
local node = minetest.get_node(pos)
local silk_touch_group
-- Silk Touch
local silk_touch = wield_stack_meta:get_float('is_silk_touch')
if minetest.get_item_group(wield_stack_name, 'pickaxe') > 0 then
silk_touch_group = 'cracky'
elseif minetest.get_item_group(wield_stack_name, 'shovel') > 0 then
silk_touch_group = 'crumbly'
elseif minetest.get_item_group(wield_stack_name, 'axe') > 0 then
silk_touch_group = 'choppy'
elseif minetest.get_item_group(wield_stack_name, 'sword') > 0 then
silk_touch_group = 'snappy'
end
if not silk_touch_group
or minetest.get_item_group(node.name, silk_touch_group) == 0
or minetest.get_item_group(node.name, 'no_silktouch') == 1
if silk_touch > 0
and minetest.get_item_group(node.name, 'no_silktouch') == 0
then
return old_handle_node_drops(pos, drops, digger)
-- drop raw item/node
return old_handle_node_drops(pos, { ItemStack(node.name) }, digger)
end
-- drop raw item/node
return old_handle_node_drops(pos, { ItemStack(node.name) }, digger)
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

View File

@ -1,11 +1,17 @@
# textdomain: x_enchanting
Sharpness=
Looting=
Fortune=
Unbreaking=
Efficiency=
Silk Touch=
Curse of Vanishing=
Knockback=
Power=
Punch=
Infinity=
Enchanted=
Enchant=
level=
Enchanting Table=
owned by=
Scroll of Enchantments=

View File

@ -1,11 +1,17 @@
# textdomain: x_enchanting
Sharpness=Ostrosť
Looting=
Fortune=Šťastie
Unbreaking=Nelámavosť
Efficiency=Výkonnosť
Silk Touch=Hodvábny dotyk
Curse of Vanishing=Kliatba zmiznutia
Knockback=Spätný úder
Power=
Punch=
Infinity=
Enchanted=Očarený
Enchant=Očarovať
level=level
Enchanting Table=Čarovný stôl
owned by=vlastník
Scroll of Enchantments=Čarovný Zvitok

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 385 KiB

View File

@ -60,6 +60,7 @@ extract.on('end', () => {
process.exit(1)
}
console.log(`${command} --logpath "${logPath}" --check "${cwd}"`)
console.log(`\n${stdout}`)
if (fs.existsSync('./logs/check.json')) {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -102,9 +102,13 @@ minetest.register_node('x_enchanting:table', {
local inv = minetest.get_meta(pos):get_inventory()
if not inv:is_empty('item') then
if not inv:is_empty('item') and inv:get_stack('item', 1):get_meta():get_int('is_enchanted') ~= 1 then
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data(#bookshelfs, minetest.registered_tools[item_stack:get_name()])
local data = XEnchanting:get_enchantment_data(
clicker,
#bookshelfs,
minetest.registered_tools[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
meta:set_string('formspec', formspec)
@ -333,8 +337,11 @@ minetest.register_node('x_enchanting:table', {
local meta = minetest.get_meta(pos)
local p_name = player:get_player_name()
local inv = meta:get_inventory()
local item_stack = inv:get_stack('item', 1)
local item_stack_meta = item_stack:get_meta()
local is_enchanted = item_stack_meta:get_int('is_enchanted')
if not inv:is_empty('item') then
if not inv:is_empty('item') and is_enchanted == 0 then
-- bookshelfs
local bookshelfs = minetest.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
@ -342,8 +349,11 @@ minetest.register_node('x_enchanting:table', {
{ 'default:bookshelf', 'group:bookshelf' }
)
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data(#bookshelfs, minetest.registered_tools[item_stack:get_name()])
local data = XEnchanting:get_enchantment_data(
player,
#bookshelfs,
minetest.registered_tools[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
meta:set_string('formspec', formspec)
@ -362,8 +372,11 @@ minetest.register_node('x_enchanting:table', {
local meta = minetest.get_meta(pos)
local p_name = player:get_player_name()
local inv = meta:get_inventory()
local item_stack = inv:get_stack('item', 1)
local item_stack_meta = item_stack:get_meta()
local is_enchanted = item_stack_meta:get_int('is_enchanted')
if not inv:is_empty('item') then
if not inv:is_empty('item') and is_enchanted == 0 then
-- bookshelfs
local bookshelfs = minetest.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
@ -371,8 +384,11 @@ minetest.register_node('x_enchanting:table', {
{ 'default:bookshelf', 'group:bookshelf' }
)
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data(#bookshelfs, minetest.registered_tools[item_stack:get_name()])
local data = XEnchanting:get_enchantment_data(
player,
#bookshelfs,
minetest.registered_tools[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
meta:set_string('formspec', formspec)
@ -416,9 +432,8 @@ minetest.register_node('x_enchanting:table', {
end
local trade_stack = inv:get_stack('trade', 1)
local data = XEnchanting.form_context[p_name].data
if trade_stack:get_count() < selected_slot or not data then
if trade_stack:get_count() < selected_slot then
return
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 B

After

Width:  |  Height:  |  Size: 108 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 B

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 B

View File

@ -14,3 +14,4 @@
---@field on_detach_child fun(self: table, child: ObjectRef): nil Function receive a "luaentity" table as `self`. `child`: an `ObjectRef` of the child that detaches
---@field on_detach fun(self: table, parent: ObjectRef|nil): nil Function receive a "luaentity" table as `self`. `parent`: an `ObjectRef` (can be `nil`) from where it got detached. This happens before the parent object is removed from the world.
---@field get_staticdata fun(self: table) Function receive a "luaentity" table as `self`. Should return a string that will be passed to `on_activate` when the object is instantiated the next time.
---@field drops table Custom for mob drops

View File

@ -15,6 +15,7 @@
---@field add_entity fun(pos: Vector, name: string, staticdata?: string): ObjectRef|nil Spawn Lua-defined entity at position. Returns `ObjectRef`, or `nil` if failed.
---@field get_node fun(pos: Vector): NodeDef Returns the node at the given position as table in the format `{name="node_name", param1=0, param2=0}`, returns `{name="ignore", param1=0, param2=0}` for unloaded areas.
---@field registered_nodes table<string, NodeDef|ItemDef> Map of registered node definitions, indexed by name
---@field registered_ores table<string, table> Map of registered ore definitions, indexed by name
---@field after fun(time: number|integer, func: fun(...), ...): JobTable Call the function `func` after `time` seconds, may be fractional. Optional: Variable number of arguments that are passed to `func`.
---@field sound_play fun(spec: SimpleSoundSpec|string, parameters: SoundParamDef, ephemeral?: boolean): any Returns a `handle`. Ephemeral sounds will not return a handle and can't be stopped or faded. It is recommend to use this for short sounds that happen in response to player actions (e.g. door closing).
---@field add_particlespawner fun(particlespawner_definition: ParticlespawnerDef): number|integer Add a `ParticleSpawner`, an object that spawns an amount of particles over `time` seconds. Returns an `id`, and -1 if adding didn't succeed.
@ -87,8 +88,12 @@
---@field place_node fun(pos: Vector, node: SetNodeTable): nil Place node with the same effects that a player would cause
---@field add_particle fun(def: ParticleDef): nil
---@field registered_tools table<string, ItemDef> Map of registered tool definitions, indexed by name
---@field registered_entities table<string, EntityDef> Map of registered entity definitions, indexed by name
---@field has_feature fun(args: table<string, boolean> | string): boolean | table returns `boolean, missing_features`, `arg`: string or table in format `{foo=true, bar=true}`, `missing_features`: `{foo=true, bar=true}`
---@field handle_node_drops fun(pos: Vector, drops: string[], digger: ObjectRef) `drops`: list of itemstrings. Handles drops from nodes after digging: Default action is to put them into digger's inventory. Can be overridden to get different functionality (e.g. dropping items on ground)
---@field register_on_dieplayer fun(func: fun(player: ObjectRef, reason?: string)): nil Called when a player dies. `reason`: a PlayerHPChangeReason table, see register_on_player_hpchange
---@field register_on_player_hpchange fun(func: fun(player, hp_change, reason), modifier): number Called when the player gets damaged or healed. `player`: ObjectRef of the player. `hp_change`: the amount of change. Negative when it is damage.. `reason`: a PlayerHPChangeReason table.. The `type` field will have one of the following values: `set_hp`: A mod or the engine called `set_hp` without giving a type - use this for custom damage types.. `punch`: Was punched. `reason.object` will hold the puncher, or nil if none. `fall`, `node_damage`: `damage_per_second` from a neighbouring node. `reason.node` will hold the node name or nil. `drown` `respawn`. Any of the above types may have additional fields from mods. `reason.from` will be `mod` or `engine`. `modifier`: when true, the function should return the actual `hp_change`. Note: modifiers only get a temporary `hp_change` that can be modified by later modifiers. Modifiers can return true as a second argument to stop the execution of further functions. Non-modifiers receive the final HP change calculated by the modifiers.
---@field get_player_information fun(player_name: string): table Table containing information about a player
---Minetest settings
---@class MinetestSettings

View File

@ -5,6 +5,8 @@
---@class MtgFarming
---@field hoe_on_use fun(itemstack: ItemStack, user: ObjectRef, pointed_thing: PointedThingDef, uses: number): ItemStack | nil
---@field place_seed fun(itemstack: ItemStack, placer: ObjectRef, pointed_thing: PointedThingDef, plantname: string): ItemStack Seed placement
---@field grow_plant fun(pos: Vector, elapsed: number): nil
---@field register_plant fun(name: string, def: table): nil
----Node definition. Used by `minetest.register_node`.
---@class NodeDefMtgFarming

View File

@ -27,6 +27,7 @@
---@field on_destruct fun(pos: Vector) Node destructor; called before removing node. Not called for bulk node placement. default: nil
---@field on_blast fun(pos: Vector, intensity?: number): nil intensity: 1.0 = mid range of regular TNT. If defined, called when an explosion touches the node, instead of removing the node.
---@field on_timer fun(pos: Vector, elapsed: number): boolean | nil default: nil, called by NodeTimers, see minetest.get_node_timer and NodeTimerRef. elapsed is the total time passed since the timer was started. return true to run the timer for another cycle with the same timeout value.
---@field _next_state string Only for x_farming composter
---Textures of node; +Y, -Y, +X, -X, +Z, -Z. List can be shortened to needed length.
---@class NodeTilesDef

View File

@ -6,3 +6,4 @@
---Table helpers
---@class TableAbstract
---@field copy fun(table: table): table returns a deep copy of `table`
---@field indexof fun(list: table, value: any): number returns the smallest numerical index containing the value `val` in the table `list`. Non-numerical indices are ignored. If `val` could not be found, `-1` is returned. `list` must not have negative indices.

View File

@ -1,8 +0,0 @@
---@diagnostic disable: codestyle-check
---Base class Unified Inventory
---@class UnifiedInventory
---@field set_inventory_formspec fun(player: ObjectRef, formspecname: string): nil
---@field register_button fun(name: string, def: table): nil
---@field single_slot fun(x: number, y: number): nil
---@field register_page fun(name: string, def: table): nil
---@field style_full table

View File

@ -4,55 +4,51 @@
---@class XEnchanting
---@field tools_enchantability table<string, number> Add enchantability to default tools. key/value = tool_name/enchantibility value
---@field roman_numbers table<number, string> Convert Arabic numbers to Roman numbers
---@field enchantment_defs table<'sharpness' | 'fortune' | 'unbreaking' | 'efficiency' | 'silk_touch', EnchantmentDef>
---@field enchantment_defs table<'sharpness' | 'fortune' | 'unbreaking' | 'efficiency' | 'silk_touch' | 'curse_of_vanishing' | 'knockback', EnchantmentDef>
---@field has_tool_group fun(self: XEnchanting, name: string): string | boolean Check if tool has one of the known tool groups, returns `false` otherwise.
---@field set_tool_enchantability fun(self: XEnchanting, tool_def: ItemDef): nil Sets `enchantibility` group and values from base table (`XEnchanting.tools_enchantability`) to all registered tools (atching known group names). If tool has already `enchantibility` group defined it will take the defined value insted.
---@field get_enchanted_tool_capabilities fun(self: XEnchanting, tool_def: ItemDef, enchantments: Enchantments[]): GetEnchantedToolCapabilitiesReturn Applies enchantments to item tool capabilities.
---@field get_enchanted_tool_capabilities fun(self: XEnchanting, tool_def: ItemDef, enchantments: Enchantment[]): ToolCapabilitiesDef Applies enchantments to item tool capabilities.
---@field set_enchanted_tool fun(self: XEnchanting, pos: Vector, itemstack: ItemStack, level: number, player_name: string): nil Set choosen enchantment and its modified tool capabilities to itemstack and `item` inventory. This will also get new `randomseed`.
---@field get_enchantment_data fun(self: XEnchanting, nr_of_bookshelfs: number, tool_def: ItemDef): EnchantmentData Algoritm to get aplicable random enchantments.
---@field get_formspec fun(self: XEnchanting, pos: Vector, player_name: string, data?: EnchantmentData): string Builds and returns `formspec` string
---@field get_enchantment_data fun(self: XEnchanting, player: ObjectRef, nr_of_bookshelfs: number, tool_def: ItemDef): EnchantmentData Algoritm to get aplicable random enchantments.
---@field get_formspec fun(self: XEnchanting, pos: Vector, player_name: string, data: EnchantmentData | nil): string Builds and returns `formspec` string
---@field form_context table<string, FormContextDef> Additional form data for player.
---@field scroll_animations table<string, table> Parameters for `ObjectRef` `set_animation` method
---@field player_seeds table<string, number | integer>
---@field registered_ores table<string, boolean> Table with registered ores, `key` ore name
---@field settings {["x_enchanting_small_formspec"]: boolean}
---Enchantment definition
---@class EnchantmentDef
---@field name string Readable name of the enchantment
---@field final_level_range table<number, number[]> Level range
---@field level_def table<number, number> Level bonus
---@field level_def table<number, number> Level bonus. Value will be set in item meta as float.
---@field weight number Enchantment weight
---@field randomseed number Math random seed. Will change after item was enchanted.
---@field form_context table<string, FormContextDef> Additional form data for player.
---@field scroll_animations table<string, table> Parameters for `ObjectRef` `set_animation` method
---@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.
---@field incompatible string[] | nil List of incompatible enchantments
---Form context
---@class FormContextDef
---@field pos Vector Formspec/node form position
---@field data EnchantmentDataSlot Enchantment data
---@class GetEnchantedToolCapabilitiesReturn
---@field tool_capabilities ToolCapabilitiesDef
---@field enchantments_desc string
---@field enchantments_desc_masked string
---@field data EnchantmentData | nil Enchantment data
---@class EnchantmentData
---@field slots EnchantmentDataSlot[]
---@class Enchantments
---@field id string
---@field value number
---@field level number
---@class ToolCapData
---@field enchantments_desc string Masket description before enchanting
---@field enchantments_desc_masked string Description added to item description definition after enchanting
---@field tool_capabilities ToolCapabilitiesDef Modified tool capabilities with applied enchantment
---@class Enchantment
---@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.
---@field incompatible string[] | nil List of incompatible enchantments
---@class EnchantmentDataSlot
---@field final_enchantments Enchantments[]
---@field tool_cap_data ToolCapData
---@field level number
---@field final_enchantments Enchantment[]
---@field tool_cap_data ToolCapabilitiesDef
---@field descriptions {["enchantments_desc"]: string, ["enchantments_desc_masked"]: string }