Compare commits

...

42 Commits

Author SHA1 Message Date
673922371c Add enchanting mod support 2025-04-19 17:13:34 +02:00
0d589e69b3 Update luacheck 2025-04-07 00:38:49 -04:00
8935043ed4 Update license year 2025-04-07 00:25:32 -04:00
e2cf02d026 Update CI script 2025-04-07 00:24:55 -04:00
0365b8043b Cleanup screenshot files 2025-04-07 00:21:38 -04:00
ba1d30909b Rename minetest API convention to core 2025-04-07 00:19:12 -04:00
82973b6462 Add API method for getting glint texture 2025-04-07 00:18:53 -04:00
ecdf5a83d1 fix typo 2024-01-30 11:56:41 -05:00
af26c6ed76 Add grindstone 2023-12-14 16:48:02 -05:00
3ee6bb7b80 Update readme and cdb 2023-12-14 16:34:33 -05:00
84df66325e Add grindstone 2023-12-14 16:28:48 -05:00
737e2f3cba Add grindstone 2023-12-14 16:27:25 -05:00
693ace14c2 Add tool texture overlay when enchanted 2023-12-07 10:47:30 -05:00
f4ed507897 Add missing use_texture_alpha 2023-04-01 14:40:34 -04:00
01703cb0a3 3 Add support for item_drop mod 2023-02-26 15:45:25 +00:00
c5c53b63ee 3 Fix indentation 2023-02-26 10:42:06 -05:00
4a0b45f472 3 Add support for item_drop mod 2023-02-26 10:34:18 -05:00
172eed1768 Disable Lua Diagnostics in pipeline 2023-02-23 21:57:21 -05:00
745708505f Prevent double enchantments and adding incompatible enchantments 2023-02-23 21:56:47 -05:00
8090e23daf Update License Year 2023-02-23 21:19:03 -05:00
46633e5b44 Update License Year 2023-02-23 21:18:58 -05:00
48d215b08e add gitattributes 2022-12-05 15:14:51 -05:00
f8f40d61a2 update licensing 2022-11-28 16:26:42 -05:00
20b53db32d update licensing 2022-11-28 16:20:00 -05:00
f891170e45 Update readme and media licensing 2022-11-22 09:07:00 -05:00
1771a773e5 update readme 2022-11-16 16:36:33 -05:00
92cd1dea01 Fix distance measure for scroll 2022-11-16 16:26:46 -05:00
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
62 changed files with 2588 additions and 418 deletions

View File

@ -13,5 +13,6 @@
"media_license": "CC-BY-SA-4.0",
"repo": "https://bitbucket.org/minetest_gamers/x_enchanting/src/master/",
"issue_tracker": "https://bitbucket.org/minetest_gamers/x_enchanting/issues?status=new&status=open",
"video_url": "https://youtu.be/JXF-GrQ9Uxs?si=dsgxDMXtm90ovE3D",
"forums": 28861
}

15
.gitattributes vendored Normal file
View File

@ -0,0 +1,15 @@
# Creating an archive
.* export-ignore
assets export-ignore
scripts export-ignore
bin export-ignore
docs export-ignore
types export-ignore
*.zip export-ignore
bitbucket-pipelines.yml export-ignore
package.json export-ignore
package-lock.json export-ignore
screenshot*.png export-ignore
i18n.py export-ignore
config.ld export-ignore

View File

@ -3,27 +3,26 @@ allow_defined_top = true
max_line_length = false
exclude_files = {
'./scripts',
'./bin',
'./logs',
'./node_modules',
'./sounds',
'./textures',
'./models',
'./docs',
'./locale',
'./types',
"./scripts",
"./bin",
"./logs",
"./node_modules",
"./sounds",
"./textures",
"./models",
"./docs",
"./locale",
"./types",
}
globals = {
'minetest',
'XEnchanting'
"core",
"XEnchanting"
}
read_globals = {
"DIR_DELIM", "INIT",
"core",
"dump", "dump2",
"Raycast",

View File

@ -1,8 +1,7 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@ -55,7 +54,7 @@ modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
@ -111,7 +110,7 @@ modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@ -158,7 +157,7 @@ Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
@ -216,7 +215,7 @@ instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
@ -267,7 +266,7 @@ Library will still fall under Section 6.)
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
@ -329,7 +328,7 @@ restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
@ -370,7 +369,7 @@ subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
@ -422,7 +421,7 @@ conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
@ -455,48 +454,103 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
### Textures
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
**CC-BY-SA-4.0, Pixel Perfection by XSSheep**, https://minecraft.curseforge.com/projects/pixel-perfection-freshly-updated
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
- x_enchanting_symbol_1.png
- x_enchanting_symbol_2.png
- x_enchanting_symbol_3.png
- x_enchanting_symbol_4.png
- x_enchanting_symbol_5.png
- x_enchanting_symbol_6.png
- x_enchanting_symbol_7.png
- x_enchanting_symbol_8.png
- x_enchanting_symbol_9.png
- x_enchanting_symbol_10.png
- x_enchanting_symbol_11.png
- x_enchanting_symbol_12.png
- x_enchanting_symbol_13.png
- x_enchanting_symbol_14.png
- x_enchanting_symbol_15.png
- x_enchanting_symbol_16.png
- x_enchanting_symbol_17.png
- x_enchanting_symbol_18.png
- x_enchanting_symbol_19.png
- x_enchanting_symbol_20.png
- x_enchanting_symbol_21.png
- x_enchanting_symbol_22.png
- x_enchanting_symbol_23.png
- x_enchanting_symbol_24.png
- x_enchanting_symbol_25.png
- x_enchanting_symbol_26.png
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
**CC-BY-SA-4.0, by SaKeL**
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
- x_enchanting_image_button_disabled.png
- x_enchanting_image_button.png
- x_enchanting_image_trade_1.png -- Derived from a texture by VanessaE (CC BY-SA 3.0)
- x_enchanting_image_trade_2.png -- Derived from a texture by VanessaE (CC BY-SA 3.0)
- x_enchanting_image_trade_3.png -- Derived from a texture by VanessaE (CC BY-SA 3.0)
- x_enchanting_scroll_handles_mesh.png -- Derived from a textures by paramat (CC BY-SA 3.0) and TumeniNodes (CC BY-SA 3.0)
- 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_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)
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
### Models
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**CC-BY-SA-4.0, by SaKeL**
Also add information on how to contact you by electronic and paper mail.
- x_enchanting_scroll.b3d
- x_enchanting_table.obj
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
### Sounds
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
**CC-BY-3.0, Kostas17**, https://freesound.org
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
- x_enchanting_enchant.ogg
That's all there is to it!
**CC0-1.0, Zeinel**, https://freesound.org
- x_enchanting_scroll.1.ogg
- x_enchanting_scroll.2.ogg
- x_enchanting_scroll.3.ogg
- x_enchanting_scroll.4.ogg
- x_enchanting_scroll.5.ogg
- x_enchanting_scroll.6.ogg
- x_enchanting_scroll.7.ogg
- x_enchanting_scroll.8.ogg
- x_enchanting_scroll.9.ogg
**CC0-1.0, by Fission9**, https://freesound.org
- x_enchanting_wood_footstep.1.ogg
- x_enchanting_wood_footstep.2.ogg
- x_enchanting_wood_footstep.3.ogg
- x_enchanting_wood_footstep.4.ogg
- x_enchanting_wood_footstep.5.ogg
- x_enchanting_wood_footstep.6.ogg
**CC0-1.0, by igroglaz**, https://freesound.org
- x_enchanting_wood_hit.1.ogg
- x_enchanting_wood_hit.2.ogg
- x_enchanting_wood_hit.3.ogg
- x_enchanting_wood_hit.4.ogg
- x_enchanting_wood_hit.5.ogg
- x_enchanting_wood_hit.6.ogg
**CC-BY-4.0, by RICHERlandTV**, https://freesound.org
- x_enchanting_wood_place.1.ogg
- x_enchanting_wood_place.2.ogg
- x_enchanting_wood_place.3.ogg
**CC-BY-4.0, by tim.kahn**, https://freesound.org/people/tim.kahn/sounds/35829/
- x_enchanting_disenchant.ogg

185
README.md
View File

