Compare commits

..

15 Commits

Author SHA1 Message Date
673922371c Add enchanting mod support 2025-04-19 17:13:34 +02:00
Juraj Vajda
0d589e69b3 Update luacheck 2025-04-07 00:38:49 -04:00
Juraj Vajda
8935043ed4 Update license year 2025-04-07 00:25:32 -04:00
Juraj Vajda
e2cf02d026 Update CI script 2025-04-07 00:24:55 -04:00
Juraj Vajda
0365b8043b Cleanup screenshot files 2025-04-07 00:21:38 -04:00
Juraj Vajda
ba1d30909b Rename minetest API convention to core 2025-04-07 00:19:12 -04:00
Juraj Vajda
82973b6462 Add API method for getting glint texture 2025-04-07 00:18:53 -04:00
Juraj Vajda
ecdf5a83d1 fix typo 2024-01-30 11:56:41 -05:00
Juraj Vajda
af26c6ed76 Add grindstone 2023-12-14 16:48:02 -05:00
Juraj Vajda
3ee6bb7b80 Update readme and cdb 2023-12-14 16:34:33 -05:00
Juraj Vajda
84df66325e Add grindstone 2023-12-14 16:28:48 -05:00
Juraj Vajda
737e2f3cba Add grindstone 2023-12-14 16:27:25 -05:00
Juraj Vajda
693ace14c2 Add tool texture overlay when enchanted 2023-12-07 10:47:30 -05:00
Juraj Vajda
f4ed507897 Add missing use_texture_alpha 2023-04-01 14:40:34 -04:00
Juraj Vajda
01703cb0a3 3 Add support for item_drop mod 2023-02-26 15:45:25 +00:00
37 changed files with 1626 additions and 198 deletions

View File

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

View File

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

View File

@ -453,3 +453,104 @@ RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
### 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
**CC-BY-SA-4.0, 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
**CC-BY-SA-4.0, 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
**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

@ -17,8 +17,8 @@ Adds Enchanting Mechanics and API.
* enchanted items have detailed enchantments description/short description
* mesh node model
* mesh entity model and animations
NOTE: item inventory/wield image will not be adjusted until this feature is supported by MT engine, see: https://github.com/minetest/minetest/issues/5686
* tool texture will have enchanted glint
* adds grind stone to remove enchantments (excluding curses)
## How To
@ -185,80 +185,3 @@ end
### Code
GNU Lesser General Public License v2.1 or later (see included LICENSE file)
### Textures
**CC-BY-SA-4.0, Pixel Perfection by XSSheep**, https://minecraft.curseforge.com/projects/pixel-perfection-freshly-updated
- x_enchanting_symbol_1.png
- x_enchanting_symbol_2.png
- x_enchanting_symbol_3.png
- x_enchanting_symbol_4.png
- x_enchanting_symbol_5.png
- x_enchanting_symbol_6.png
- x_enchanting_symbol_7.png
- x_enchanting_symbol_8.png
- x_enchanting_symbol_9.png
- x_enchanting_symbol_10.png
- x_enchanting_symbol_11.png
- x_enchanting_symbol_12.png
- x_enchanting_symbol_13.png
- x_enchanting_symbol_14.png
- x_enchanting_symbol_15.png
- x_enchanting_symbol_16.png
- x_enchanting_symbol_17.png
- x_enchanting_symbol_18.png
- x_enchanting_symbol_19.png
- x_enchanting_symbol_20.png
- x_enchanting_symbol_21.png
- x_enchanting_symbol_22.png
- x_enchanting_symbol_23.png
- x_enchanting_symbol_24.png
- x_enchanting_symbol_25.png
- x_enchanting_symbol_26.png
**CC-BY-SA-4.0, 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
**CC-BY-SA-4.0, 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

150
api.lua
View File

