Compare commits

...

29 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
42 changed files with 1858 additions and 293 deletions

View File

@ -13,5 +13,6 @@
"media_license": "CC-BY-SA-4.0", "media_license": "CC-BY-SA-4.0",
"repo": "https://bitbucket.org/minetest_gamers/x_enchanting/src/master/", "repo": "https://bitbucket.org/minetest_gamers/x_enchanting/src/master/",
"issue_tracker": "https://bitbucket.org/minetest_gamers/x_enchanting/issues?status=new&status=open", "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 "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 max_line_length = false
exclude_files = { exclude_files = {
'./scripts', "./scripts",
'./bin', "./bin",
'./logs', "./logs",
'./node_modules', "./node_modules",
'./sounds', "./sounds",
'./textures', "./textures",
'./models', "./models",
'./docs', "./docs",
'./locale', "./locale",
'./types', "./types",
} }
globals = { globals = {
'minetest', "core",
'XEnchanting' "XEnchanting"
} }
read_globals = { read_globals = {
"DIR_DELIM", "INIT", "DIR_DELIM", "INIT",
"core",
"dump", "dump2", "dump", "dump2",
"Raycast", "Raycast",

View File

@ -1,8 +1,7 @@
GNU LESSER GENERAL PUBLIC LICENSE GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999 Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc. Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. 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 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 author's reputation will not be affected by problems that might be
introduced by others. introduced by others.
Finally, software patents pose a constant threat to the existence of Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a 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 "work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must former contains code derived from the library, whereas the latter must
be combined with the library in order to run. be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 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, 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 and you may at your option offer warranty protection in exchange for a
fee. fee.
2. You may modify your copy or copies of the Library or any portion 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 of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1 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 ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in that version instead if you wish.) Do not make any other change in
these notices. these notices.
Once this change is made in a given copy, it is irreversible for 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 that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy. 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. distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6, Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself. whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or 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 link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work 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 accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you use both them and the Library together in an executable that you
distribute. distribute.
7. You may place library facilities that are a work based on the 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 Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined 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. restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with You are not responsible for enforcing compliance by third parties with
this License. this License.
11. If, as a consequence of a court judgment or allegation of patent 11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues), infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or 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 the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by license version number, you may choose any version ever published by
the Free Software Foundation. the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free 14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these, programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is 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 SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. DAMAGES.
END OF TERMS AND CONDITIONS ### Textures
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest **CC-BY-SA-4.0, Pixel Perfection by XSSheep**, https://minecraft.curseforge.com/projects/pixel-perfection-freshly-updated
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).
To apply these terms, attach the following notices to the library. It is - x_enchanting_symbol_1.png
safest to attach them to the start of each source file to most effectively - x_enchanting_symbol_2.png
convey the exclusion of warranty; and each file should have at least the - x_enchanting_symbol_3.png
"copyright" line and a pointer to where the full notice is found. - 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.> **CC-BY-SA-4.0, by SaKeL**
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or - x_enchanting_image_button_disabled.png
modify it under the terms of the GNU Lesser General Public - x_enchanting_image_button.png
License as published by the Free Software Foundation; either - x_enchanting_image_trade_1.png -- Derived from a texture by VanessaE (CC BY-SA 3.0)
version 2.1 of the License, or (at your option) any later version. - 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, ### Models
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 **CC-BY-SA-4.0, by SaKeL**
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
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 ### Sounds
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the **CC-BY-3.0, Kostas17**, https://freesound.org
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990 - x_enchanting_enchant.ogg
Ty Coon, President of Vice
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

View File

@ -6,9 +6,10 @@ Adds Enchanting Mechanics and API.
## Features ## Features
* does not add new tools/items, it's using MT API to change existing items instead
* adds enchanting table * adds enchanting table
* supports all registered tools with known tool groups: pickaxe, shovel, axe, sword, e.g. `groups = {pickaxe = 1}` * supports all registered tools with known tool groups: pickaxe, shovel, axe, sword, e.g. `groups = {pickaxe = 1}`
* support all bows with group `{bow = 1}` * supports all bows with group `{bow = 1}`
* supports `default:bookshelf` or anything with `group:bookshelf` * supports `default:bookshelf` or anything with `group:bookshelf`
* enchanting trade is for `default:mese_crystal` or anything with `groups = {enchanting_trade = 1}` * enchanting trade is for `default:mese_crystal` or anything with `groups = {enchanting_trade = 1}`
* adds enchantability for all MT default tools, for custom tools the enchantability can be set in the item group, e.g. `groups = {enchantability = 15}` * adds enchantability for all MT default tools, for custom tools the enchantability can be set in the item group, e.g. `groups = {enchantability = 15}`
@ -16,8 +17,8 @@ Adds Enchanting Mechanics and API.
* enchanted items have detailed enchantments description/short description * enchanted items have detailed enchantments description/short description
* mesh node model * mesh node model
* mesh entity model and animations * mesh entity model and animations
* tool texture will have enchanted glint
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 * adds grind stone to remove enchantments (excluding curses)
## How To ## How To
@ -97,7 +98,7 @@ groups: sword
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. 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 Supported: mobs_monster, mobs_animal, animalia
groups: sword groups: sword
@ -177,86 +178,10 @@ end
## Optional Dependencies ## Optional Dependencies
- xdecor (adjusts conflicting recipe) - xdecor (adjusts conflicting recipe)
- item_drop (make sure fortune will execute before item_drop mod overrides `handle_node_drops`)
## License: ## License:
### Code ### Code
GNU Lesser General Public License v2.1 or later (see included LICENSE file) 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_gui_slot_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC BY-SA 4.0)
- x_enchanting_gui_cloth_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC BY-SA 4.0)
- x_enchanting_gui_cloth_trade_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC BY-SA 4.0)
- x_enchanting_gui_formbg.png -- Derived from texture Pixel Perfection by XSSheep (CC BY-SA 4.0)
- x_enchanting_gui_hb_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC BY-SA 4.0)
### Models
**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

