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

10 Commits

26 changed files with 533 additions and 60 deletions

106
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,30 +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.
### Knockback
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
@ -130,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

310
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'),
@ -125,7 +158,13 @@ XEnchanting = {
[1] = 1
},
weight = 1,
secondary = true
secondary = true,
groups = {
'pickaxe',
'shovel',
'axe'
},
incompatible = { 'fortune' }
},
curse_of_vanishing = {
name = S('Curse of Vanishing'),
@ -136,7 +175,9 @@ XEnchanting = {
[1] = 1
},
weight = 1,
secondary = true
secondary = true,
-- all applicable
groups = nil
},
knockback = {
name = S('Knockback'),
@ -149,7 +190,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 = {},
@ -159,7 +263,8 @@ XEnchanting = {
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
@ -192,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
@ -318,7 +428,7 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
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
@ -363,7 +473,7 @@ function XEnchanting.get_enchanted_descriptions(self, enchantments)
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 {
enchantments_desc = enchantments_desc,
@ -390,16 +500,22 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
end
local stack_meta = itemstack:get_meta()
---@type table<string, {["value"]: number}>
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)
@ -418,6 +534,58 @@ 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, player, nr_of_bookshelfs, tool_def)
@ -433,6 +601,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 +661,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
@ -495,7 +682,8 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
id = enchantment_name,
value = enchantment_def.level_def[level],
level = level,
secondary = enchantment_def.secondary
secondary = enchantment_def.secondary,
incompatible = enchantment_def.incompatible
})
end
end
@ -513,10 +701,11 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
total_weight = total_weight + self.enchantment_defs[enchantment.id].weight
end
-- Pick a random integer in the half range [0; total_weight] as a number `rand_weight`
-- 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 = {}
for _, enchantment in pairs(possible_enchantments) do
@ -533,22 +722,40 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
local rand_ench = 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
table.insert(final_enchantments, rand_ench)
else
local probability = (probability_level + 1) / 50
table.remove(possible_enchantments, rand_ench_idx)
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()
@ -586,6 +793,39 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
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()
@ -597,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
@ -621,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.descriptions.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.descriptions.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
@ -640,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.

137
init.lua
View File

@ -7,12 +7,96 @@ 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)
@ -27,7 +111,6 @@ 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)
@ -37,29 +120,63 @@ 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_float('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 node = minetest.get_node(pos)
-- Silk Touch
local silk_touch = wield_stack_meta:get_float('is_silk_touch')
if minetest.get_item_group(node.name, 'no_silktouch') == 1 then
return old_handle_node_drops(pos, drops, digger)
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
-- 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]]
-- Curse of Vanishing
local player_inventory_lists = { 'main', 'craft' }
for _, list_name in ipairs(player_inventory_lists) do
@ -72,8 +189,6 @@ minetest.register_on_player_hpchange(function(player, hp_change, reason)
player_inv:set_stack(list_name, i, ItemStack(''))
end
end
player_inv:set_list(list_name, {})
end
end
end

View File

@ -1,14 +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,14 +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

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,11 +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, ObjectRef> Map of registered entity 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

@ -14,6 +14,8 @@
---@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
@ -23,6 +25,8 @@
---@field level_def table<number, number> 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.
---@field incompatible string[] | nil List of incompatible enchantments
---Form context
@ -36,10 +40,11 @@
---@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.
---@field incompatible string[] | nil List of incompatible enchantments
---@class EnchantmentDataSlot