@ -1,6 +1,6 @@
--[[
X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
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
@ -16,7 +16,7 @@
License along with this library; if not, write to juraj.vajda@gmail.com
--]]
local S = minetest.get_translator(minetest.get_current_modname())
local S = core.get_translator(core.get_current_modname())
---@type XEnchanting
XEnchanting = {
@ -195,7 +195,8 @@ XEnchanting = {
weight = 1,
secondary = true,
-- all applicable
groups = nil
groups = nil,
cursed = true
},
knockback = {
name = S('Knockback'),
@ -319,15 +320,15 @@ end
---@diagnostic disable-next-line: unused-local
function XEnchanting.has_tool_group(self, name)
if minetest.get_item_group(name, 'pickaxe') > 0 then
if core.get_item_group(name, 'pickaxe') > 0 then
return 'pickaxe'
elseif minetest.get_item_group(name, 'shovel') > 0 then
elseif core.get_item_group(name, 'shovel') > 0 then
return 'shovel'
elseif minetest.get_item_group(name, 'axe') > 0 then
elseif core.get_item_group(name, 'axe') > 0 then
return 'axe'
elseif minetest.get_item_group(name, 'sword') > 0 then
elseif core.get_item_group(name, 'sword') > 0 then
return 'sword'
elseif minetest.get_item_group(name, 'bow') > 0 then
elseif core.get_item_group(name, 'bow') > 0 then
return 'bow'
end
@ -335,7 +336,7 @@ function XEnchanting.has_tool_group(self, name)
end
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
return
end
@ -346,7 +347,7 @@ function XEnchanting.set_tool_enchantability(self, tool_def)
_enchantability = self.tools_enchantability[tool_def.name]
end
minetest.override_item(tool_def.name, {
core.override_item(tool_def.name, {
groups = mergeTables(tool_def.groups, { enchantability = _enchantability })
})
end
@ -505,7 +506,7 @@ function XEnchanting.get_enchanted_descriptions(self, enchantments)
end
end
enchantments_desc = '\n' .. minetest.colorize('#AE81FF', S('Enchanted'))
enchantments_desc = '\n' .. core.colorize('#AE81FF', S('Enchanted'))
.. '\n' .. table.concat(enchantments_desc, '\n')
enchantments_desc_masked = table.concat(enchantments_desc_masked, '') .. '..?'
@ -515,6 +516,10 @@ function XEnchanting.get_enchanted_descriptions(self, enchantments)
}
end
function XEnchanting.get_glint_texture_modifier(self, image)
return image .. '^((' .. image .. '^[contrast:127:127)^[mask:x_enchanting_glint.png^[opacity:80)'
end
function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name)
local data = self.form_context[player_name].data
@ -525,9 +530,9 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
local capabilities = data.slots[level].tool_cap_data
local description = data.slots[level].descriptions.enchantments_desc
local final_enchantments = data.slots[level].final_enchantments
local inv = minetest.get_meta(pos):get_inventory()
local tool_def = minetest.registered_tools[itemstack:get_name()]
local node_meta = minetest.get_meta(pos)
local inv = core.get_meta(pos):get_inventory()
local tool_def = core.registered_tools[itemstack:get_name()]
local node_meta = core.get_meta(pos)
if not tool_def then
return
@ -550,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('short_description', S('Enchanted') .. ' ' .. itemstack:get_short_description())
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)
@ -564,7 +585,7 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
local formspec = self:get_formspec(pos, player_name)
node_meta:set_string('formspec', formspec)
minetest.sound_play('x_enchanting_enchant', {
core.sound_play('x_enchanting_enchant', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
@ -588,7 +609,7 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
glow = 1
}
if minetest.has_feature({ dynamic_add_media_table = true, particlespawner_tweenable = true }) then
if core.has_feature({ dynamic_add_media_table = true, particlespawner_tweenable = true }) then
-- new syntax, after v5.6.0
particlespawner_def = {
amount = 50,
@ -620,7 +641,7 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
}
end
minetest.add_particlespawner(particlespawner_def)
core.add_particlespawner(particlespawner_def)
end
function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_def)
@ -648,7 +669,7 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
else
---@diagnostic disable-next-line: unused-local
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
break
end
@ -673,7 +694,7 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
local chosen_enchantment_level = slot_lvl
-- Applying modifiers to the enchantment level
local enchantability = minetest.get_item_group(tool_def.name, 'enchantability')
local enchantability = core.get_item_group(tool_def.name, 'enchantability')
-- Generate a random number between 1 and 1+(enchantability/2), with a triangular distribution
local rand_enchantability = 1 + math.random(enchantability / 4 + 1) + math.random(enchantability / 4 + 1)
-- Choose the enchantment level
@ -858,12 +879,13 @@ local function get_list_bg(x, y)
return out
end
local function get_formspec_bg(player_name)
local info = minetest.get_player_information(player_name)
local bg = 'background[5,5;1,1;x_enchanting_gui_formbg.png;true]'
local function get_formspec_bg(player_name, bg_img)
local bg_image = bg_img and bg_img or 'x_enchanting_gui_formbg.png'
local info = core.get_player_information(player_name)
local bg = 'background[5,5;1,1;' .. bg_image .. ';true]'
if info.formspec_version > 1 then
bg = 'background9[5,5;1,1;x_enchanting_gui_formbg.png;true;10]'
bg = 'background9[5,5;1,1;' .. bg_image .. ';true;10]'
end
return bg
@ -871,7 +893,7 @@ end
function XEnchanting.get_formspec(self, pos, player_name, data)
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
local inv = minetest.get_meta(pos):get_inventory()
local inv = core.get_meta(pos):get_inventory()
---@diagnostic disable-next-line: codestyle-check
local model_scroll_open = 'model[0,0;2,3;x_enchanting_table;x_enchanting_scroll.b3d;x_enchanting_scroll_mesh.png,x_enchanting_scroll_handles_mesh.png,x_enchanting_scroll_mesh.png;89,0;false;false;' .. self.scroll_animations.scroll_open_idle[1].x .. ',' .. self.scroll_animations.scroll_open_idle[1].y .. ';0]'
---@diagnostic disable-next-line: codestyle-check
@ -912,10 +934,10 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
if inv:get_stack('trade', 1):get_count() >= i then
---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[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
---@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
formspec[#formspec + 1] = 'image[2.3,' .. -0.5 + i .. ';1,1;x_enchanting_image_trade_' .. i .. '.png;]'
@ -950,3 +972,77 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
return table.concat(formspec, '')
end
function XEnchanting.get_grindstone_formspec(self, pos, player_name, data)
local _data = data or {}
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
local model = 'model[0,0;2,3;x_enchanting_grindstone;x_enchanting_grindstone.obj;x_enchanting_grindstone_mesh.png;0,0;true;false;]'
local formspec = {}
formspec[#formspec + 1] = 'size[8,9]'
formspec[#formspec + 1] = 'bgcolor[#080808BB;true]'
formspec[#formspec + 1] = 'listcolors[#FFFFFF00;#FFFFFF1A;#FFFFFF00;#30434C;#FFF]'
formspec[#formspec + 1] = get_formspec_bg(player_name)
formspec[#formspec + 1] = 'style_type[label;font=mono,bold]'
formspec[#formspec + 1] = 'style[slot_1,slot_2,slot_3;font=mono,bold;textcolor=#4D413A]'
formspec[#formspec + 1] = 'label[0, 0;' .. S('Disenchant') .. ']'
-- item
formspec[#formspec + 1] = 'list[nodemeta:' .. spos .. ';item;0.5, 2.5;1, 1;]'
formspec[#formspec + 1] = 'image[0.5, 2.5;1,1;x_enchanting_gui_cloth_bg.png]'
-- result
if _data.result_disabled then
formspec[#formspec + 1] = 'image[4.75, 1.25;1.5,1.5;x_enchanting_gui_cloth_bg_corssed.png]'
else
formspec[#formspec + 1] = 'list[nodemeta:' .. spos .. ';result;5, 1.5;1, 1;]'
formspec[#formspec + 1] = 'image[5, 1.5;1,1;x_enchanting_gui_cloth_bg.png]'
end
-- inventories
formspec[#formspec + 1] = 'list[current_player;main;0, 4.85;8, 1;]'
formspec[#formspec + 1] = 'list[current_player;main;0, 6.08;8, 3;8]'
formspec[#formspec + 1] = 'listring[nodemeta:' .. spos .. ';result]'
formspec[#formspec + 1] = 'listring[current_player;main]'
formspec[#formspec + 1] = 'listring[nodemeta:' .. spos .. ';item]'
formspec[#formspec + 1] = 'listring[current_player;main]'
formspec[#formspec + 1] = get_hotbar_bg(0, 4.85)
formspec[#formspec + 1] = get_list_bg(0, 6.08)
formspec[#formspec + 1] = model
return table.concat(formspec, '')
end
function XEnchanting.has_all_cursed_ench(self, itemstack)
if itemstack:is_empty() then
return false
end
local item_stack_meta = itemstack:get_meta()
local stack_enchantment_data = core.deserialize(item_stack_meta:get_string('x_enchanting')) or {}
local stack_enchantment_data_length = get_table_length(stack_enchantment_data)
local cursed_ench = 0
-- Get cursed enchantments
for id, value in pairs(stack_enchantment_data) do
local ench_def = self.enchantment_defs[id]
if ench_def.cursed then
cursed_ench = cursed_ench + 1
end
end
return cursed_ench == stack_enchantment_data_length
end
--
-- Sounds
--
function XEnchanting.node_sound_wood_defaults(table)
table = table or {}
table.footstep = table.footstep or { name = 'x_enchanting_wood_footstep', gain = 0.15 }
table.dig = table.dig or { name = 'x_enchanting_wood_hit', gain = 0.5 }
table.dug = table.dug or { name = 'x_enchanting_wood_place', gain = 0.1 }
table.place = table.place or { name = 'x_enchanting_wood_place', gain = 0.15 }
return table
end

BIN
assets/promo_scene.blend Normal file

Binary file not shown.

Binary file not shown.

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' }
}
})

View File

@ -1,6 +1,6 @@
--[[
X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
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
@ -16,11 +16,12 @@
License along with this library; if not, write to juraj.vajda@gmail.com
--]]
local path = minetest.get_modpath('x_enchanting')
local mod_start_time = minetest.get_us_time()
local path = core.get_modpath('x_enchanting')
local mod_start_time = core.get_us_time()
dofile(path .. '/api.lua')
dofile(path .. '/table.lua')
dofile(path .. '/grindstone.lua')
---Check if string X starts with string Y
---@param str string
@ -30,23 +31,23 @@ local function starts_with(str, start)
return str:sub(1, #start) == start
end
minetest.register_on_mods_loaded(function()
core.register_on_mods_loaded(function()
-- 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
XEnchanting:set_tool_enchantability(tool_def)
end
end
-- 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
XEnchanting.registered_ores[def.ore] = true
end
end
-- 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:')
or starts_with(name, 'mobs_monster:')
then
@ -102,7 +103,7 @@ minetest.register_on_mods_loaded(function()
stack:set_count(stack:get_count() * chance)
if stack:get_count() > 0 then
minetest.item_drop(stack, puncher, pos)
core.item_drop(stack, puncher, pos)
end
end
end
@ -160,7 +161,7 @@ minetest.register_on_mods_loaded(function()
stack:set_count(stack:get_count() * chance)
if stack:get_count() > 0 then
minetest.item_drop(stack, puncher, pos)
core.item_drop(stack, puncher, pos)
end
end
end
@ -172,7 +173,7 @@ minetest.register_on_mods_loaded(function()
end)
---@diagnostic disable-next-line: unused-local
minetest.register_on_joinplayer(function(player, last_login)
core.register_on_joinplayer(function(player, last_login)
XEnchanting.form_context[player:get_player_name()] = nil
if not XEnchanting.player_seeds[player:get_player_name()] then
@ -181,20 +182,20 @@ minetest.register_on_joinplayer(function(player, last_login)
end)
---@diagnostic disable-next-line: unused-local
minetest.register_on_leaveplayer(function(player, timed_out)
core.register_on_leaveplayer(function(player, timed_out)
XEnchanting.form_context[player:get_player_name()] = nil
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
or not digger:is_player()
then
return old_handle_node_drops(pos, drops, digger)
end
local node = minetest.get_node(pos)
local node = core.get_node(pos)
local wield_stack = digger:get_wielded_item()
local wield_stack_meta = wield_stack:get_meta()
@ -206,13 +207,13 @@ function minetest.handle_node_drops(pos, drops, digger)
for _, itemstring in ipairs(drops) do
if XEnchanting.registered_ores[node.name]
or minetest.get_item_group(node.name, 'stone') > 0
or minetest.get_item_group(node.name, 'soil') > 0
or minetest.get_item_group(node.name, 'sand') > 0
or minetest.get_item_group(node.name, 'snowy') > 0
or minetest.get_item_group(node.name, 'slippery') > 0
or minetest.get_item_group(node.name, 'tree') > 0
or minetest.get_item_group(node.name, 'leaves') > 0
or core.get_item_group(node.name, 'stone') > 0
or core.get_item_group(node.name, 'soil') > 0
or core.get_item_group(node.name, 'sand') > 0
or core.get_item_group(node.name, 'snowy') > 0
or core.get_item_group(node.name, 'slippery') > 0
or core.get_item_group(node.name, 'tree') > 0
or core.get_item_group(node.name, 'leaves') > 0
then
local tool_capabilities = wield_stack:get_tool_capabilities()
local stack = ItemStack(itemstring)
@ -237,7 +238,7 @@ function minetest.handle_node_drops(pos, drops, digger)
local silk_touch = wield_stack_meta:get_float('is_silk_touch')
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
-- drop raw item/node
return old_handle_node_drops(pos, { ItemStack(node.name) }, digger)
@ -247,7 +248,7 @@ function minetest.handle_node_drops(pos, drops, digger)
end
---@diagnostic disable-next-line: unused-local
minetest.register_on_player_hpchange(function(player, hp_change, reason)
core.register_on_player_hpchange(function(player, hp_change, reason)
-- Curse of Vanishing
if (player:get_hp() + hp_change) <= 0 then
-- Going to die
@ -272,9 +273,9 @@ minetest.register_on_player_hpchange(function(player, hp_change, reason)
end, true)
-- 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)
if hitter and hitter:is_player() then
local hitter_wield_stack = hitter:get_wielded_item()
@ -306,6 +307,6 @@ function minetest.calculate_knockback(player, hitter, time_from_last_punch,
tool_capabilities, dir, distance, damage)
end
local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000
local mod_end_time = (core.get_us_time() - mod_start_time) / 1000000
print('[Mod] x_enchanting loaded.. [' .. mod_end_time .. 's]')

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -1,6 +1,6 @@
/**
* Deploy code to CDB
* Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
* 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
@ -29,7 +29,7 @@ try {
ref: 'master'
}
const response = await fetch('https://content.minetest.net/api/packages/SaKeL/x_enchanting/releases/new/', {
const response = await fetch('https://content.luanti.org/api/packages/SaKeL/x_enchanting/releases/new/', {
method: 'POST',
body: JSON.stringify(body),
headers: {

View File

@ -1,6 +1,6 @@
/**
* Run LUA diagnostics in continuous integration
* Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
* 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

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.

View File

@ -1,6 +1,7 @@
---@diagnostic disable
--[[
X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
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
@ -16,21 +17,19 @@
License along with this library; if not, write to juraj.vajda@gmail.com
--]]
---@diagnostic disable-next-line
screwdriver = minetest.global_exists('screwdriver') and screwdriver --[[@as MtgScrewdriver]]
local S = minetest.get_translator(minetest.get_current_modname())
local S = core.get_translator(core.get_current_modname())
----
--- Table Node
----
minetest.register_node('x_enchanting:table', {
core.register_node('x_enchanting:table', {
description = S('Enchanting Table'),
short_description = S('Enchanting Table'),
drawtype = 'mesh',
mesh = 'x_enchanting_table.obj',
tiles = { 'x_enchanting_table.png' },
use_texture_alpha = 'clip',
paramtype = 'light',
paramtype2 = 'facedir',
walkable = true,
@ -64,15 +63,15 @@ minetest.register_node('x_enchanting:table', {
light_source = 6,
---@param pos Vector
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local meta = core.get_meta(pos)
local inv = meta:get_inventory()
meta:set_string('infotext', S('Enchanting Table'))
meta:set_string('owner', '')
inv:set_size('item', 1)
inv:set_size('trade', 1)
minetest.add_entity({ x = pos.x, y = pos.y + 0.7, z = pos.z }, 'x_enchanting:table_scroll')
minetest.get_node_timer(pos):start(5)
core.add_entity({ x = pos.x, y = pos.y + 0.7, z = pos.z }, 'x_enchanting:table_scroll')
core.get_node_timer(pos):start(5)
end,
---@param pos Vector
---@param placer ObjectRef | nil
@ -80,7 +79,7 @@ minetest.register_node('x_enchanting:table', {
---@param pointed_thing PointedThingDef
---@diagnostic disable-next-line: unused-local
after_place_node = function(pos, placer, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local meta = core.get_meta(pos)
if not placer then
return
@ -101,34 +100,34 @@ minetest.register_node('x_enchanting:table', {
---@param pointed_thing? PointedThingDef
---@diagnostic disable-next-line: unused-local
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local meta = core.get_meta(pos)
local p_name = clicker:get_player_name()
if minetest.is_protected(pos, p_name) then
if core.is_protected(pos, p_name) then
return itemstack
end
minetest.sound_play('x_enchanting_scroll', {
core.sound_play('x_enchanting_scroll', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
}, true)
-- bookshelfs
local bookshelfs = minetest.find_nodes_in_area(
local bookshelfs = core.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ 'default:bookshelf', 'group:bookshelf' }
)
local inv = minetest.get_meta(pos):get_inventory()
local inv = core.get_meta(pos):get_inventory()
if not inv:is_empty('item') and inv:get_stack('item', 1):get_meta():get_int('is_enchanted') ~= 1 then
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data(
clicker,
#bookshelfs,
minetest.registered_tools[item_stack:get_name()]
core.registered_tools[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
@ -146,12 +145,12 @@ minetest.register_node('x_enchanting:table', {
---@return table | nil
---@diagnostic disable-next-line: unused-local
on_blast = function(pos, intensity)
if minetest.is_protected(pos, '') then
if core.is_protected(pos, '') then
return
end
local drops = {}
local inv = minetest.get_meta(pos):get_inventory()
local inv = core.get_meta(pos):get_inventory()
local stack_item = inv:get_stack('item', 1)
local stack_trade = inv:get_stack('trade', 1)
@ -164,7 +163,7 @@ minetest.register_node('x_enchanting:table', {
end
drops[#drops + 1] = 'x_enchanting:table'
minetest.remove_node(pos)
core.remove_node(pos)
return drops
end,
@ -175,11 +174,11 @@ minetest.register_node('x_enchanting:table', {
return false
end
local inv = minetest.get_meta(pos):get_inventory()
local inv = core.get_meta(pos):get_inventory()
return inv:is_empty('item')
and inv:is_empty('trade')
and not minetest.is_protected(pos, player:get_player_name())
and not core.is_protected(pos, player:get_player_name())
end,
---@diagnostic disable-next-line: unused-local
on_rotate = function(pos, node, user, mode, new_param2)
@ -190,10 +189,10 @@ minetest.register_node('x_enchanting:table', {
---@diagnostic disable-next-line: unused-local
on_timer = function(pos, elapsed)
-- entity
local table_scroll = minetest.get_objects_inside_radius(pos, 0.9)
local table_scroll = core.get_objects_inside_radius(pos, 0.9)
if #table_scroll == 0 then
minetest.add_entity({ x = pos.x, y = pos.y + 0.7, z = pos.z }, 'x_enchanting:table_scroll')
core.add_entity({ x = pos.x, y = pos.y + 0.7, z = pos.z }, 'x_enchanting:table_scroll')
end
local particlespawner_def = {
@ -213,7 +212,7 @@ minetest.register_node('x_enchanting:table', {
glow = 1
}
if minetest.has_feature({ dynamic_add_media_table = true, particlespawner_tweenable = true }) then
if core.has_feature({ dynamic_add_media_table = true, particlespawner_tweenable = true }) then
-- new syntax, after v5.6.0
particlespawner_def = {
amount = 50,
@ -244,10 +243,10 @@ minetest.register_node('x_enchanting:table', {
}
end
minetest.add_particlespawner(particlespawner_def)
core.add_particlespawner(particlespawner_def)
---bookshelfs
local bookshelfs = minetest.find_nodes_in_area(
local bookshelfs = core.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ 'default:bookshelf', 'group:bookshelf' }
@ -268,8 +267,8 @@ minetest.register_node('x_enchanting:table', {
local rand2 = math.random(10, 500) / 100
local rand3 = math.random(50, 200) / 100
minetest.after(rand2, function()
minetest.add_particle({
core.after(rand2, function()
core.add_particle({
pos = pos_random,
velocity = { x = x, y = 2 - y, z = z },
acceleration = { x = 0, y = rand1, z = 0 },
@ -285,7 +284,7 @@ minetest.register_node('x_enchanting:table', {
end,
---@param pos Vector
on_destruct = function(pos)
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 0.9)) do
for _, obj in ipairs(core.get_objects_inside_radius(pos, 0.9)) do
if obj
and obj:get_luaentity()
and obj:get_luaentity().name == 'x_enchanting:table_scroll'
@ -307,14 +306,14 @@ minetest.register_node('x_enchanting:table', {
local is_enchanted = st_meta:get_int('is_enchanted')
if listname == 'item'
and minetest.get_item_group(st_name, 'enchantability') > 0
and core.get_item_group(st_name, 'enchantability') > 0
and is_enchanted ~= 1
then
return stack:get_count()
elseif listname == 'trade'
and (
st_name == 'default:mese_crystal'
or minetest.get_item_group(st_name, 'enchanting_trade') > 0
or core.get_item_group(st_name, 'enchanting_trade') > 0
)
and is_enchanted ~= 1
then
@ -337,7 +336,7 @@ minetest.register_node('x_enchanting:table', {
elseif listname == 'trade'
and (
st_name == 'default:mese_crystal'
or minetest.get_item_group(st_name, 'enchanting_trade') > 0
or core.get_item_group(st_name, 'enchanting_trade') > 0
)
then
return stack:get_count()
@ -363,7 +362,7 @@ minetest.register_node('x_enchanting:table', {
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
on_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
local meta = core.get_meta(pos)
local p_name = player:get_player_name()
local inv = meta:get_inventory()
local item_stack = inv:get_stack('item', 1)
@ -372,7 +371,7 @@ minetest.register_node('x_enchanting:table', {
if not inv:is_empty('item') and is_enchanted == 0 then
-- bookshelfs
local bookshelfs = minetest.find_nodes_in_area(
local bookshelfs = core.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ 'default:bookshelf', 'group:bookshelf' }
@ -381,7 +380,7 @@ minetest.register_node('x_enchanting:table', {
local data = XEnchanting:get_enchantment_data(
player,
#bookshelfs,
minetest.registered_tools[item_stack:get_name()]
core.registered_tools[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
@ -399,7 +398,7 @@ minetest.register_node('x_enchanting:table', {
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
on_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
local meta = core.get_meta(pos)
local p_name = player:get_player_name()
local inv = meta:get_inventory()
local item_stack = inv:get_stack('item', 1)
@ -408,7 +407,7 @@ minetest.register_node('x_enchanting:table', {
if not inv:is_empty('item') and is_enchanted == 0 then
-- bookshelfs
local bookshelfs = minetest.find_nodes_in_area(
local bookshelfs = core.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
{ 'default:bookshelf', 'group:bookshelf' }
@ -417,7 +416,7 @@ minetest.register_node('x_enchanting:table', {
local data = XEnchanting:get_enchantment_data(
player,
#bookshelfs,
minetest.registered_tools[item_stack:get_name()]
core.registered_tools[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
@ -456,7 +455,7 @@ minetest.register_node('x_enchanting:table', {
return
end
local inv = minetest.get_meta(pos):get_inventory()
local inv = core.get_meta(pos):get_inventory()
if inv:is_empty('trade') or inv:is_empty('item') then
return
@ -489,7 +488,7 @@ minetest.register_node('x_enchanting:table', {
--- Entity (Scroll)
----
minetest.register_entity('x_enchanting:table_scroll', {
core.register_entity('x_enchanting:table_scroll', {
initial_properties = {
visual = 'mesh',
mesh = 'x_enchanting_scroll.b3d',
@ -544,7 +543,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- table
if self._tablechecktimer <= 0 then
self._tablechecktimer = 5
local node = minetest.get_node({ x = pos.x, y = pos.y - 0.7, z = pos.z })
local node = core.get_node({ x = pos.x, y = pos.y - 0.7, z = pos.z })
if node.name ~= 'x_enchanting:table' then
-- remove entity when no table under it
@ -555,7 +554,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- player
if self._playerchecktimer <= 0 then
self._playerchecktimer = 1
local objects = minetest.get_objects_inside_radius(pos, 5)
local objects = core.get_objects_inside_radius(pos, 5)
-- inital value
local shortest_distance = 10
@ -588,7 +587,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
self._scroll_closed = false
self.object:set_animation(unpack(XEnchanting.scroll_animations.scroll_open))
minetest.sound_play('x_enchanting_scroll', {
core.sound_play('x_enchanting_scroll', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
@ -597,7 +596,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
self._scroll_closed = true
self.object:set_animation(unpack(XEnchanting.scroll_animations.scroll_close))
minetest.sound_play('x_enchanting_scroll', {
core.sound_play('x_enchanting_scroll', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
@ -608,7 +607,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- rotation
if self._player and self._player:get_pos() then
local direction = vector.direction(pos, self._player:get_pos())
self.object:set_yaw(minetest.dir_to_yaw(direction))
self.object:set_yaw(core.dir_to_yaw(direction))
else
self.object:set_rotation({
x = self._last_rotation.x,
@ -634,17 +633,17 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- Recipe
---
if minetest.get_modpath('xdecor') then
minetest.register_craft({
if core.get_modpath('xdecor') or core.get_modpath('enchanting') then
core.register_craft({
output = 'x_enchanting:table',
recipe = {
{ 'default:book', '', '' },
{ 'default:diamond', 'default:obsidian', 'default:diamond' },
{ 'default:diamond', 'xdecor:enchantment_table', 'default:diamond' },
{ 'default:obsidian', 'default:obsidian', 'default:obsidian' }
}
})
else
minetest.register_craft({
core.register_craft({
output = 'x_enchanting:table',
recipe = {
{ '', 'default:book', '' },

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B