195
api.lua
View File

@ -1,4 +1,22 @@
local S = minetest.get_translator(minetest.get_current_modname()) --[[
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())
---@type XEnchanting ---@type XEnchanting
XEnchanting = { XEnchanting = {
@ -177,7 +195,8 @@ XEnchanting = {
weight = 1, weight = 1,
secondary = true, secondary = true,
-- all applicable -- all applicable
groups = nil groups = nil,
cursed = true
}, },
knockback = { knockback = {
name = S('Knockback'), name = S('Knockback'),
@ -288,16 +307,28 @@ local function get_table_length(table)
return length return length
end 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) 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' return 'pickaxe'
elseif minetest.get_item_group(name, 'shovel') > 0 then elseif core.get_item_group(name, 'shovel') > 0 then
return 'shovel' return 'shovel'
elseif minetest.get_item_group(name, 'axe') > 0 then elseif core.get_item_group(name, 'axe') > 0 then
return 'axe' return 'axe'
elseif minetest.get_item_group(name, 'sword') > 0 then elseif core.get_item_group(name, 'sword') > 0 then
return 'sword' return 'sword'
elseif minetest.get_item_group(name, 'bow') > 0 then elseif core.get_item_group(name, 'bow') > 0 then
return 'bow' return 'bow'
end end
@ -305,7 +336,7 @@ function XEnchanting.has_tool_group(self, name)
end end
function XEnchanting.set_tool_enchantability(self, tool_def) function XEnchanting.set_tool_enchantability(self, tool_def)
if minetest.get_item_group(tool_def.name, 'enchantability') > 0 then if core.get_item_group(tool_def.name, 'enchantability') > 0 then
-- enchantability is already set, we dont need to override the item -- enchantability is already set, we dont need to override the item
return return
end end
@ -316,15 +347,17 @@ function XEnchanting.set_tool_enchantability(self, tool_def)
_enchantability = self.tools_enchantability[tool_def.name] _enchantability = self.tools_enchantability[tool_def.name]
end end
minetest.override_item(tool_def.name, { core.override_item(tool_def.name, {
groups = mergeTables(tool_def.groups, { enchantability = _enchantability }) groups = mergeTables(tool_def.groups, { enchantability = _enchantability })
}) })
end end
---@diagnostic disable-next-line: unused-local
function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantments) function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantments)
local tool_stack = ItemStack({ name = tool_def.name }) local tool_stack = ItemStack({ name = tool_def.name })
local tool_capabilities = tool_stack:get_tool_capabilities() local tool_capabilities = tool_stack:get_tool_capabilities()
---@diagnostic disable-next-line: unused-local
for i, enchantment in ipairs(enchantments) do for i, enchantment in ipairs(enchantments) do
-- Efficiency -- Efficiency
if enchantment.id == 'efficiency' then if enchantment.id == 'efficiency' then
@ -439,6 +472,7 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
return tool_capabilities return tool_capabilities
end end
---@diagnostic disable-next-line: unused-local
function XEnchanting.get_randomseed(self) function XEnchanting.get_randomseed(self)
return tonumber(tostring(os.time()):reverse():sub(1, 9)) --[[@as integer]] return tonumber(tostring(os.time()):reverse():sub(1, 9)) --[[@as integer]]
end end
@ -447,6 +481,7 @@ function XEnchanting.get_enchanted_descriptions(self, enchantments)
local enchantments_desc = {} local enchantments_desc = {}
local enchantments_desc_masked = {} local enchantments_desc_masked = {}
---@diagnostic disable-next-line: unused-local
for i, enchantment in ipairs(enchantments) do for i, enchantment in ipairs(enchantments) do
local add_roman_numbers = true local add_roman_numbers = true
@ -471,7 +506,7 @@ function XEnchanting.get_enchanted_descriptions(self, enchantments)
end 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') .. '\n' .. table.concat(enchantments_desc, '\n')
enchantments_desc_masked = table.concat(enchantments_desc_masked, '') .. '..?' enchantments_desc_masked = table.concat(enchantments_desc_masked, '') .. '..?'
@ -481,6 +516,10 @@ function XEnchanting.get_enchanted_descriptions(self, enchantments)
} }
end 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) function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name)
local data = self.form_context[player_name].data local data = self.form_context[player_name].data
@ -491,9 +530,9 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
local capabilities = data.slots[level].tool_cap_data local capabilities = data.slots[level].tool_cap_data
local description = data.slots[level].descriptions.enchantments_desc local description = data.slots[level].descriptions.enchantments_desc
local final_enchantments = data.slots[level].final_enchantments local final_enchantments = data.slots[level].final_enchantments
local inv = minetest.get_meta(pos):get_inventory() local inv = core.get_meta(pos):get_inventory()
local tool_def = minetest.registered_tools[itemstack:get_name()] local tool_def = core.registered_tools[itemstack:get_name()]
local node_meta = minetest.get_meta(pos) local node_meta = core.get_meta(pos)
if not tool_def then if not tool_def then
return return
@ -503,6 +542,7 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
---@type table<string, {["value"]: number}> ---@type table<string, {["value"]: number}>
local final_enchantments_meta = {} local final_enchantments_meta = {}
---@diagnostic disable-next-line: unused-local
for i, enchantment in ipairs(final_enchantments) do for i, enchantment in ipairs(final_enchantments) do
stack_meta:set_float('is_' .. enchantment.id, enchantment.value) stack_meta:set_float('is_' .. enchantment.id, enchantment.value)
-- store only necessary data, keeping the meta optimized -- store only necessary data, keeping the meta optimized
@ -515,7 +555,23 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
stack_meta:set_string('description', itemstack:get_description() .. '\n' .. description) stack_meta:set_string('description', itemstack:get_description() .. '\n' .. description)
stack_meta:set_string('short_description', S('Enchanted') .. ' ' .. itemstack:get_short_description()) stack_meta:set_string('short_description', S('Enchanted') .. ' ' .. itemstack:get_short_description())
stack_meta:set_int('is_enchanted', 1) stack_meta:set_int('is_enchanted', 1)
stack_meta:set_string('x_enchanting', minetest.serialize(final_enchantments_meta)) stack_meta:set_string('x_enchanting', core.serialize(final_enchantments_meta))
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) inv:set_stack('item', 1, itemstack)
@ -529,7 +585,7 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
local formspec = self:get_formspec(pos, player_name) local formspec = self:get_formspec(pos, player_name)
node_meta:set_string('formspec', formspec) node_meta:set_string('formspec', formspec)
minetest.sound_play('x_enchanting_enchant', { core.sound_play('x_enchanting_enchant', {
gain = 0.3, gain = 0.3,
pos = pos, pos = pos,
max_hear_distance = 10 max_hear_distance = 10
@ -553,7 +609,7 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
glow = 1 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 -- new syntax, after v5.6.0
particlespawner_def = { particlespawner_def = {
amount = 50, amount = 50,
@ -585,7 +641,7 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
} }
end end
minetest.add_particlespawner(particlespawner_def) core.add_particlespawner(particlespawner_def)
end end
function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_def) function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_def)
@ -611,8 +667,9 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
if not enchantment_def.groups then if not enchantment_def.groups then
group_enchantments[enchantment_name] = enchantment_def group_enchantments[enchantment_name] = enchantment_def
else else
---@diagnostic disable-next-line: unused-local
for i, group in ipairs(enchantment_def.groups) do for i, group in ipairs(enchantment_def.groups) do
if minetest.get_item_group(tool_def.name, group) > 0 then if core.get_item_group(tool_def.name, group) > 0 then
group_enchantments[enchantment_name] = enchantment_def group_enchantments[enchantment_name] = enchantment_def
break break
end end
@ -637,7 +694,7 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
local chosen_enchantment_level = slot_lvl local chosen_enchantment_level = slot_lvl
-- Applying modifiers to the enchantment level -- 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 -- Generate a random number between 1 and 1+(enchantability/2), with a triangular distribution
local rand_enchantability = 1 + math.random(enchantability / 4 + 1) + math.random(enchantability / 4 + 1) local rand_enchantability = 1 + math.random(enchantability / 4 + 1) + math.random(enchantability / 4 + 1)
-- Choose the enchantment level -- Choose the enchantment level
@ -697,6 +754,7 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
local total_weight = 0 local total_weight = 0
-- calculate total weight -- calculate total weight
---@diagnostic disable-next-line: unused-local
for j, enchantment in ipairs(possible_enchantments) do for j, enchantment in ipairs(possible_enchantments) do
total_weight = total_weight + self.enchantment_defs[enchantment.id].weight total_weight = total_weight + self.enchantment_defs[enchantment.id].weight
end end
@ -741,11 +799,17 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
table.remove(possible_enchantments, idx) table.remove(possible_enchantments, idx)
end end
end end
else else
local probability = (probability_level + 1) / 50 local probability = (probability_level + 1) / 50
table.insert(final_enchantments, rand_ench) 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) table.remove(possible_enchantments, rand_ench_idx)
for idx, value in pairs(possible_enchantments) do for idx, value in pairs(possible_enchantments) do
@ -815,12 +879,13 @@ local function get_list_bg(x, y)
return out return out
end end
local function get_formspec_bg(player_name) local function get_formspec_bg(player_name, bg_img)
local info = minetest.get_player_information(player_name) local bg_image = bg_img and bg_img or 'x_enchanting_gui_formbg.png'
local bg = 'background[5,5;1,1;x_enchanting_gui_formbg.png;true]' local info = core.get_player_information(player_name)
local bg = 'background[5,5;1,1;' .. bg_image .. ';true]'
if info.formspec_version > 1 then if info.formspec_version > 1 then
bg = 'background9[5,5;1,1;x_enchanting_gui_formbg.png;true;10]' bg = 'background9[5,5;1,1;' .. bg_image .. ';true;10]'
end end
return bg return bg
@ -828,7 +893,7 @@ end
function XEnchanting.get_formspec(self, pos, player_name, data) function XEnchanting.get_formspec(self, pos, player_name, data)
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z 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 ---@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]' 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 ---@diagnostic disable-next-line: codestyle-check
@ -869,10 +934,10 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
if inv:get_stack('trade', 1):get_count() >= i then if inv:get_stack('trade', 1):get_count() >= i then
---@diagnostic disable-next-line: codestyle-check ---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button.png;slot_' .. i .. ';' .. slot.descriptions.enchantments_desc_masked .. minetest.formspec_escape(' [' .. slot.level .. ']') .. ']' 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 else
---@diagnostic disable-next-line: codestyle-check ---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';' .. slot.descriptions.enchantments_desc_masked .. minetest.formspec_escape(' [' .. slot.level .. ']') .. ']' 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 end
formspec[#formspec + 1] = 'image[2.3,' .. -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;]'
@ -907,3 +972,77 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
return table.concat(formspec, '') return table.concat(formspec, '')
end 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.

View File

@ -31,36 +31,6 @@ pipelines:
script: script:
- nvm use v17.2.0 - nvm use v17.2.0
- npm run lua-diagnostics - 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: tags:
"*": "*":
- step: - step:
@ -73,24 +43,24 @@ pipelines:
- nvm install v17.2.0 - nvm install v17.2.0
- npm i -g npm@8 - npm i -g npm@8
- npm ci - npm ci
- parallel: # - parallel:
- step: - step:
name: Lua Check name: Lua Check
script: script:
- apt-get update - apt-get update
- apt-get -y install lua5.1 - apt-get -y install lua5.1
- apt-get -y install luarocks - apt-get -y install luarocks
- luarocks install luacheck - luarocks install luacheck
- luacheck . - luacheck .
- step: # - step:
name: Lua Diagnostics # name: Lua Diagnostics
caches: # caches:
- node-modules # - node-modules
- npm # - npm
- nvm # - nvm
script: # script:
- nvm use v17.2.0 # - nvm use v17.2.0
- npm run lua-diagnostics # - npm run lua-diagnostics
- step: - step:
name: Deploy to ContentDB name: Deploy to ContentDB
caches: 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' }
}
})