@ -6,8 +6,10 @@ Adds Enchanting Mechanics and API.
## Features
* does not add new tools/items, it's using MT API to change existing items instead
* adds enchanting table
* supports all registered tools with known tool groups: pickaxe, shovel, axe, sword, e.g. `groups = {pickaxe = 1}`
* supports 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}`
@ -15,8 +17,8 @@ Adds Enchanting Mechanics and API.
* enchanted items have detailed enchantments description/short description
* mesh node model
* mesh entity model and animations
NOTE: item inventory/wield image will not be adjusted until this feature is supported by MT engine, see: https://github.com/minetest/minetest/issues/5686
* tool texture will have enchanted glint
* adds grind stone to remove enchantments (excluding curses)
## How To
@ -50,22 +52,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, animalia
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
@ -73,82 +178,10 @@ Causes certain blocks to drop themselves as items instead of their usual drops w
## Optional Dependencies
- xdecor (adjusts conflicting recipe)
- item_drop (make sure fortune will execute before item_drop mod overrides `handle_node_drops`)
## License:
### Code
GNU Lesser General Public License v2.1 or later (see included LICENSE file)
### Textures
**CC BY-SA 4.0, Pixel Perfection by XSSheep**, https://minecraft.curseforge.com/projects/pixel-perfection-freshly-updated
- x_enchanting_symbol_1.png
- x_enchanting_symbol_2.png
- x_enchanting_symbol_3.png
- x_enchanting_symbol_4.png
- x_enchanting_symbol_5.png
- x_enchanting_symbol_6.png
- x_enchanting_symbol_7.png
- x_enchanting_symbol_8.png
- x_enchanting_symbol_9.png
- x_enchanting_symbol_10.png
- x_enchanting_symbol_11.png
- x_enchanting_symbol_12.png
- x_enchanting_symbol_13.png
- x_enchanting_symbol_14.png
- x_enchanting_symbol_15.png
- x_enchanting_symbol_16.png
- x_enchanting_symbol_17.png
- x_enchanting_symbol_18.png
- x_enchanting_symbol_19.png
- x_enchanting_symbol_20.png
- x_enchanting_symbol_21.png
- x_enchanting_symbol_22.png
- x_enchanting_symbol_23.png
- x_enchanting_symbol_24.png
- x_enchanting_symbol_25.png
- x_enchanting_symbol_26.png
**LGPL-2.1-or-later, by SaKeL**
- x_enchanting_image_button_disabled.png
- x_enchanting_image_button.png
- x_enchanting_image_trade_1.png -- Derived from a texture by VanessaE (CC BY-SA 3.0)
- x_enchanting_image_trade_2.png -- Derived from a texture by VanessaE (CC BY-SA 3.0)
- x_enchanting_image_trade_3.png -- Derived from a texture by VanessaE (CC BY-SA 3.0)
- x_enchanting_scroll_handles_mesh.png -- Derived from a textures by paramat (CC BY-SA 3.0) and TumeniNodes (CC BY-SA 3.0)
- 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
### Models
**LGPL-2.1-or-later, by SaKeL**
- x_enchanting_scroll.b3d
- x_enchanting_table.obj
### Sounds
**CC-BY-3.0, Kostas17**, https://freesound.org
- x_enchanting_enchant.ogg
**CC0-1.0, Zeinel**, https://freesound.org
- x_enchanting_scroll.1.ogg
- x_enchanting_scroll.2.ogg
- x_enchanting_scroll.3.ogg
- x_enchanting_scroll.4.ogg
- x_enchanting_scroll.5.ogg
- x_enchanting_scroll.6.ogg
- x_enchanting_scroll.7.ogg
- x_enchanting_scroll.8.ogg
- x_enchanting_scroll.9.ogg
## Installation
see: https://wiki.minetest.net/Installing_Mods

665
api.lua
View File

@ -1,6 +1,22 @@
default = minetest.global_exists('default') and default --[[@as MtgDefault]]
--[[
X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2025 SaKeL
local S = minetest.get_translator(minetest.get_current_modname())
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to juraj.vajda@gmail.com
--]]
local S = core.get_translator(core.get_current_modname())
---@type XEnchanting
XEnchanting = {
@ -62,7 +78,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 +116,13 @@ XEnchanting = {
[2] = 2,
[3] = 3
},
weight = 2
weight = 2,
groups = {
'pickaxe',
'shovel',
'axe'
},
incompatible = { 'silk_touch' }
},
unbreaking = {
name = S('Unbreaking'),
@ -94,7 +138,9 @@ XEnchanting = {
[2] = 200,
[3] = 300
},
weight = 5
weight = 5,
-- all applicable
groups = nil
},
efficiency = {
name = S('Efficiency'),
@ -114,7 +160,12 @@ XEnchanting = {
[4] = 40,
[5] = 45,
},
weight = 10
weight = 10,
groups = {
'pickaxe',
'shovel',
'axe'
}
},
silk_touch = {
name = S('Silk Touch'),
@ -122,19 +173,117 @@ 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,
cursed = true
},
knockback = {
name = S('Knockback'),
final_level_range = {
[1] = { 5, 55 },
[2] = { 25, 75 }
},
-- increase %
level_def = {
[1] = 105,
[2] = 190
},
weight = 5,
groups = {
'sword'
}
},
randomseed = os.time(),
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 = {},
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,40 +295,69 @@ 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
---Find element v of t satisfying f(v)
local function tableFind(t, f)
for _, v in ipairs(t) do
if f(v) then
return v
end
end
return nil
end
---@diagnostic disable-next-line: unused-local
function XEnchanting.has_tool_group(self, name)
if minetest.get_item_group(name, 'pickaxe') > 0 then
if core.get_item_group(name, 'pickaxe') > 0 then
return 'pickaxe'
elseif minetest.get_item_group(name, 'shovel') > 0 then
elseif core.get_item_group(name, 'shovel') > 0 then
return 'shovel'
elseif minetest.get_item_group(name, 'axe') > 0 then
elseif core.get_item_group(name, 'axe') > 0 then
return 'axe'
elseif minetest.get_item_group(name, 'sword') > 0 then
elseif core.get_item_group(name, 'sword') > 0 then
return 'sword'
elseif core.get_item_group(name, 'bow') > 0 then
return 'bow'
end
return false
end
function XEnchanting.set_tool_enchantability(self, tool_def)
if core.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
minetest.override_item(tool_def.name, {
core.override_item(tool_def.name, {
groups = mergeTables(tool_def.groups, { enchantability = _enchantability })
})
end
---@diagnostic disable-next-line: unused-local
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 = {}
---@diagnostic disable-next-line: unused-local
for i, enchantment in ipairs(enchantments) do
-- Efficiency
if enchantment.id == 'efficiency' then
@ -246,18 +424,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 +448,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,88 +458,119 @@ 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
---@diagnostic disable-next-line: unused-local
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 = {}
---@diagnostic disable-next-line: unused-local
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
end
-- Silk Touch
if enchantment.id == 'silk_touch' then
else
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
end
if #enchantments_desc_masked == 0 then
if #enchantments_desc_masked == 0 and not enchantment.secondary 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'))
enchantments_desc = '\n' .. core.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
}
end
function XEnchanting.get_glint_texture_modifier(self, image)
return image .. '^((' .. image .. '^[contrast:127:127)^[mask:x_enchanting_glint.png^[opacity:80)'
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()]
local node_meta = minetest.get_meta(pos)
local inv = core.get_meta(pos):get_inventory()
local tool_def = core.registered_tools[itemstack:get_name()]
local node_meta = core.get_meta(pos)
if not tool_def then
return
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
---@diagnostic disable-next-line: unused-local
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', core.serialize(final_enchantments_meta))
if is_silk_touch > 0 then
stack_meta:set_int('is_silk_touch', 1)
if tool_def and tool_def.inventory_image and tool_def.inventory_image ~= '' then
stack_meta:set_string('inventory_image', tool_def.inventory_image .. '^((' .. tool_def.inventory_image .. '^[contrast:127:127)^[mask:x_enchanting_glint.png^[opacity:80)')
end
if tool_def and tool_def.inventory_overlay and tool_def.inventory_overlay ~= '' then
stack_meta:set_string('inventory_overlay', tool_def.inventory_overlay .. '^((' .. tool_def.inventory_overlay .. '^[contrast:127:127)^[mask:x_enchanting_glint.png^[opacity:80)')
end
if tool_def and tool_def.wield_image and tool_def.wield_image ~= '' then
stack_meta:set_string('wield_image', tool_def.wield_image .. '^((' .. tool_def.wield_image .. '^[contrast:127:127)^[mask:x_enchanting_glint.png^[opacity:80)')
end
if tool_def and tool_def.wield_overlay and tool_def.wield_overlay ~= '' then
stack_meta:set_string('wield_overlay', tool_def.wield_overlay .. '^((' .. tool_def.wield_overlay .. '^[contrast:127:127)^[mask:x_enchanting_glint.png^[opacity:80)')
end
inv:set_stack('item', 1, itemstack)
@ -395,19 +580,74 @@ 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)
minetest.sound_play('x_enchanting_enchant', {
core.sound_play('x_enchanting_enchant', {
gain = 0.3,
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 core.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
core.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 +657,31 @@ 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
---@diagnostic disable-next-line: unused-local
for i, group in ipairs(enchantment_def.groups) do
if core.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)
@ -435,14 +694,12 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
local chosen_enchantment_level = slot_lvl
-- Applying modifiers to the enchantment level
local enchantability = minetest.get_item_group(tool_def.name, 'enchantability')
local enchantability = core.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 +712,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 +731,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,27 +749,94 @@ 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
-- calculate total weight
---@diagnostic disable-next-line: unused-local
for j, enchantment in ipairs(possible_enchantments) do
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)
rand_weight = rand_weight - self.enchantment_defs[rand_ench.id].weight
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
local alreadyInTable = tableFind(final_enchantments, function(value)
return value.id == rand_ench.id
end)
if not alreadyInTable then
table.insert(final_enchantments, rand_ench)
end
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
-- If `rand_weight` is now negative, select the current enchantment and stop.
if rand_weight < 0 then
break
@ -515,20 +844,56 @@ 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, bg_img)
local bg_image = bg_img and bg_img or 'x_enchanting_gui_formbg.png'
local info = core.get_player_information(player_name)
local bg = 'background[5,5;1,1;' .. bg_image .. ';true]'
if info.formspec_version > 1 then
bg = 'background9[5,5;1,1;' .. bg_image .. ';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()
local inv = core.get_meta(pos):get_inventory()
---@diagnostic disable-next-line: codestyle-check
local model_scroll_open = 'model[0,0;2,3;x_enchanting_table;x_enchanting_scroll.b3d;x_enchanting_scroll_mesh.png,x_enchanting_scroll_handles_mesh.png,x_enchanting_scroll_mesh.png;89,0;false;false;' .. self.scroll_animations.scroll_open_idle[1].x .. ',' .. self.scroll_animations.scroll_open_idle[1].y .. ';0]'
---@diagnostic disable-next-line: codestyle-check
@ -537,21 +902,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 +934,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 .. core.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 .. core.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 +953,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
@ -599,3 +972,77 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
return table.concat(formspec, '')
end
function XEnchanting.get_grindstone_formspec(self, pos, player_name, data)
local _data = data or {}
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
local model = 'model[0,0;2,3;x_enchanting_grindstone;x_enchanting_grindstone.obj;x_enchanting_grindstone_mesh.png;0,0;true;false;]'
local formspec = {}
formspec[#formspec + 1] = 'size[8,9]'
formspec[#formspec + 1] = 'bgcolor[#080808BB;true]'
formspec[#formspec + 1] = 'listcolors[#FFFFFF00;#FFFFFF1A;#FFFFFF00;#30434C;#FFF]'
formspec[#formspec + 1] = get_formspec_bg(player_name)
formspec[#formspec + 1] = 'style_type[label;font=mono,bold]'
formspec[#formspec + 1] = 'style[slot_1,slot_2,slot_3;font=mono,bold;textcolor=#4D413A]'
formspec[#formspec + 1] = 'label[0, 0;' .. S('Disenchant') .. ']'
-- item
formspec[#formspec + 1] = 'list[nodemeta:' .. spos .. ';item;0.5, 2.5;1, 1;]'
formspec[#formspec + 1] = 'image[0.5, 2.5;1,1;x_enchanting_gui_cloth_bg.png]'
-- result
if _data.result_disabled then
formspec[#formspec + 1] = 'image[4.75, 1.25;1.5,1.5;x_enchanting_gui_cloth_bg_corssed.png]'
else
formspec[#formspec + 1] = 'list[nodemeta:' .. spos .. ';result;5, 1.5;1, 1;]'
formspec[#formspec + 1] = 'image[5, 1.5;1,1;x_enchanting_gui_cloth_bg.png]'
end
-- inventories
formspec[#formspec + 1] = 'list[current_player;main;0, 4.85;8, 1;]'
formspec[#formspec + 1] = 'list[current_player;main;0, 6.08;8, 3;8]'
formspec[#formspec + 1] = 'listring[nodemeta:' .. spos .. ';result]'
formspec[#formspec + 1] = 'listring[current_player;main]'
formspec[#formspec + 1] = 'listring[nodemeta:' .. spos .. ';item]'
formspec[#formspec + 1] = 'listring[current_player;main]'
formspec[#formspec + 1] = get_hotbar_bg(0, 4.85)
formspec[#formspec + 1] = get_list_bg(0, 6.08)
formspec[#formspec + 1] = model
return table.concat(formspec, '')
end
function XEnchanting.has_all_cursed_ench(self, itemstack)
if itemstack:is_empty() then
return false
end
local item_stack_meta = itemstack:get_meta()
local stack_enchantment_data = core.deserialize(item_stack_meta:get_string('x_enchanting')) or {}
local stack_enchantment_data_length = get_table_length(stack_enchantment_data)
local cursed_ench = 0
-- Get cursed enchantments
for id, value in pairs(stack_enchantment_data) do
local ench_def = self.enchantment_defs[id]
if ench_def.cursed then
cursed_ench = cursed_ench + 1
end
end
return cursed_ench == stack_enchantment_data_length
end
--
-- Sounds
--
function XEnchanting.node_sound_wood_defaults(table)
table = table or {}
table.footstep = table.footstep or { name = 'x_enchanting_wood_footstep', gain = 0.15 }
table.dig = table.dig or { name = 'x_enchanting_wood_hit', gain = 0.5 }
table.dug = table.dug or { name = 'x_enchanting_wood_place', gain = 0.1 }
table.place = table.place or { name = 'x_enchanting_wood_place', gain = 0.15 }
return table
end

BIN
assets/promo_scene.blend Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -31,36 +31,6 @@ pipelines:
script:
- nvm use v17.2.0
- npm run lua-diagnostics
branches:
master:
- step:
name: Install Node Dependencies
caches:
- node-modules
- npm
- nvm
script:
- nvm install v17.2.0
- npm i -g npm@8
- npm ci
- parallel:
- step:
name: Lua Check
script:
- apt-get update
- apt-get -y install lua5.1
- apt-get -y install luarocks
- luarocks install luacheck
- luacheck .
- step:
name: Lua Diagnostics
caches:
- node-modules
- npm
- nvm
script:
- nvm use v17.2.0
- npm run lua-diagnostics
tags:
"*":
- step:
@ -73,7 +43,7 @@ pipelines:
- nvm install v17.2.0
- npm i -g npm@8
- npm ci
- parallel:
# - parallel:
- step:
name: Lua Check
script:
@ -82,15 +52,15 @@ pipelines:
- apt-get -y install luarocks
- luarocks install luacheck
- luacheck .
- step:
name: Lua Diagnostics
caches:
- node-modules
- npm
- nvm
script:
- nvm use v17.2.0
- npm run lua-diagnostics
# - step:
# name: Lua Diagnostics
# caches:
# - node-modules
# - npm
# - nvm
# script:
# - nvm use v17.2.0
# - npm run lua-diagnostics
- step:
name: Deploy to ContentDB
caches:

380
grindstone.lua Normal file
View File

@ -0,0 +1,380 @@
---@diagnostic disable
--[[
X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2025 SaKeL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to juraj.vajda@gmail.com
--]]
local S = core.get_translator(core.get_current_modname())
----
--- Grindstone Node
----
core.register_node('x_enchanting:grindstone', {
description = S('Grindstone'),
short_description = S('Grindstone'),
drawtype = 'mesh',
mesh = 'x_enchanting_grindstone.obj',
tiles = { 'x_enchanting_grindstone_mesh.png' },
inventory_image = 'x_enchanting_grindstone_item.png',
use_texture_alpha = 'clip',
paramtype = 'light',
paramtype2 = 'facedir',
walkable = true,
wield_scale = { x = 2, y = 2, z = 2 },
selection_box = {
type = 'fixed',
fixed = { -1 / 2, -1 / 2, -1 / 2, 1 / 2, 1 / 2 - 1 / 16, 1 / 2 }
},
collision_box = {
type = 'fixed',
fixed = { -1 / 2, -1 / 2, -1 / 2, 1 / 2, 1 / 2 - 1 / 16, 1 / 2 }
},
sounds = XEnchanting.node_sound_wood_defaults(),
is_ground_content = false,
groups = { choppy = 2, oddly_breakable_by_hand = 2 },
stack_max = 1,
mod_origin = 'x_enchanting',
---@param pos Vector
on_construct = function(pos)
local meta = core.get_meta(pos)
local inv = meta:get_inventory()
meta:set_string('infotext', S('Grindstone'))
meta:set_string('owner', '')
inv:set_size('item', 1)
inv:set_size('result', 1)
end,
---@param pos Vector
---@param placer ObjectRef | nil
---@param itemstack ItemStack
---@param pointed_thing PointedThingDef
---@diagnostic disable-next-line: unused-local
after_place_node = function(pos, placer, itemstack, pointed_thing)
local meta = core.get_meta(pos)
if not placer then
return
end
local player_name = placer:get_player_name()
meta:set_string('owner', player_name)
meta:set_string('infotext', S('Grindstone') .. ' (' .. S('owned by') .. ' ' .. player_name .. ')')
local formspec = XEnchanting:get_grindstone_formspec(pos, player_name)
meta:set_string('formspec', formspec)
end,
---@param pos Vector
---@param node NodeDef
---@param clicker ObjectRef
---@param itemstack ItemStack
---@param pointed_thing? PointedThingDef
---@diagnostic disable-next-line: unused-local
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local meta = core.get_meta(pos)
local p_name = clicker:get_player_name()
local inv = meta:get_inventory()
local item_stack = inv:get_stack('item', 1)
local has_all_cursed_ench = XEnchanting:has_all_cursed_ench(item_stack)
if core.is_protected(pos, p_name) then
return itemstack
end
core.sound_play('x_enchanting_wood_hit', {
gain = 0.5,
pos = pos,
max_hear_distance = 10
}, true)
local formspec = XEnchanting:get_grindstone_formspec(pos, p_name, { result_disabled = has_all_cursed_ench })
meta:set_string('formspec', formspec)
return itemstack
end,
---@param pos Vector
---@param intensity? number
---@return table | nil
---@diagnostic disable-next-line: unused-local
on_blast = function(pos, intensity)
if core.is_protected(pos, '') then
return
end
local drops = {}
local inv = core.get_meta(pos):get_inventory()
local stack_item = inv:get_stack('item', 1)
if not stack_item:is_empty() then
drops[#drops + 1] = stack_item:to_table()
end
drops[#drops + 1] = 'x_enchanting:grindstone'
core.remove_node(pos)
return drops
end,
---@param pos Vector
---@param player? ObjectRef
can_dig = function(pos, player)
if not player then
return false
end
local inv = core.get_meta(pos):get_inventory()
return inv:is_empty('item')
and inv:is_empty('result')
and not core.is_protected(pos, player:get_player_name())
end,
---@diagnostic disable-next-line: unused-local
on_rotate = function(pos, node, user, mode, new_param2)
return false
end,
---@param pos Vector
---@param listname string
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local st_meta = stack:get_meta()
local is_enchanted = st_meta:get_int('is_enchanted')
if listname == 'item' and is_enchanted == 1 then
return stack:get_count()
end
return 0
end,
---@param pos Vector
---@param listname string
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
return stack:get_count()
end,
---@param pos Vector
---@param from_list string
---@param from_index number
---@param to_list string
---@param to_index number
---@param count number
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
return 0
end,
---@param pos Vector
---@param listname string
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
on_metadata_inventory_put = function(pos, listname, index, stack, player)
--
-- Create new itemstack with removed enchantments (excl. curses) and populate the result slot with this new item
--
local meta = core.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')
local stack_enchantment_data = core.deserialize(item_stack_meta:get_string('x_enchanting')) or {}
local has_all_cursed_ench = XEnchanting:has_all_cursed_ench(item_stack)
if not inv:is_empty('item') and is_enchanted == 1 and not has_all_cursed_ench then
-- Discenchanted item
local has_curse = false
local current_enchantments = {}
for id, value in pairs(stack_enchantment_data) do
-- Remove enchantment meta data (excl. cursed)
local ench_def = XEnchanting.enchantment_defs[id]
if not ench_def.cursed then
item_stack_meta:set_float('is_' .. id, 0)
stack_enchantment_data[id] = nil
else
has_curse = true
end
-- Get descriptions
if stack_enchantment_data[id] then
local level
-- Find level
for i, v in ipairs(ench_def.level_def) do
if v == value.value then
level = i
break
end
end
table.insert(current_enchantments, {
id = id,
value = value.value,
level = level,
secondary = ench_def.secondary,
incompatible = ench_def.incompatible
})
end
end
if not has_curse then
-- Reset meta data if not cursed
item_stack_meta:set_string('inventory_image', '')
item_stack_meta:set_string('inventory_overlay', '')
item_stack_meta:set_string('wield_image', '')
item_stack_meta:set_string('wield_overlay', '')
item_stack_meta:set_string('description', '')
item_stack_meta:set_string('short_description', '')
item_stack_meta:set_int('is_enchanted', 0)
end
local descriptions = XEnchanting:get_enchanted_descriptions(current_enchantments)
-- Upgrade description with remaining enchantments
if #current_enchantments > 0 then
local item_stack_def = core.registered_tools[item_stack:get_name()]
item_stack_meta:set_string('description', (item_stack_def and item_stack_def.description or '') .. '\n' .. descriptions.enchantments_desc)
end
item_stack_meta:set_tool_capabilities(nil)
item_stack_meta:set_string('x_enchanting', core.serialize(stack_enchantment_data))
inv:set_stack('result', 1, item_stack)
end
local formspec = XEnchanting:get_grindstone_formspec(pos, p_name, { result_disabled = has_all_cursed_ench })
meta:set_string('formspec', formspec)
end,
---@param pos Vector
---@param listname string
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
on_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = core.get_meta(pos)
local p_name = player:get_player_name()
local inv = meta:get_inventory()
local result_stack = inv:get_stack('result', 1)
local item_stack = inv:get_stack('item', 1)
local item_stack_meta = item_stack:get_meta()
local has_all_cursed_ench = XEnchanting:has_all_cursed_ench(item_stack)
local stack_enchantment_data = core.deserialize(item_stack_meta:get_string('x_enchanting')) or {}
local result_payment_stack = ItemStack({ name = 'default:mese_crystal_fragment' })
if item_stack:is_empty() or has_all_cursed_ench then
-- Remove result item
inv:remove_item('result', result_stack)
end
-- Collect total result
local result_total = 0
if result_total == 0 then
-- get payback result (excl. cursed enchantments) from the original item
for id, value in pairs(stack_enchantment_data) do
local ench_def = XEnchanting.enchantment_defs[id]
local lvl_index
if not ench_def.cursed then
-- find level index
for i, v in ipairs(ench_def.level_def) do
if v == stack_enchantment_data[id].value then
lvl_index = i
end
end
end
if lvl_index then
local final_level_range = ench_def.final_level_range[lvl_index]
local range_max = final_level_range[1] + final_level_range[2]
local range_min = math.floor(range_max / 2)
local result = math.random(range_min, range_max)
result_total = result_total + result
end
end
if result_total > result_payment_stack:get_stack_max() then
result_total = result_payment_stack:get_stack_max()
elseif result_total == 0 then
result_total = 1
end
end
result_total = math.floor(result_total / 10)
result_payment_stack:set_count(result_total)
if listname == 'result' and result_stack:is_empty() then
-- Drop payment result
inv:set_stack('item', 1, ItemStack(''))
core.item_drop(result_payment_stack, player, player:get_pos())
core.sound_play('x_enchanting_disenchant', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
}, true)
-- particles
local particlespawner_def = {
amount = 50,
time = 0.5,
minpos = { x = pos.x - 0.5, y = pos.y + 0.5, z = pos.z - 0.5 },
maxpos = { x = pos.x + 0.5, y = pos.y + 0.5, z = pos.z + 0.5 },
minvel = { x = -1.5, y = -0.5, z = -1.5 },
maxvel = { x = 1.5, y = -1.5, z = 1.5 },
minacc = { x = -3.5, y = -6.5, z = -3.5 },
maxacc = { x = 5.5, y = -7.5, z = 5.5 },
minexptime = 0.5,
maxexptime = 1,
minsize = 0.5,
maxsize = 1,
texture = 'x_enchanting_scroll_particle.png^[colorize:#FFE5C2:256',
glow = 1
}
core.add_particlespawner(particlespawner_def)
end
local formspec = XEnchanting:get_grindstone_formspec(pos, p_name)
meta:set_string('formspec', formspec)
end
})
----
-- Recipe
---
core.register_craft({
output = 'x_enchanting:grindstone',
recipe = {
{ 'group:stick', 'stairs:slab_stone', 'group:stick' },
{ 'group:wood', '', 'group:wood' }
}
})

309
init.lua
View File

@ -1,69 +1,312 @@
-- X Enchanting
-- by SaKeL
--[[
X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2025 SaKeL
local path = minetest.get_modpath('x_enchanting')
local mod_start_time = minetest.get_us_time()
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to juraj.vajda@gmail.com
--]]
local path = core.get_modpath('x_enchanting')
local mod_start_time = core.get_us_time()
dofile(path .. '/api.lua')
dofile(path .. '/table.lua')
dofile(path .. '/grindstone.lua')
minetest.register_on_mods_loaded(function()
for name, tool_def in pairs(minetest.registered_tools) do
---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
core.register_on_mods_loaded(function()
-- Tools override
for name, tool_def in pairs(core.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(core.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(core.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
core.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
core.item_drop(stack, puncher, pos)
end
end
end
end
end
end
end
end
end)
minetest.register_on_joinplayer(function(player, last_login)
---@diagnostic disable-next-line: unused-local
core.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)
---@diagnostic disable-next-line: unused-local
core.register_on_leaveplayer(function(player, timed_out)
XEnchanting.form_context[player:get_player_name()] = nil
end)
minetest.register_on_leaveplayer(function(player, timed_out)
XEnchanting.form_context[player:get_player_name()] = nil
end)
local old_handle_node_drops = core.handle_node_drops
-- Silk Touch
local old_handle_node_drops = minetest.handle_node_drops
function minetest.handle_node_drops(pos, drops, digger)
function core.handle_node_drops(pos, drops, digger)
if not digger
or not digger:is_player()
then
return old_handle_node_drops(pos, drops, digger)
end
local node = core.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
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
-- Fortune
local fortune = wield_stack_meta:get_float('is_fortune')
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 fortune > 0 then
local new_drops = {}
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
for _, itemstring in ipairs(drops) do
if XEnchanting.registered_ores[node.name]
or core.get_item_group(node.name, 'stone') > 0
or core.get_item_group(node.name, 'soil') > 0
or core.get_item_group(node.name, 'sand') > 0
or core.get_item_group(node.name, 'snowy') > 0
or core.get_item_group(node.name, 'slippery') > 0
or core.get_item_group(node.name, 'tree') > 0
or core.get_item_group(node.name, 'leaves') > 0
then
local tool_capabilities = wield_stack:get_tool_capabilities()
local stack = ItemStack(itemstring)
local chance = math.random(1, tool_capabilities.max_drop_level)
stack:set_count(stack:get_count() * chance)
if stack:get_count() > 0 then
table.insert(new_drops, stack)
end
end
end
if #new_drops > 0 then
return old_handle_node_drops(pos, new_drops, digger)
end
return old_handle_node_drops(pos, drops, digger)
end
-- Silk Touch
local silk_touch = wield_stack_meta:get_float('is_silk_touch')
if silk_touch > 0
and core.get_item_group(node.name, 'no_silktouch') == 0
then
-- drop raw item/node
return old_handle_node_drops(pos, { ItemStack(node.name) }, digger)
end
return old_handle_node_drops(pos, drops, digger)
end
local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000
---@diagnostic disable-next-line: unused-local
core.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 = core.calculate_knockback
function core.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 = (core.get_us_time() - mod_start_time) / 1000000
print('[Mod] x_enchanting loaded.. [' .. mod_end_time .. 's]')

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

View File

@ -1,6 +1,6 @@
name = x_enchanting
description = Adds Enchanting Mechanics and API.
depends =
optional_depends = xdecor
optional_depends = xdecor, item_drop, stairs, enchanting
supported_games = minetest_game
min_minetest_version = 5.4

View File

@ -0,0 +1,928 @@
# Blender v2.83.20 OBJ File: 'x_enchanting_grindstone.blend'
# www.blender.org
mtllib x_enchanting_grindstone.mtl
o x_enchanting_grindstone.vox
v 0.125000 -0.499000 0.437500
v 0.187500 -0.499000 0.312500
v 0.125000 -0.499000 -0.375000
v 0.125000 -0.499000 -0.437500
v 0.125000 -0.311500 0.375000
v -0.250000 -0.311500 0.312500
v 0.187500 -0.311500 0.312500
v 0.187500 -0.311500 -0.312500
v 0.125000 -0.311500 -0.437500
v -0.250000 -0.311500 -0.312500
v 0.187500 -0.249000 0.375000
v 0.187500 -0.186500 0.375000
v -0.250000 -0.186500 0.312500
v -0.250000 -0.186500 -0.312500
v -0.250000 -0.124000 0.312500
v 0.187500 -0.186500 -0.312500
v -0.312500 -0.124000 0.500000
v 0.187500 -0.124000 0.187500
v -0.312500 -0.061500 0.062500
v 0.187500 -0.061500 0.062500
v -0.125000 -0.061500 -0.062500
v 0.187500 -0.124000 -0.312500
v -0.312500 -0.124000 -0.500000
v -0.312500 0.001000 0.500000
v -0.312500 -0.061500 -0.062500
v 0.187500 0.001000 -0.062500
v -0.312500 0.001000 -0.500000
v -0.375000 0.063500 -0.062500
v -0.125000 -0.311500 0.062500
v 0.312500 0.376000 -0.062500
v -0.187500 -0.499000 0.312500
v -0.187500 -0.499000 -0.312500
v -0.187500 -0.499000 -0.375000
v -0.125000 -0.499000 -0.375000
v -0.187500 -0.311500 -0.312500
v -0.187500 -0.311500 0.312500
v -0.187500 -0.311500 0.062500
v 0.250000 -0.311500 -0.312500
v -0.187500 -0.186500 0.312500
v -0.187500 -0.186500 0.375000
v -0.187500 -0.186500 0.062500
v -0.187500 -0.186500 -0.375000
v -0.187500 -0.186500 -0.312500
v 0.250000 -0.186500 -0.312500
v -0.187500 -0.124000 0.312500
v -0.187500 -0.124000 0.125000
v 0.125000 -0.061500 0.062500
v 0.125000 -0.311500 0.062500
v -0.187500 -0.061500 -0.062500
v 0.312500 -0.124000 -0.500000
v 0.312500 0.001000 0.500000
v -0.187500 -0.061500 0.062500
v -0.187500 0.001000 0.125000
v 0.125000 -0.061500 -0.062500
v 0.125000 0.063500 0.062500
v 0.437500 0.251000 0.062500
v 0.437500 0.251000 -0.062500
v 0.500000 0.376000 -0.062500
v 0.250000 -0.499000 0.437500
v 0.187500 -0.499000 -0.312500
v 0.250000 -0.499000 -0.312500
v 0.187500 -0.499000 -0.375000
v -0.125000 -0.311500 0.437500
v -0.125000 -0.311500 -0.375000
v 0.125000 -0.186500 0.437500
v -0.125000 -0.186500 -0.375000
v 0.062500 -0.186500 -0.375000
v 0.125000 -0.186500 -0.375000
v 0.250000 -0.124000 0.437500
v -0.187500 -0.124000 -0.312500
v -0.250000 -0.124000 -0.312500
v -0.125000 -0.124000 -0.375000
v -0.187500 -0.124000 0.500000
v 0.312500 -0.124000 0.500000
v 0.187500 0.001000 0.062500
v 0.312500 -0.061500 0.062500
v -0.375000 0.063500 0.062500
v -0.187500 0.001000 0.062500
v 0.500000 0.251000 0.062500
v 0.500000 0.376000 0.062500
v -0.125000 -0.499000 0.375000
v 0.125000 -0.499000 0.375000
v -0.250000 -0.499000 0.312500
v -0.250000 -0.499000 -0.437500
v -0.125000 -0.499000 -0.437500
v -0.125000 -0.311500 0.375000
v -0.125000 -0.311500 -0.312500
v 0.125000 -0.186500 0.375000
v 0.125000 -0.249000 0.375000
v 0.125000 -0.186500 -0.437500
v -0.125000 -0.186500 0.375000
v 0.187500 -0.186500 0.312500
v 0.250000 -0.124000 0.312500
v 0.125000 -0.124000 -0.437500
v -0.125000 -0.124000 0.375000
v -0.187500 0.001000 0.375000
v 0.312500 -0.061500 -0.062500
v 0.437500 -0.061500 -0.062500
v -0.312500 0.001000 -0.062500
v -0.187500 0.001000 -0.062500
v -0.125000 0.063500 -0.062500
v 0.312500 0.063500 -0.062500
v 0.312500 0.001000 -0.062500
v 0.500000 0.251000 -0.062500
v -0.125000 0.313500 -0.312500
v -0.250000 -0.499000 0.437500
v -0.187500 -0.499000 0.375000
v -0.125000 -0.499000 0.437500
v 0.187500 -0.499000 0.375000
v 0.250000 -0.499000 0.312500
v -0.250000 -0.499000 -0.312500
v 0.250000 -0.499000 -0.437500
v 0.125000 -0.311500 0.437500
v -0.125000 -0.311500 0.312500
v 0.250000 -0.311500 0.312500
v -0.125000 -0.311500 -0.437500
v 0.062500 -0.311500 -0.375000
v 0.125000 -0.311500 -0.375000
v -0.250000 -0.124000 0.437500
v -0.187500 -0.124000 0.437500
v -0.125000 -0.124000 0.500000
v 0.125000 -0.124000 0.437500
v -0.125000 -0.124000 0.437500
v 0.125000 -0.124000 0.375000
v 0.187500 -0.124000 0.312500
v 0.250000 -0.124000 -0.312500
v 0.125000 -0.124000 -0.375000
v -0.250000 -0.124000 -0.437500
v -0.125000 -0.124000 -0.437500
v 0.250000 -0.124000 -0.437500
v -0.375000 -0.061500 0.062500
v -0.125000 -0.061500 0.062500
v 0.437500 -0.061500 0.062500
v -0.375000 -0.061500 -0.062500
v 0.187500 -0.061500 -0.062500
v -0.125000 -0.186500 0.437500
v 0.250000 -0.186500 0.312500
v -0.125000 -0.186500 -0.437500
v 0.187500 0.001000 0.375000
v 0.187500 0.001000 0.187500
v -0.312500 0.001000 0.062500
v 0.312500 0.001000 0.062500
v -0.187500 0.001000 -0.375000
v 0.187500 0.001000 -0.375000
v 0.312500 0.001000 -0.500000
v 0.312500 0.063500 0.062500
v -0.125000 0.313500 0.312500
v -0.125000 0.313500 -0.062500
v 0.312500 0.376000 0.062500
v -0.125000 0.063500 0.062500
v 0.125000 -0.311500 -0.312500
v 0.125000 0.063500 -0.062500
v 0.125000 0.313500 -0.312500
v 0.125000 -0.311500 0.312500
v 0.125000 0.313500 0.312500
v 0.125000 0.313500 -0.062500
vt 0.781250 0.968750
vt 0.843750 0.812500
vt 0.843750 0.968750
vt 0.625000 0.437500
vt 0.625000 0.375000
vt 0.593750 0.421875
vt 0.968750 0.453125
vt 1.000000 0.500000
vt 0.968750 0.421875
vt 0.437500 0.687500
vt 0.562500 0.843750
vt 0.437500 0.843750
vt 0.718750 0.062500
vt 0.687500 0.109375
vt 0.718750 0.109375
vt 0.562500 0.093750
vt 0.687500 0.078125
vt 0.687500 0.093750
vt 0.437500 0.343750
vt 0.437500 0.500000
vt 0.375000 0.468750
vt 0.625000 0.093750
vt 0.593750 0.109375
vt 0.625000 0.109375
vt 0.625000 0.171875
vt 0.593750 0.187500
vt 0.625000 0.187500
vt 0.718750 0.500000
vt 0.718750 0.468750
vt 0.656250 0.468750
vt 0.593750 0.218750
vt 0.625000 0.375000
vt 0.593750 0.375000
vt 0.468750 0.671875
vt 0.406250 0.671875
vt 0.187500 0.656250
vt 0.625000 0.500000
vt 0.468750 0.468750
vt 0.593750 0.468750
vt 0.750000 0.796875
vt 0.687500 0.859375
vt 0.687500 0.796875
vt 0.718750 0.859375
vt 0.718750 0.937500
vt 0.656250 0.890625
vt 0.718750 0.140625
vt 0.687500 0.187500
vt 0.718750 0.187500
vt 0.093750 0.593750
vt 0.312500 0.593750
vt 0.312500 0.625000
vt 0.968750 0.421875
vt 1.000000 0.453125
vt 0.968750 0.453125
vt 0.562500 0.093750
vt 0.593750 0.140625
vt 0.562500 0.140625
vt 0.625000 0.453125
vt 0.656250 0.468750
vt 0.625000 0.500000
vt 0.687500 0.750000
vt 0.750000 0.734375
vt 0.750000 0.750000
vt 0.625000 0.109375
vt 0.656250 0.093750
vt 0.656250 0.109375
vt 0.625000 0.187500
vt 0.656250 0.171875
vt 0.656250 0.187500
vt 0.125000 0.843750
vt 0.125000 0.906250
vt 0.187500 0.906250
vt 0.468750 0.640625
vt 0.406250 0.640625
vt 0.187500 0.625000
vt 0.625000 0.734375
vt 0.687500 0.812500
vt 0.625000 0.812500
vt 0.437500 0.500000
vt 0.625000 0.500000
vt 0.593750 0.468750
vt 0.062500 0.468750
vt 0.000000 0.500000
vt 0.000000 0.343750
vt 0.750000 0.562500
vt 0.937500 0.562500
vt 0.843750 0.593750
vt 0.125000 0.937500
vt 0.000000 0.843750
vt 0.000000 1.000000
vt 0.500000 0.343750
vt 0.531250 0.390625
vt 0.500000 0.390625
vt 0.687500 0.218750
vt 0.718750 0.265625
vt 0.687500 0.265625
vt 0.531250 0.109375
vt 0.562500 0.093750
vt 0.562500 0.109375
vt 0.625000 0.296875
vt 0.656250 0.281250
vt 0.656250 0.296875
vt 0.562500 0.734375
vt 0.625000 0.812500
vt 0.562500 0.812500
vt 0.218750 0.546875
vt 0.281250 0.593750
vt 0.218750 0.578125
vt 0.156250 0.671875
vt 0.187500 0.687500
vt 0.062500 0.687500
vt 0.750000 0.765625
vt 0.687500 0.765625
vt 0.437500 0.375000
vt 0.468750 0.375000
vt 0.468750 0.421875
vt 0.718750 0.265625
vt 0.687500 0.312500
vt 0.718750 0.312500
vt 0.562500 0.281250
vt 0.531250 0.265625
vt 0.562500 0.265625
vt 0.656250 0.296875
vt 0.625000 0.312500
vt 0.656250 0.312500
vt 0.687500 0.828125
vt 0.625000 0.859375
vt 0.625000 0.828125
vt 0.531250 0.546875
vt 0.593750 0.593750
vt 0.531250 0.578125
vt 0.187500 0.781250
vt 0.312500 0.687500
vt 0.312500 0.843750
vt 0.562500 0.171875
vt 0.593750 0.218750
vt 0.562500 0.218750
vt 0.656250 0.984375
vt 0.718750 0.984375
vt 0.656250 0.968750
vt 0.312500 0.625000
vt 0.625000 0.593750
vt 0.625000 0.625000
vt 0.000000 0.640625
vt 0.125000 0.640625
vt 0.031250 0.656250
vt 0.312500 0.843750
vt 0.437500 0.687500
vt 0.312500 0.687500
vt 0.656250 0.234375
vt 0.625000 0.218750
vt 0.625000 0.250000
vt 0.468750 0.421875
vt 0.468750 0.406250
vt 0.500000 0.406250
vt 0.562500 0.062500
vt 0.687500 0.078125
vt 0.687500 0.062500
vt 0.687500 0.281250
vt 0.687500 0.265625
vt 0.656250 0.265625
vt 0.531250 0.328125
vt 0.500000 0.328125
vt 0.500000 0.234375
vt 0.687500 0.140625
vt 0.593750 0.156250
vt 0.562500 0.140625
vt 0.593750 0.218750
vt 0.562500 0.375000
vt 0.593750 0.375000
vt 0.562500 0.156250
vt 0.562500 0.171875
vt 0.593750 0.171875
vt 0.468750 0.312500
vt 0.500000 0.296875
vt 0.500000 0.312500
vt 0.531250 0.203125
vt 0.500000 0.109375
vt 0.468750 0.093750
vt 0.531250 0.093750
vt 0.000000 0.187500
vt 0.031250 0.234375
vt 0.031250 0.296875
vt 0.750000 0.750000
vt 0.687500 0.734375
vt 0.625000 0.703125
vt 0.687500 0.703125
vt 0.625000 0.703125
vt 0.687500 0.687500
vt 0.687500 0.703125
vt 0.625000 0.812500
vt 0.687500 0.828125
vt 0.625000 0.828125
vt 0.625000 0.687500
vt 0.562500 0.734375
vt 0.562500 0.687500
vt 0.562500 0.859375
vt 0.625000 0.812500
vt 0.625000 0.859375
vt 0.531250 0.171875
vt 0.500000 0.125000
vt 0.531250 0.125000
vt 0.187500 0.687500
vt 0.187500 0.750000
vt 0.125000 0.750000
vt 0.437500 0.937500
vt 0.437500 1.000000
vt 0.312500 1.000000
vt 0.562500 0.906250
vt 0.437500 0.843750
vt 0.437500 0.906250
vt 0.750000 0.781250
vt 0.937500 0.781250
vt 0.937500 0.812500
vt 0.781250 0.812500
vt 0.750000 0.812500
vt 0.750000 1.000000
vt 0.750000 0.968750
vt 0.937500 1.000000
vt 0.937500 0.968750
vt 0.468750 0.375000
vt 0.437500 0.375000
vt 0.468750 0.421875
vt 0.437500 0.453125
vt 0.468750 0.453125
vt 0.593750 0.453125
vt 0.593750 0.437500
vt 0.593750 0.375000
vt 1.000000 0.375000
vt 0.968750 0.375000
vt 0.656250 0.421875
vt 0.656250 0.375000
vt 0.843750 0.484375
vt 0.843750 0.500000
vt 0.968750 0.468750
vt 0.781250 0.500000
vt 0.781250 0.484375
vt 0.625000 0.453125
vt 0.656250 0.453125
vt 0.562500 0.687500
vt 0.687500 0.062500
vt 0.562500 0.078125
vt 0.375000 0.375000
vt 0.250000 0.375000
vt 0.218750 0.343750
vt 0.218750 0.375000
vt 0.218750 0.500000
vt 0.281250 0.468750
vt 0.218750 0.468750
vt 0.593750 0.093750
vt 0.593750 0.171875
vt 0.625000 0.218750
vt 0.406250 0.687500
vt 0.187500 0.687500
vt 0.687500 0.656250
vt 0.687500 0.687500
vt 0.468750 0.687500
vt 0.593750 0.375000
vt 0.625000 0.375000
vt 0.593750 0.421875
vt 0.437500 0.500000
vt 0.437500 0.453125
vt 0.468750 0.453125
vt 0.562500 0.453125
vt 0.562500 0.421875
vt 0.593750 0.453125
vt 0.750000 0.859375
vt 0.750000 0.968750
vt 0.656250 0.968750
vt 0.562500 0.890625
vt 0.562500 0.859375
vt 0.593750 0.875000
vt 0.593750 0.859375
vt 0.656250 0.875000
vt 0.656250 0.859375
vt 0.750000 0.937500
vt 0.687500 0.140625
vt 0.000000 0.625000
vt 0.000000 0.593750
vt 0.062500 0.593750
vt 0.968750 0.375000
vt 1.000000 0.375000
vt 1.000000 0.500000
vt 0.843750 0.500000
vt 0.968750 0.468750
vt 0.843750 0.484375
vt 0.781250 0.484375
vt 0.781250 0.500000
vt 0.750000 0.500000
vt 0.750000 0.468750
vt 0.781250 0.453125
vt 0.781250 0.421875
vt 0.593750 0.093750
vt 0.625000 0.375000
vt 0.656250 0.375000
vt 0.656250 0.421875
vt 0.656250 0.453125
vt 0.687500 0.734375
vt 0.625000 0.093750
vt 0.625000 0.171875
vt 0.187500 0.937500
vt 0.312500 0.843750
vt 0.187500 1.000000
vt 0.312500 1.000000
vt 0.406250 0.656250
vt 0.187500 0.656250
vt 0.687500 0.625000
vt 0.687500 0.656250
vt 0.468750 0.656250
vt 0.687500 0.734375
vt 0.468750 0.468750
vt 0.218750 0.343750
vt 0.062500 0.375000
vt 0.218750 0.375000
vt 0.218750 0.468750
vt 0.218750 0.500000
vt 0.843750 0.750000
vt 0.937500 0.750000
vt 0.937500 0.781250
vt 0.750000 0.781250
vt 0.750000 0.750000
vt 0.781250 0.750000
vt 0.781250 0.593750
vt 0.750000 0.593750
vt 0.937500 0.593750
vt 0.531250 0.343750
vt 0.718750 0.218750
vt 0.531250 0.093750
vt 0.625000 0.281250
vt 0.625000 0.734375
vt 0.031250 0.593750
vt 0.031250 0.500000
vt 0.093750 0.546875
vt 0.093750 0.500000
vt 0.093750 0.578125
vt 0.093750 0.593750
vt 0.062500 0.593750
vt 0.218750 0.500000
vt 0.281250 0.500000
vt 0.218750 0.593750
vt 0.156250 0.656250
vt 0.187500 0.656250
vt 0.062500 0.656250
vt 0.093750 0.671875
vt 0.093750 0.656250
vt 0.687500 0.265625
vt 0.531250 0.281250
vt 0.625000 0.296875
vt 0.687500 0.859375
vt 0.406250 0.578125
vt 0.406250 0.593750
vt 0.343750 0.593750
vt 0.343750 0.500000
vt 0.406250 0.546875
vt 0.406250 0.500000
vt 0.531250 0.500000
vt 0.593750 0.500000
vt 0.531250 0.593750
vt 0.125000 0.843750
vt 0.125000 0.781250
vt 0.593750 0.171875
vt 0.593750 0.921875
vt 0.593750 1.000000
vt 0.656250 1.000000
vt 0.562500 0.890625
vt 0.562500 0.921875
vt 0.656250 0.890625
vt 0.718750 1.000000
vt 0.750000 1.000000
vt 0.750000 0.968750
vt 0.312500 0.593750
vt 0.031250 0.671875
vt 0.000000 0.671875
vt 0.125000 0.671875
vt 0.093750 0.656250
vt 0.093750 0.671875
vt 0.437500 0.843750
vt 0.656250 0.218750
vt 0.687500 0.250000
vt 0.687500 0.234375
vt 0.531250 0.421875
vt 0.562500 0.078125
vt 0.656250 0.250000
vt 0.625000 0.250000
vt 0.625000 0.281250
vt 0.500000 0.171875
vt 0.687500 0.156250
vt 0.562500 0.156250
vt 0.562500 0.218750
vt 0.687500 0.171875
vt 0.687500 0.156250
vt 0.468750 0.296875
vt 0.468750 0.109375
vt 0.500000 0.187500
vt 0.468750 0.234375
vt 0.437500 0.296875
vt 0.437500 0.234375
vt 0.468750 0.328125
vt 0.500000 0.343750
vt 0.468750 0.203125
vt 0.406250 0.328125
vt 0.000000 0.343750
vt 0.312500 0.312500
vt 0.406250 0.312500
vt 0.406250 0.203125
vt 0.406250 0.218750
vt 0.343750 0.218750
vt 0.093750 0.328125
vt 0.093750 0.312500
vt 0.093750 0.203125
vt 0.093750 0.218750
vt 0.031250 0.328125
vt 0.062500 0.234375
vt 0.062500 0.296875
vt 0.031250 0.203125
vt 0.687500 0.750000
vt 0.625000 0.734375
vt 0.625000 0.687500
vt 0.687500 0.812500
vt 0.625000 0.734375
vt 0.562500 0.812500
vt 0.000000 0.687500
vt 0.000000 0.843750
vt 0.312500 0.937500
vt 0.437500 0.843750
vt 0.437500 1.000000
vt 0.562500 1.000000
vt 0.562500 0.843750
vn -1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn 0.0000 0.0000 1.0000
vn 0.0000 1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
usemtl grindstone
s off
f 13/1/1 10/2/1 6/3/1
f 11/4/2 109/5/2 5/6/2
f 16/7/1 144/8/1 8/9/1
f 114/10/3 155/11/3 147/12/3
f 113/13/1 82/14/1 1/15/1
f 65/16/4 91/17/4 136/18/4
f 24/19/4 51/20/4 139/21/4
f 122/22/1 88/23/1 65/24/1
f 127/25/1 90/26/1 68/27/1
f 140/28/1 18/29/1 125/30/1
f 44/31/4 92/32/4 137/33/4
f 19/34/1 25/35/1 23/36/1
f 144/37/3 72/38/3 127/39/3
f 77/40/4 101/41/4 28/42/4
f 133/43/3 56/44/3 146/45/3
f 118/46/1 4/47/1 3/48/1
f 121/49/3 74/50/3 51/51/3
f 35/52/5 42/53/5 43/54/5
f 86/55/5 108/56/5 81/57/5
f 40/58/5 45/59/5 96/60/5
f 49/61/6 132/62/6 52/63/6
f 91/64/5 123/65/5 136/66/5
f 138/67/5 72/68/5 66/69/5
f 48/70/1 47/71/1 54/72/1
f 97/73/5 76/74/5 74/75/5
f 57/76/5 133/77/5 98/78/5
f 96/79/2 139/80/2 124/81/2
f 144/82/4 145/83/4 27/84/4
f 69/85/5 59/86/5 115/87/5
f 55/88/1 154/89/1 155/90/1
f 35/91/3 111/92/3 32/93/3
f 38/94/3 60/95/3 61/96/3
f 43/97/3 71/98/3 14/99/3
f 44/100/3 22/101/3 16/102/3
f 102/103/1 149/104/1 30/105/1
f 113/106/3 69/107/3 65/108/3
f 78/109/3 150/110/3 77/111/3
f 131/112/1 28/42/1 134/113/1
f 33/114/3 34/115/3 64/116/3
f 7/117/2 110/118/2 2/119/2
f 15/120/2 39/121/2 13/122/2
f 125/123/2 137/124/2 92/125/2
f 80/126/5 104/127/5 58/128/5
f 116/129/2 128/130/2 138/131/2
f 150/132/1 114/133/1 147/134/1
f 116/135/5 34/136/5 85/137/5
f 103/138/2 26/139/2 102/140/2
f 145/141/2 23/142/2 27/143/2
f 28/144/2 101/145/2 99/146/2
f 105/147/2 151/148/2 87/149/2
f 109/150/6 110/151/6 59/152/6
f 85/153/6 34/154/6 33/155/6
f 5/156/6 63/157/6 86/158/6
f 61/159/6 60/160/6 62/161/6
f 10/162/6 35/163/6 37/164/6
f 138/165/4 67/166/4 90/167/4
f 8/168/6 115/169/6 7/170/6
f 9/171/6 118/172/6 117/173/6
f 120/174/6 121/175/6 73/176/6
f 13/122/4 39/121/4 41/177/4
f 107/178/6 108/179/6 106/180/6
f 50/181/6 94/182/6 129/183/6
f 134/113/6 19/184/6 131/112/6
f 76/185/6 98/186/6 133/187/6
f 54/188/6 20/189/6 47/190/6
f 104/191/6 56/192/6 57/193/6
f 149/194/4 58/195/4 30/196/4
f 102/197/4 55/198/4 146/199/4
f 6/200/2 31/201/2 83/202/2
f 29/203/1 132/204/1 21/205/1
f 156/206/4 153/207/4 105/208/4
f 48/209/6 114/210/6 29/211/6
f 128/212/1 84/213/1 10/2/1
f 84/213/1 111/214/1 10/2/1
f 14/215/1 71/216/1 128/212/1
f 119/217/1 15/218/1 13/1/1
f 14/215/1 128/212/1 10/2/1
f 106/219/1 119/217/1 6/3/1
f 119/217/1 13/1/1 6/3/1
f 13/1/1 14/215/1 10/2/1
f 6/3/1 83/220/1 106/219/1
f 81/221/2 107/222/2 86/223/2
f 107/222/2 40/224/2 86/223/2
f 91/225/2 88/226/2 89/227/2
f 109/5/2 82/228/2 5/6/2
f 5/6/2 86/223/2 89/227/2
f 86/223/2 40/224/2 91/225/2
f 89/227/2 86/223/2 91/225/2
f 89/227/2 11/4/2 5/6/2
f 62/229/1 60/230/1 8/9/1
f 7/231/1 2/232/1 109/5/1
f 144/8/1 62/229/1 8/9/1
f 7/231/1 109/5/1 11/4/1
f 135/233/1 26/234/1 22/235/1
f 26/234/1 144/8/1 22/235/1
f 140/28/1 75/236/1 20/237/1
f 18/29/1 140/28/1 20/237/1
f 22/235/1 18/29/1 135/233/1
f 18/29/1 20/237/1 135/233/1
f 12/238/1 92/239/1 11/4/1
f 92/239/1 16/7/1 8/9/1
f 11/4/1 92/239/1 7/231/1
f 16/7/1 22/235/1 144/8/1
f 8/9/1 7/231/1 92/239/1
f 114/10/3 154/240/3 155/11/3
f 113/13/1 5/241/1 82/14/1
f 65/16/4 88/242/4 91/17/4
f 96/243/4 53/244/4 141/245/4
f 53/244/4 78/246/4 141/245/4
f 96/243/4 141/245/4 24/19/4
f 51/20/4 142/247/4 140/248/4
f 142/247/4 75/249/4 140/248/4
f 140/248/4 139/21/4 51/20/4
f 139/21/4 96/243/4 24/19/4
f 122/22/1 124/250/1 88/23/1
f 127/25/1 94/251/1 90/26/1
f 92/239/1 12/238/1 125/30/1
f 12/238/1 139/80/1 125/30/1
f 139/80/1 140/28/1 125/30/1
f 44/31/4 16/252/4 92/32/4
f 25/35/1 99/253/1 27/254/1
f 17/255/1 24/256/1 19/34/1
f 24/256/1 141/257/1 19/34/1
f 25/35/1 27/254/1 23/36/1
f 17/255/1 19/34/1 23/36/1
f 3/258/3 62/259/3 118/260/3
f 62/259/3 144/37/3 118/260/3
f 143/261/3 42/262/3 72/38/3
f 42/262/3 66/263/3 72/38/3
f 67/264/3 117/265/3 118/260/3
f 68/266/3 67/264/3 118/260/3
f 144/37/3 143/261/3 72/38/3
f 127/39/3 68/266/3 144/37/3
f 68/266/3 118/260/3 144/37/3
f 77/40/4 150/267/4 101/41/4
f 80/268/3 149/269/3 56/44/3
f 149/269/3 146/45/3 56/44/3
f 55/270/3 47/271/3 75/272/3
f 47/271/3 20/273/3 75/272/3
f 146/45/3 55/270/3 75/272/3
f 142/274/3 76/275/3 133/43/3
f 146/45/3 75/272/3 142/274/3
f 56/44/3 79/276/3 80/268/3
f 142/274/3 133/43/3 146/45/3
f 118/46/1 9/277/1 4/47/1
f 51/51/3 24/278/3 121/49/3
f 24/278/3 17/279/3 73/280/3
f 73/280/3 121/49/3 24/278/3
f 32/281/5 33/282/5 35/52/5
f 33/282/5 42/53/5 35/52/5
f 143/283/5 100/284/5 70/285/5
f 100/284/5 49/286/5 70/285/5
f 52/287/5 78/288/5 53/289/5
f 52/287/5 53/289/5 46/290/5
f 52/287/5 46/290/5 49/286/5
f 46/290/5 70/285/5 49/286/5
f 43/54/5 41/291/5 37/292/5
f 43/54/5 37/292/5 35/52/5
f 143/283/5 70/285/5 42/53/5
f 70/285/5 43/54/5 42/53/5
f 86/55/5 63/293/5 108/56/5
f 107/294/5 31/295/5 36/296/5
f 36/296/5 37/292/5 41/291/5
f 36/296/5 41/291/5 39/297/5
f 40/58/5 107/294/5 36/296/5
f 46/290/5 53/289/5 45/59/5
f 53/289/5 96/60/5 45/59/5
f 36/296/5 39/297/5 40/58/5
f 39/297/5 45/59/5 40/58/5
f 49/61/6 21/298/6 132/62/6
f 91/64/5 95/299/5 123/65/5
f 138/67/5 129/300/5 72/68/5
f 54/72/1 152/301/1 151/302/1
f 152/301/1 156/303/1 153/304/1
f 151/302/1 152/301/1 153/304/1
f 151/302/1 48/70/1 54/72/1
f 142/305/5 51/306/5 76/74/5
f 51/306/5 74/75/5 76/74/5
f 50/307/5 145/308/5 97/73/5
f 145/308/5 103/309/5 97/73/5
f 50/307/5 97/73/5 74/75/5
f 57/76/5 56/310/5 133/77/5
f 95/311/2 91/225/2 40/224/2
f 139/80/2 12/238/2 124/81/2
f 12/238/2 11/4/2 88/226/2
f 124/81/2 12/238/2 88/226/2
f 11/4/2 89/227/2 88/226/2
f 95/311/2 40/224/2 96/79/2
f 124/81/2 95/311/2 96/79/2
f 27/84/4 99/312/4 143/313/4
f 99/312/4 100/314/4 143/313/4
f 26/315/4 103/316/4 144/82/4
f 103/316/4 145/83/4 144/82/4
f 27/84/4 143/313/4 144/82/4
f 38/317/5 61/318/5 112/319/5
f 112/319/5 130/320/5 38/317/5
f 130/320/5 126/321/5 44/322/5
f 130/320/5 44/322/5 38/317/5
f 137/323/5 93/324/5 69/85/5
f 38/317/5 44/322/5 137/323/5
f 59/86/5 110/325/5 115/87/5
f 115/87/5 38/317/5 137/323/5
f 137/323/5 69/85/5 115/87/5
f 156/303/1 152/301/1 55/88/1
f 47/71/1 48/70/1 154/89/1
f 155/90/1 156/303/1 55/88/1
f 55/88/1 47/71/1 154/89/1
f 35/91/3 10/326/3 111/92/3
f 38/94/3 8/327/3 60/95/3
f 43/97/3 70/328/3 71/98/3
f 44/100/3 126/329/3 22/101/3
f 102/103/1 146/330/1 149/104/1
f 119/331/3 106/332/3 63/333/3
f 106/332/3 108/334/3 63/333/3
f 136/335/3 123/336/3 120/337/3
f 120/337/3 119/331/3 136/335/3
f 119/331/3 63/333/3 136/335/3
f 113/106/3 1/338/3 59/339/3
f 69/107/3 122/340/3 65/108/3
f 65/108/3 136/335/3 63/333/3
f 113/106/3 59/339/3 69/107/3
f 65/108/3 63/333/3 113/106/3
f 78/109/3 52/341/3 132/342/3
f 77/111/3 131/343/3 141/344/3
f 131/343/3 19/345/3 141/344/3
f 78/109/3 132/342/3 150/110/3
f 77/111/3 141/344/3 78/109/3
f 131/112/1 77/40/1 28/42/1
f 64/116/3 117/265/3 67/264/3
f 66/263/3 42/262/3 64/116/3
f 42/262/3 33/114/3 64/116/3
f 64/116/3 67/264/3 66/263/3
f 7/117/2 115/346/2 110/118/2
f 15/120/2 45/347/2 39/121/2
f 125/123/2 93/348/2 137/124/2
f 80/126/5 79/349/5 104/127/5
f 90/350/2 94/351/2 130/352/2
f 130/352/2 112/353/2 9/354/2
f 112/353/2 4/355/2 9/354/2
f 90/350/2 130/352/2 9/354/2
f 116/129/2 85/356/2 84/357/2
f 90/350/2 9/354/2 116/129/2
f 128/130/2 129/358/2 138/131/2
f 138/131/2 90/350/2 116/129/2
f 116/129/2 84/357/2 128/130/2
f 148/359/1 101/360/1 150/132/1
f 132/204/1 29/203/1 114/133/1
f 147/134/1 148/359/1 150/132/1
f 150/132/1 132/204/1 114/133/1
f 116/135/5 64/361/5 34/136/5
f 57/362/2 98/363/2 102/140/2
f 98/363/2 97/364/2 103/138/2
f 98/363/2 103/138/2 102/140/2
f 58/365/2 104/366/2 57/362/2
f 102/140/2 30/367/2 57/362/2
f 30/367/2 58/365/2 57/362/2
f 135/368/2 54/369/2 26/139/2
f 54/369/2 152/370/2 26/139/2
f 26/139/2 152/370/2 102/140/2
f 145/141/2 50/371/2 23/142/2
f 25/372/2 134/373/2 99/146/2
f 134/373/2 28/144/2 99/146/2
f 101/145/2 21/374/2 100/375/2
f 21/374/2 49/376/2 100/375/2
f 101/145/2 100/375/2 99/146/2
f 105/147/2 153/377/2 151/148/2
f 109/150/6 2/378/6 110/151/6
f 59/152/6 1/379/6 109/150/6
f 1/379/6 82/380/6 109/150/6
f 32/93/6 111/92/6 33/155/6
f 111/92/6 84/381/6 33/155/6
f 84/381/6 85/153/6 33/155/6
f 5/156/6 113/382/6 63/157/6
f 3/383/6 4/384/6 62/161/6
f 4/384/6 112/385/6 62/161/6
f 112/385/6 61/159/6 62/161/6
f 36/386/6 6/200/6 37/164/6
f 6/200/6 10/162/6 37/164/6
f 66/387/4 67/166/4 138/165/4
f 67/166/4 68/388/4 90/167/4
f 8/168/6 38/389/6 115/169/6
f 64/390/6 116/391/6 117/173/6
f 116/391/6 9/171/6 117/173/6
f 120/174/6 123/392/6 121/175/6
f 41/177/4 43/97/4 14/99/4
f 14/99/4 13/122/4 41/177/4
f 107/178/6 81/393/6 108/179/6
f 106/180/6 83/202/6 107/178/6
f 83/202/6 31/201/6 107/178/6
f 74/394/6 121/175/6 122/395/6
f 121/175/6 123/392/6 122/395/6
f 123/392/6 95/396/6 124/397/6
f 123/392/6 124/397/6 122/395/6
f 120/174/6 73/176/6 119/398/6
f 73/176/6 17/399/6 119/398/6
f 74/394/6 122/395/6 69/400/6
f 119/398/6 17/399/6 15/401/6
f 17/399/6 23/402/6 15/401/6
f 46/403/6 45/404/6 15/401/6
f 50/181/6 74/394/6 93/405/6
f 74/394/6 69/400/6 93/405/6
f 93/405/6 125/406/6 18/407/6
f 46/403/6 15/401/6 71/408/6
f 15/401/6 23/402/6 71/408/6
f 71/408/6 70/409/6 46/403/6
f 50/181/6 93/405/6 126/410/6
f 93/405/6 18/407/6 126/410/6
f 18/407/6 22/411/6 126/410/6
f 128/412/6 71/408/6 23/402/6
f 127/413/6 72/414/6 129/183/6
f 129/183/6 128/412/6 23/402/6
f 50/181/6 126/410/6 130/415/6
f 94/182/6 127/413/6 129/183/6
f 50/181/6 130/415/6 94/182/6
f 129/183/6 23/402/6 50/181/6
f 134/113/6 25/416/6 19/184/6
f 76/185/6 97/417/6 98/186/6
f 54/188/6 135/418/6 20/189/6
f 104/191/6 79/419/6 56/192/6
f 149/194/4 80/420/4 58/195/4
f 102/197/4 152/421/4 55/198/4
f 6/200/2 36/386/2 31/201/2
f 21/205/1 101/360/1 87/422/1
f 101/360/1 148/359/1 105/423/1
f 87/422/1 101/360/1 105/423/1
f 87/422/1 29/203/1 21/205/1
f 105/208/4 148/424/4 156/206/4
f 148/424/4 147/134/4 156/206/4
f 147/134/4 155/425/4 156/206/4
f 29/211/6 87/426/6 151/427/6
f 48/209/6 154/428/6 114/210/6
f 29/211/6 151/427/6 48/209/6

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -1,3 +1,21 @@
/**
* Deploy code to CDB
* Copyright (C) 2025 SaKeL
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to juraj.vajda@gmail.com
*/
import fetch from 'node-fetch'
import yargs from 'yargs/yargs'
import {hideBin} from 'yargs/helpers'
@ -11,7 +29,7 @@ try {
ref: 'master'
}
const response = await fetch('https://content.minetest.net/api/packages/SaKeL/x_enchanting/releases/new/', {
const response = await fetch('https://content.luanti.org/api/packages/SaKeL/x_enchanting/releases/new/', {
method: 'POST',
body: JSON.stringify(body),
headers: {

View File

@ -1,3 +1,21 @@
/**
* Run LUA diagnostics in continuous integration
* Copyright (C) 2025 SaKeL
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to juraj.vajda@gmail.com
*/
import * as path from 'node:path'
import * as fs from 'node:fs'
import {exec} from 'node:child_process'
@ -60,6 +78,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.

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.

157
table.lua
View File

@ -1,17 +1,35 @@
screwdriver = minetest.global_exists('screwdriver') and screwdriver --[[@as MtgScrewdriver]]
---@diagnostic disable
--[[
X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2025 SaKeL
local S = minetest.get_translator(minetest.get_current_modname())
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to juraj.vajda@gmail.com
--]]
local S = core.get_translator(core.get_current_modname())
----
--- Table Node
----
minetest.register_node('x_enchanting:table', {
core.register_node('x_enchanting:table', {
description = S('Enchanting Table'),
short_description = S('Enchanting Table'),
drawtype = 'mesh',
mesh = 'x_enchanting_table.obj',
tiles = { 'x_enchanting_table.png' },
use_texture_alpha = 'clip',
paramtype = 'light',
paramtype2 = 'facedir',
walkable = true,
@ -45,22 +63,23 @@ minetest.register_node('x_enchanting:table', {
light_source = 6,
---@param pos Vector
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local meta = core.get_meta(pos)
local inv = meta:get_inventory()
meta:set_string('infotext', S('Enchanting Table'))
meta:set_string('owner', '')
inv:set_size('item', 1)
inv:set_size('trade', 1)
minetest.add_entity({ x = pos.x, y = pos.y + 0.7, z = pos.z }, 'x_enchanting:table_scroll')
minetest.get_node_timer(pos):start(5)
core.add_entity({ x = pos.x, y = pos.y + 0.7, z = pos.z }, 'x_enchanting:table_scroll')
core.get_node_timer(pos):start(5)
end,
---@param pos Vector
---@param placer ObjectRef | nil
---@param itemstack ItemStack
---@param pointed_thing PointedThingDef
---@diagnostic disable-next-line: unused-local
after_place_node = function(pos, placer, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local meta = core.get_meta(pos)
if not placer then
return
@ -79,32 +98,37 @@ minetest.register_node('x_enchanting:table', {
---@param clicker ObjectRef
---@param itemstack ItemStack
---@param pointed_thing? PointedThingDef
---@diagnostic disable-next-line: unused-local
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local meta = core.get_meta(pos)
local p_name = clicker:get_player_name()
if minetest.is_protected(pos, p_name) then
if core.is_protected(pos, p_name) then
return itemstack
end
minetest.sound_play('x_enchanting_scroll', {
core.sound_play('x_enchanting_scroll', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
}, true)
-- bookshelfs
local bookshelfs = minetest.find_nodes_in_area(
local bookshelfs = core.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ 'default:bookshelf', 'group:bookshelf' }
)
local inv = minetest.get_meta(pos):get_inventory()
local inv = core.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,
core.registered_tools[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
meta:set_string('formspec', formspec)
@ -119,13 +143,14 @@ minetest.register_node('x_enchanting:table', {
---@param pos Vector
---@param intensity? number
---@return table | nil
---@diagnostic disable-next-line: unused-local
on_blast = function(pos, intensity)
if minetest.is_protected(pos, '') then
if core.is_protected(pos, '') then
return
end
local drops = {}
local inv = minetest.get_meta(pos):get_inventory()
local inv = core.get_meta(pos):get_inventory()
local stack_item = inv:get_stack('item', 1)
local stack_trade = inv:get_stack('trade', 1)
@ -138,7 +163,7 @@ minetest.register_node('x_enchanting:table', {
end
drops[#drops + 1] = 'x_enchanting:table'
minetest.remove_node(pos)
core.remove_node(pos)
return drops
end,
@ -149,23 +174,25 @@ minetest.register_node('x_enchanting:table', {
return false
end
local inv = minetest.get_meta(pos):get_inventory()
local inv = core.get_meta(pos):get_inventory()
return inv:is_empty('item')
and inv:is_empty('trade')
and not minetest.is_protected(pos, player:get_player_name())
and not core.is_protected(pos, player:get_player_name())
end,
---@diagnostic disable-next-line: unused-local
on_rotate = function(pos, node, user, mode, new_param2)
return false
end,
---@param pos Vector
---@param elapsed number
---@diagnostic disable-next-line: unused-local
on_timer = function(pos, elapsed)
-- entity
local table_scroll = minetest.get_objects_inside_radius(pos, 0.9)
local table_scroll = core.get_objects_inside_radius(pos, 0.9)
if #table_scroll == 0 then
minetest.add_entity({ x = pos.x, y = pos.y + 0.7, z = pos.z }, 'x_enchanting:table_scroll')
core.add_entity({ x = pos.x, y = pos.y + 0.7, z = pos.z }, 'x_enchanting:table_scroll')
end
local particlespawner_def = {
@ -185,7 +212,7 @@ minetest.register_node('x_enchanting:table', {
glow = 1
}
if minetest.has_feature({ dynamic_add_media_table = true, particlespawner_tweenable = true }) then
if core.has_feature({ dynamic_add_media_table = true, particlespawner_tweenable = true }) then
-- new syntax, after v5.6.0
particlespawner_def = {
amount = 50,
@ -216,10 +243,10 @@ minetest.register_node('x_enchanting:table', {
}
end
minetest.add_particlespawner(particlespawner_def)
core.add_particlespawner(particlespawner_def)
---bookshelfs
local bookshelfs = minetest.find_nodes_in_area(
local bookshelfs = core.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ 'default:bookshelf', 'group:bookshelf' }
@ -230,6 +257,7 @@ minetest.register_node('x_enchanting:table', {
end
-- symbol particles
---@diagnostic disable-next-line: unused-local
for i = 1, 10, 1 do
local pos_random = bookshelfs[math.random(1, #bookshelfs)]
local x = pos.x - pos_random.x
@ -239,8 +267,8 @@ minetest.register_node('x_enchanting:table', {
local rand2 = math.random(10, 500) / 100
local rand3 = math.random(50, 200) / 100
minetest.after(rand2, function()
minetest.add_particle({
core.after(rand2, function()
core.add_particle({
pos = pos_random,
velocity = { x = x, y = 2 - y, z = z },
acceleration = { x = 0, y = rand1, z = 0 },
@ -256,7 +284,7 @@ minetest.register_node('x_enchanting:table', {
end,
---@param pos Vector
on_destruct = function(pos)
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 0.9)) do
for _, obj in ipairs(core.get_objects_inside_radius(pos, 0.9)) do
if obj
and obj:get_luaentity()
and obj:get_luaentity().name == 'x_enchanting:table_scroll'
@ -271,20 +299,21 @@ minetest.register_node('x_enchanting:table', {
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local st_meta = stack:get_meta()
local st_name = stack:get_name()
local is_enchanted = st_meta:get_int('is_enchanted')
if listname == 'item'
and minetest.get_item_group(st_name, 'enchantability') > 0
and core.get_item_group(st_name, 'enchantability') > 0
and is_enchanted ~= 1
then
return stack:get_count()
elseif listname == 'trade'
and (
st_name == 'default:mese_crystal'
or minetest.get_item_group(st_name, 'enchanting_trade') > 0
or core.get_item_group(st_name, 'enchanting_trade') > 0
)
and is_enchanted ~= 1
then
@ -298,6 +327,7 @@ minetest.register_node('x_enchanting:table', {
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local st_name = stack:get_name()
@ -306,7 +336,7 @@ minetest.register_node('x_enchanting:table', {
elseif listname == 'trade'
and (
st_name == 'default:mese_crystal'
or minetest.get_item_group(st_name, 'enchanting_trade') > 0
or core.get_item_group(st_name, 'enchanting_trade') > 0
)
then
return stack:get_count()
@ -321,6 +351,7 @@ minetest.register_node('x_enchanting:table', {
---@param to_index number
---@param count number
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
return 0
end,
@ -329,21 +360,28 @@ minetest.register_node('x_enchanting:table', {
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
on_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
local meta = core.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(
local bookshelfs = core.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ '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,
core.registered_tools[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
meta:set_string('formspec', formspec)
@ -358,21 +396,28 @@ minetest.register_node('x_enchanting:table', {
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
on_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
local meta = core.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(
local bookshelfs = core.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ '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,
core.registered_tools[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
meta:set_string('formspec', formspec)
@ -387,6 +432,7 @@ minetest.register_node('x_enchanting:table', {
---@param formname string
---@param fields table
---@param sender ObjectRef
---@diagnostic disable-next-line: unused-local
on_receive_fields = function(pos, formname, fields, sender)
local p_name = sender:get_player_name()
@ -409,16 +455,15 @@ minetest.register_node('x_enchanting:table', {
return
end
local inv = minetest.get_meta(pos):get_inventory()
local inv = core.get_meta(pos):get_inventory()
if inv:is_empty('trade') or inv:is_empty('item') then
return
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
@ -443,7 +488,7 @@ minetest.register_node('x_enchanting:table', {
--- Entity (Scroll)
----
minetest.register_entity('x_enchanting:table_scroll', {
core.register_entity('x_enchanting:table_scroll', {
initial_properties = {
visual = 'mesh',
mesh = 'x_enchanting_scroll.b3d',
@ -466,12 +511,14 @@ minetest.register_entity('x_enchanting:table_scroll', {
},
---@param self table
---@param killer ObjectRef
---@diagnostic disable-next-line: unused-local
on_death = function(self, killer)
self.object:remove()
end,
---@param self table
---@param staticdata StringAbstract
---@param dtime_s number
---@diagnostic disable-next-line: unused-local
on_activate = function(self, staticdata, dtime_s)
self._scroll_closed = true
self._tablechecktimer = 5
@ -485,6 +532,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
---@param self table
---@param dtime number
---@param moveresult? table
---@diagnostic disable-next-line: unused-local
on_step = function(self, dtime, moveresult)
local pos = self.object:get_pos()
@ -495,7 +543,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- table
if self._tablechecktimer <= 0 then
self._tablechecktimer = 5
local node = minetest.get_node({ x = pos.x, y = pos.y - 0.7, z = pos.z })
local node = core.get_node({ x = pos.x, y = pos.y - 0.7, z = pos.z })
if node.name ~= 'x_enchanting:table' then
-- remove entity when no table under it
@ -506,13 +554,14 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- player
if self._playerchecktimer <= 0 then
self._playerchecktimer = 1
local objects = minetest.get_objects_inside_radius(pos, 5)
local objects = core.get_objects_inside_radius(pos, 5)
-- inital value
local shortest_distance = 10
local found_player = false
if #objects > 0 then
---@diagnostic disable-next-line: unused-local
for i, obj in ipairs(objects) do
if obj:is_player() and obj:get_pos() then
-- player
@ -520,6 +569,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
local distance = vector.distance(pos, obj:get_pos())
if distance < shortest_distance then
shortest_distance = distance
self._player = obj
end
end
@ -537,7 +587,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
self._scroll_closed = false
self.object:set_animation(unpack(XEnchanting.scroll_animations.scroll_open))
minetest.sound_play('x_enchanting_scroll', {
core.sound_play('x_enchanting_scroll', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
@ -546,7 +596,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
self._scroll_closed = true
self.object:set_animation(unpack(XEnchanting.scroll_animations.scroll_close))
minetest.sound_play('x_enchanting_scroll', {
core.sound_play('x_enchanting_scroll', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
@ -557,7 +607,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- rotation
if self._player and self._player:get_pos() then
local direction = vector.direction(pos, self._player:get_pos())
self.object:set_yaw(minetest.dir_to_yaw(direction))
self.object:set_yaw(core.dir_to_yaw(direction))
else
self.object:set_rotation({
x = self._last_rotation.x,
@ -573,6 +623,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
---@param dir Vector
---@param damage number
---@return boolean | nil
---@diagnostic disable-next-line: unused-local
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
return true
end
@ -582,17 +633,17 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- Recipe
---
if minetest.get_modpath('xdecor') then
minetest.register_craft({
if core.get_modpath('xdecor') or core.get_modpath('enchanting') then
core.register_craft({
output = 'x_enchanting:table',
recipe = {
{ 'default:book', '', '' },
{ 'default:diamond', 'default:obsidian', 'default:diamond' },
{ 'default:diamond', 'xdecor:enchantment_table', 'default:diamond' },
{ 'default:obsidian', 'default:obsidian', 'default:obsidian' }
}
})
else
minetest.register_craft({
core.register_craft({
output = 'x_enchanting:table',
recipe = {
{ '', 'default:book', '' },

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 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,8 @@
---@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 registered_craftitems table<string, table> Map of registered craft items, 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 +89,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

@ -45,6 +45,7 @@
---@field get_rotation fun(self: ObjectRef): Vector returns the rotation, a vector (radians)
---@field get_attach fun(self: ObjectRef): any Returns parent, bone, position, rotation, forced_visible, or nil if it isn't attached.
---@field set_attach fun(self: ObjectRef, parent: ObjectRef, bone?: string, position?: Vector, rotation?: Vector, forced_visible?: boolean): any Returns parent, bone, position, rotation, forced_visible, or nil if it isn't attached.
---@field drops table Custom for mob drops
---`ObjectRef` armor groups
---@class ObjectRefArmorGroups

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 | nil
---@field descriptions {["enchantments_desc"]: string, ["enchantments_desc_masked"]: string }