128
init.lua
View File

@ -1,11 +1,27 @@
-- X Enchanting --[[
-- by SaKeL X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2025 SaKeL
local path = minetest.get_modpath('x_enchanting') This library is free software; you can redistribute it and/or
local mod_start_time = minetest.get_us_time() 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 .. '/api.lua')
dofile(path .. '/table.lua') dofile(path .. '/table.lua')
dofile(path .. '/grindstone.lua')
---Check if string X starts with string Y ---Check if string X starts with string Y
---@param str string ---@param str string
@ -15,23 +31,23 @@ local function starts_with(str, start)
return str:sub(1, #start) == start return str:sub(1, #start) == start
end end
minetest.register_on_mods_loaded(function() core.register_on_mods_loaded(function()
-- Tools override -- Tools override
for name, tool_def in pairs(minetest.registered_tools) do for name, tool_def in pairs(core.registered_tools) do
if XEnchanting:has_tool_group(name) then if XEnchanting:has_tool_group(name) then
XEnchanting:set_tool_enchantability(tool_def) XEnchanting:set_tool_enchantability(tool_def)
end end
end end
-- Ores override - Fortune -- Ores override - Fortune
for _, def in pairs(minetest.registered_ores) do for _, def in pairs(core.registered_ores) do
if not XEnchanting.registered_ores[def.ore] then if not XEnchanting.registered_ores[def.ore] then
XEnchanting.registered_ores[def.ore] = true XEnchanting.registered_ores[def.ore] = true
end end
end end
-- Entities override - Looting -- Entities override - Looting
for name, def in pairs(minetest.registered_entities) do for name, def in pairs(core.registered_entities) do
if starts_with(name, 'mobs_animal:') if starts_with(name, 'mobs_animal:')
or starts_with(name, 'mobs_monster:') or starts_with(name, 'mobs_monster:')
then then
@ -87,7 +103,7 @@ minetest.register_on_mods_loaded(function()
stack:set_count(stack:get_count() * chance) stack:set_count(stack:get_count() * chance)
if stack:get_count() > 0 then if stack:get_count() > 0 then
minetest.item_drop(stack, puncher, pos) core.item_drop(stack, puncher, pos)
end end
end end
end end
@ -95,11 +111,69 @@ minetest.register_on_mods_loaded(function()
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 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 XEnchanting.form_context[player:get_player_name()] = nil
if not XEnchanting.player_seeds[player:get_player_name()] then if not XEnchanting.player_seeds[player:get_player_name()] then
@ -107,20 +181,21 @@ minetest.register_on_joinplayer(function(player, last_login)
end end
end) end)
minetest.register_on_leaveplayer(function(player, timed_out) ---@diagnostic disable-next-line: unused-local
core.register_on_leaveplayer(function(player, timed_out)
XEnchanting.form_context[player:get_player_name()] = nil XEnchanting.form_context[player:get_player_name()] = nil
end) end)
local old_handle_node_drops = minetest.handle_node_drops local old_handle_node_drops = core.handle_node_drops
function minetest.handle_node_drops(pos, drops, digger) function core.handle_node_drops(pos, drops, digger)
if not digger if not digger
or not digger:is_player() or not digger:is_player()
then then
return old_handle_node_drops(pos, drops, digger) return old_handle_node_drops(pos, drops, digger)
end end
local node = minetest.get_node(pos) local node = core.get_node(pos)
local wield_stack = digger:get_wielded_item() local wield_stack = digger:get_wielded_item()
local wield_stack_meta = wield_stack:get_meta() local wield_stack_meta = wield_stack:get_meta()
@ -132,13 +207,13 @@ function minetest.handle_node_drops(pos, drops, digger)
for _, itemstring in ipairs(drops) do for _, itemstring in ipairs(drops) do
if XEnchanting.registered_ores[node.name] if XEnchanting.registered_ores[node.name]
or minetest.get_item_group(node.name, 'stone') > 0 or core.get_item_group(node.name, 'stone') > 0
or minetest.get_item_group(node.name, 'soil') > 0 or core.get_item_group(node.name, 'soil') > 0
or minetest.get_item_group(node.name, 'sand') > 0 or core.get_item_group(node.name, 'sand') > 0
or minetest.get_item_group(node.name, 'snowy') > 0 or core.get_item_group(node.name, 'snowy') > 0
or minetest.get_item_group(node.name, 'slippery') > 0 or core.get_item_group(node.name, 'slippery') > 0
or minetest.get_item_group(node.name, 'tree') > 0 or core.get_item_group(node.name, 'tree') > 0
or minetest.get_item_group(node.name, 'leaves') > 0 or core.get_item_group(node.name, 'leaves') > 0
then then
local tool_capabilities = wield_stack:get_tool_capabilities() local tool_capabilities = wield_stack:get_tool_capabilities()
local stack = ItemStack(itemstring) local stack = ItemStack(itemstring)
@ -163,7 +238,7 @@ function minetest.handle_node_drops(pos, drops, digger)
local silk_touch = wield_stack_meta:get_float('is_silk_touch') local silk_touch = wield_stack_meta:get_float('is_silk_touch')
if silk_touch > 0 if silk_touch > 0
and minetest.get_item_group(node.name, 'no_silktouch') == 0 and core.get_item_group(node.name, 'no_silktouch') == 0
then then
-- drop raw item/node -- drop raw item/node
return old_handle_node_drops(pos, { ItemStack(node.name) }, digger) return old_handle_node_drops(pos, { ItemStack(node.name) }, digger)
@ -172,7 +247,8 @@ function minetest.handle_node_drops(pos, drops, digger)
return old_handle_node_drops(pos, drops, digger) return old_handle_node_drops(pos, drops, digger)
end end
minetest.register_on_player_hpchange(function(player, hp_change, reason) ---@diagnostic disable-next-line: unused-local
core.register_on_player_hpchange(function(player, hp_change, reason)
-- Curse of Vanishing -- Curse of Vanishing
if (player:get_hp() + hp_change) <= 0 then if (player:get_hp() + hp_change) <= 0 then
-- Going to die -- Going to die
@ -197,9 +273,9 @@ minetest.register_on_player_hpchange(function(player, hp_change, reason)
end, true) end, true)
-- Knockback (only for players) -- Knockback (only for players)
local old_calculate_knockback = minetest.calculate_knockback local old_calculate_knockback = core.calculate_knockback
function minetest.calculate_knockback(player, hitter, time_from_last_punch, function core.calculate_knockback(player, hitter, time_from_last_punch,
tool_capabilities, dir, distance, damage) tool_capabilities, dir, distance, damage)
if hitter and hitter:is_player() then if hitter and hitter:is_player() then
local hitter_wield_stack = hitter:get_wielded_item() local hitter_wield_stack = hitter:get_wielded_item()
@ -231,6 +307,6 @@ function minetest.calculate_knockback(player, hitter, time_from_last_punch,
tool_capabilities, dir, distance, damage) tool_capabilities, dir, distance, damage)
end end
local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000 local mod_end_time = (core.get_us_time() - mod_start_time) / 1000000
print('[Mod] x_enchanting loaded.. [' .. mod_end_time .. 's]') print('[Mod] x_enchanting loaded.. [' .. mod_end_time .. 's]')

View File

@ -1,6 +1,6 @@
name = x_enchanting name = x_enchanting
description = Adds Enchanting Mechanics and API. description = Adds Enchanting Mechanics and API.
depends = depends =
optional_depends = xdecor optional_depends = xdecor, item_drop, stairs, enchanting
supported_games = minetest_game supported_games = minetest_game
min_minetest_version = 5.4 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: 385 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 fetch from 'node-fetch'
import yargs from 'yargs/yargs' import yargs from 'yargs/yargs'
import {hideBin} from 'yargs/helpers' import {hideBin} from 'yargs/helpers'
@ -11,7 +29,7 @@ try {
ref: 'master' 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', method: 'POST',
body: JSON.stringify(body), body: JSON.stringify(body),
headers: { 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 path from 'node:path'
import * as fs from 'node:fs' import * as fs from 'node:fs'
import {exec} from 'node:child_process' import {exec} from 'node:child_process'

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.

140
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 --- Table Node
---- ----
minetest.register_node('x_enchanting:table', { core.register_node('x_enchanting:table', {
description = S('Enchanting Table'), description = S('Enchanting Table'),
short_description = S('Enchanting Table'), short_description = S('Enchanting Table'),
drawtype = 'mesh', drawtype = 'mesh',
mesh = 'x_enchanting_table.obj', mesh = 'x_enchanting_table.obj',
tiles = { 'x_enchanting_table.png' }, tiles = { 'x_enchanting_table.png' },
use_texture_alpha = 'clip',
paramtype = 'light', paramtype = 'light',
paramtype2 = 'facedir', paramtype2 = 'facedir',
walkable = true, walkable = true,
@ -45,22 +63,23 @@ minetest.register_node('x_enchanting:table', {
light_source = 6, light_source = 6,
---@param pos Vector ---@param pos Vector
on_construct = function(pos) on_construct = function(pos)
local meta = minetest.get_meta(pos) local meta = core.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
meta:set_string('infotext', S('Enchanting Table')) meta:set_string('infotext', S('Enchanting Table'))
meta:set_string('owner', '') meta:set_string('owner', '')
inv:set_size('item', 1) inv:set_size('item', 1)
inv:set_size('trade', 1) inv:set_size('trade', 1)
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')
minetest.get_node_timer(pos):start(5) core.get_node_timer(pos):start(5)
end, end,
---@param pos Vector ---@param pos Vector
---@param placer ObjectRef | nil ---@param placer ObjectRef | nil
---@param itemstack ItemStack ---@param itemstack ItemStack
---@param pointed_thing PointedThingDef ---@param pointed_thing PointedThingDef
---@diagnostic disable-next-line: unused-local
after_place_node = function(pos, placer, itemstack, pointed_thing) 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 if not placer then
return return
@ -79,35 +98,36 @@ minetest.register_node('x_enchanting:table', {
---@param clicker ObjectRef ---@param clicker ObjectRef
---@param itemstack ItemStack ---@param itemstack ItemStack
---@param pointed_thing? PointedThingDef ---@param pointed_thing? PointedThingDef
---@diagnostic disable-next-line: unused-local
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) 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() 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 return itemstack
end end
minetest.sound_play('x_enchanting_scroll', { core.sound_play('x_enchanting_scroll', {
gain = 0.3, gain = 0.3,
pos = pos, pos = pos,
max_hear_distance = 10 max_hear_distance = 10
}, true) }, true)
-- bookshelfs -- 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, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 }, { x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ 'default:bookshelf', 'group:bookshelf' } { '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') and inv:get_stack('item', 1):get_meta():get_int('is_enchanted') ~= 1 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 item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data( local data = XEnchanting:get_enchantment_data(
clicker, clicker,
#bookshelfs, #bookshelfs,
minetest.registered_tools[item_stack:get_name()] core.registered_tools[item_stack:get_name()]
) )
local formspec = XEnchanting:get_formspec(pos, p_name, data) local formspec = XEnchanting:get_formspec(pos, p_name, data)
@ -123,13 +143,14 @@ minetest.register_node('x_enchanting:table', {
---@param pos Vector ---@param pos Vector
---@param intensity? number ---@param intensity? number
---@return table | nil ---@return table | nil
---@diagnostic disable-next-line: unused-local
on_blast = function(pos, intensity) on_blast = function(pos, intensity)
if minetest.is_protected(pos, '') then if core.is_protected(pos, '') then
return return
end end
local drops = {} 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_item = inv:get_stack('item', 1)
local stack_trade = inv:get_stack('trade', 1) local stack_trade = inv:get_stack('trade', 1)
@ -142,7 +163,7 @@ minetest.register_node('x_enchanting:table', {
end end
drops[#drops + 1] = 'x_enchanting:table' drops[#drops + 1] = 'x_enchanting:table'
minetest.remove_node(pos) core.remove_node(pos)
return drops return drops
end, end,
@ -153,23 +174,25 @@ minetest.register_node('x_enchanting:table', {
return false return false
end end
local inv = minetest.get_meta(pos):get_inventory() local inv = core.get_meta(pos):get_inventory()
return inv:is_empty('item') return inv:is_empty('item')
and inv:is_empty('trade') 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, end,
---@diagnostic disable-next-line: unused-local
on_rotate = function(pos, node, user, mode, new_param2) on_rotate = function(pos, node, user, mode, new_param2)
return false return false
end, end,
---@param pos Vector ---@param pos Vector
---@param elapsed number ---@param elapsed number
---@diagnostic disable-next-line: unused-local
on_timer = function(pos, elapsed) on_timer = function(pos, elapsed)
-- entity -- 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 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 end
local particlespawner_def = { local particlespawner_def = {
@ -189,7 +212,7 @@ minetest.register_node('x_enchanting:table', {
glow = 1 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 -- new syntax, after v5.6.0
particlespawner_def = { particlespawner_def = {
amount = 50, amount = 50,
@ -220,10 +243,10 @@ minetest.register_node('x_enchanting:table', {
} }
end end
minetest.add_particlespawner(particlespawner_def) core.add_particlespawner(particlespawner_def)
---bookshelfs ---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, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 }, { x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ 'default:bookshelf', 'group:bookshelf' } { 'default:bookshelf', 'group:bookshelf' }
@ -234,6 +257,7 @@ minetest.register_node('x_enchanting:table', {
end end
-- symbol particles -- symbol particles
---@diagnostic disable-next-line: unused-local
for i = 1, 10, 1 do for i = 1, 10, 1 do
local pos_random = bookshelfs[math.random(1, #bookshelfs)] local pos_random = bookshelfs[math.random(1, #bookshelfs)]
local x = pos.x - pos_random.x local x = pos.x - pos_random.x
@ -243,8 +267,8 @@ minetest.register_node('x_enchanting:table', {
local rand2 = math.random(10, 500) / 100 local rand2 = math.random(10, 500) / 100
local rand3 = math.random(50, 200) / 100 local rand3 = math.random(50, 200) / 100
minetest.after(rand2, function() core.after(rand2, function()
minetest.add_particle({ core.add_particle({
pos = pos_random, pos = pos_random,
velocity = { x = x, y = 2 - y, z = z }, velocity = { x = x, y = 2 - y, z = z },
acceleration = { x = 0, y = rand1, z = 0 }, acceleration = { x = 0, y = rand1, z = 0 },
@ -260,7 +284,7 @@ minetest.register_node('x_enchanting:table', {
end, end,
---@param pos Vector ---@param pos Vector
on_destruct = function(pos) 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 if obj
and obj:get_luaentity() and obj:get_luaentity()
and obj:get_luaentity().name == 'x_enchanting:table_scroll' and obj:get_luaentity().name == 'x_enchanting:table_scroll'
@ -275,20 +299,21 @@ minetest.register_node('x_enchanting:table', {
---@param index number ---@param index number
---@param stack ItemStack ---@param stack ItemStack
---@param player ObjectRef ---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_put = function(pos, listname, index, stack, player) allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local st_meta = stack:get_meta() local st_meta = stack:get_meta()
local st_name = stack:get_name() local st_name = stack:get_name()
local is_enchanted = st_meta:get_int('is_enchanted') local is_enchanted = st_meta:get_int('is_enchanted')
if listname == 'item' 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 and is_enchanted ~= 1
then then
return stack:get_count() return stack:get_count()
elseif listname == 'trade' elseif listname == 'trade'
and ( and (
st_name == 'default:mese_crystal' 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 and is_enchanted ~= 1
then then
@ -302,6 +327,7 @@ minetest.register_node('x_enchanting:table', {
---@param index number ---@param index number
---@param stack ItemStack ---@param stack ItemStack
---@param player ObjectRef ---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_take = function(pos, listname, index, stack, player) allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local st_name = stack:get_name() local st_name = stack:get_name()
@ -310,7 +336,7 @@ minetest.register_node('x_enchanting:table', {
elseif listname == 'trade' elseif listname == 'trade'
and ( and (
st_name == 'default:mese_crystal' 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 then
return stack:get_count() return stack:get_count()
@ -325,6 +351,7 @@ minetest.register_node('x_enchanting:table', {
---@param to_index number ---@param to_index number
---@param count number ---@param count number
---@param player ObjectRef ---@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) allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
return 0 return 0
end, end,
@ -333,24 +360,27 @@ minetest.register_node('x_enchanting:table', {
---@param index number ---@param index number
---@param stack ItemStack ---@param stack ItemStack
---@param player ObjectRef ---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
on_metadata_inventory_put = function(pos, listname, index, stack, player) 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 p_name = player:get_player_name()
local inv = meta:get_inventory() 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 -- 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, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 }, { x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ 'default:bookshelf', 'group:bookshelf' } { 'default:bookshelf', 'group:bookshelf' }
) )
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data( local data = XEnchanting:get_enchantment_data(
player, player,
#bookshelfs, #bookshelfs,
minetest.registered_tools[item_stack:get_name()] core.registered_tools[item_stack:get_name()]
) )
local formspec = XEnchanting:get_formspec(pos, p_name, data) local formspec = XEnchanting:get_formspec(pos, p_name, data)
@ -366,24 +396,27 @@ minetest.register_node('x_enchanting:table', {
---@param index number ---@param index number
---@param stack ItemStack ---@param stack ItemStack
---@param player ObjectRef ---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
on_metadata_inventory_take = function(pos, listname, index, stack, player) 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 p_name = player:get_player_name()
local inv = meta:get_inventory() 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 -- 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, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 }, { x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ 'default:bookshelf', 'group:bookshelf' } { 'default:bookshelf', 'group:bookshelf' }
) )
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data( local data = XEnchanting:get_enchantment_data(
player, player,
#bookshelfs, #bookshelfs,
minetest.registered_tools[item_stack:get_name()] core.registered_tools[item_stack:get_name()]
) )
local formspec = XEnchanting:get_formspec(pos, p_name, data) local formspec = XEnchanting:get_formspec(pos, p_name, data)
@ -399,6 +432,7 @@ minetest.register_node('x_enchanting:table', {
---@param formname string ---@param formname string
---@param fields table ---@param fields table
---@param sender ObjectRef ---@param sender ObjectRef
---@diagnostic disable-next-line: unused-local
on_receive_fields = function(pos, formname, fields, sender) on_receive_fields = function(pos, formname, fields, sender)
local p_name = sender:get_player_name() local p_name = sender:get_player_name()
@ -421,7 +455,7 @@ minetest.register_node('x_enchanting:table', {
return return
end 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 if inv:is_empty('trade') or inv:is_empty('item') then
return return
@ -454,7 +488,7 @@ minetest.register_node('x_enchanting:table', {
--- Entity (Scroll) --- Entity (Scroll)
---- ----
minetest.register_entity('x_enchanting:table_scroll', { core.register_entity('x_enchanting:table_scroll', {
initial_properties = { initial_properties = {
visual = 'mesh', visual = 'mesh',
mesh = 'x_enchanting_scroll.b3d', mesh = 'x_enchanting_scroll.b3d',
@ -477,12 +511,14 @@ minetest.register_entity('x_enchanting:table_scroll', {
}, },
---@param self table ---@param self table
---@param killer ObjectRef ---@param killer ObjectRef
---@diagnostic disable-next-line: unused-local
on_death = function(self, killer) on_death = function(self, killer)
self.object:remove() self.object:remove()
end, end,
---@param self table ---@param self table
---@param staticdata StringAbstract ---@param staticdata StringAbstract
---@param dtime_s number ---@param dtime_s number
---@diagnostic disable-next-line: unused-local
on_activate = function(self, staticdata, dtime_s) on_activate = function(self, staticdata, dtime_s)
self._scroll_closed = true self._scroll_closed = true
self._tablechecktimer = 5 self._tablechecktimer = 5
@ -496,6 +532,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
---@param self table ---@param self table
---@param dtime number ---@param dtime number
---@param moveresult? table ---@param moveresult? table
---@diagnostic disable-next-line: unused-local
on_step = function(self, dtime, moveresult) on_step = function(self, dtime, moveresult)
local pos = self.object:get_pos() local pos = self.object:get_pos()
@ -506,7 +543,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- table -- table
if self._tablechecktimer <= 0 then if self._tablechecktimer <= 0 then
self._tablechecktimer = 5 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 if node.name ~= 'x_enchanting:table' then
-- remove entity when no table under it -- remove entity when no table under it
@ -517,13 +554,14 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- player -- player
if self._playerchecktimer <= 0 then if self._playerchecktimer <= 0 then
self._playerchecktimer = 1 self._playerchecktimer = 1
local objects = minetest.get_objects_inside_radius(pos, 5) local objects = core.get_objects_inside_radius(pos, 5)
-- inital value -- inital value
local shortest_distance = 10 local shortest_distance = 10
local found_player = false local found_player = false
if #objects > 0 then if #objects > 0 then
---@diagnostic disable-next-line: unused-local
for i, obj in ipairs(objects) do for i, obj in ipairs(objects) do
if obj:is_player() and obj:get_pos() then if obj:is_player() and obj:get_pos() then
-- player -- player
@ -531,6 +569,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
local distance = vector.distance(pos, obj:get_pos()) local distance = vector.distance(pos, obj:get_pos())
if distance < shortest_distance then if distance < shortest_distance then
shortest_distance = distance
self._player = obj self._player = obj
end end
end end
@ -548,7 +587,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
self._scroll_closed = false self._scroll_closed = false
self.object:set_animation(unpack(XEnchanting.scroll_animations.scroll_open)) 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, gain = 0.3,
pos = pos, pos = pos,
max_hear_distance = 10 max_hear_distance = 10
@ -557,7 +596,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
self._scroll_closed = true self._scroll_closed = true
self.object:set_animation(unpack(XEnchanting.scroll_animations.scroll_close)) 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, gain = 0.3,
pos = pos, pos = pos,
max_hear_distance = 10 max_hear_distance = 10
@ -568,7 +607,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- rotation -- rotation
if self._player and self._player:get_pos() then if self._player and self._player:get_pos() then
local direction = vector.direction(pos, self._player:get_pos()) 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 else
self.object:set_rotation({ self.object:set_rotation({
x = self._last_rotation.x, x = self._last_rotation.x,
@ -584,6 +623,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
---@param dir Vector ---@param dir Vector
---@param damage number ---@param damage number
---@return boolean | nil ---@return boolean | nil
---@diagnostic disable-next-line: unused-local
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
return true return true
end end
@ -593,17 +633,17 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- Recipe -- Recipe
--- ---
if minetest.get_modpath('xdecor') then if core.get_modpath('xdecor') or core.get_modpath('enchanting') then
minetest.register_craft({ core.register_craft({
output = 'x_enchanting:table', output = 'x_enchanting:table',
recipe = { recipe = {
{ 'default:book', '', '' }, { 'default:book', '', '' },
{ 'default:diamond', 'default:obsidian', 'default:diamond' }, { 'default:diamond', 'xdecor:enchantment_table', 'default:diamond' },
{ 'default:obsidian', 'default:obsidian', 'default:obsidian' } { 'default:obsidian', 'default:obsidian', 'default:obsidian' }
} }
}) })
else else
minetest.register_craft({ core.register_craft({
output = 'x_enchanting:table', output = 'x_enchanting:table',
recipe = { recipe = {
{ '', 'default:book', '' }, { '', '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: 395 B

View File

@ -16,6 +16,7 @@
---@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 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_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_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 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 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. ---@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.

View File

@ -45,6 +45,7 @@
---@field get_rotation fun(self: ObjectRef): Vector returns the rotation, a vector (radians) ---@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 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 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 ---`ObjectRef` armor groups
---@class ObjectRefArmorGroups ---@class ObjectRefArmorGroups

View File

@ -50,5 +50,5 @@
---@class EnchantmentDataSlot ---@class EnchantmentDataSlot
---@field level number ---@field level number
---@field final_enchantments Enchantment[] ---@field final_enchantments Enchantment[]
---@field tool_cap_data ToolCapabilitiesDef ---@field tool_cap_data ToolCapabilitiesDef | nil
---@field descriptions {["enchantments_desc"]: string, ["enchantments_desc_masked"]: string } ---@field descriptions {["enchantments_desc"]: string, ["enchantments_desc_masked"]: string }