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

22 Commits

Author SHA1 Message Date
eee56f7f0d add writing table 2023-12-12 08:42:25 -05:00
478de78b48 Merge branch 'master' into feature/add-writing-table 2023-12-07 17:41:57 -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
1af9cd2755 Add writing table - initial commit 2022-11-28 16:01:50 -05:00
f891170e45 Update readme and media licensing 2022-11-22 09:07:00 -05:00
1771a773e5 update readme 2022-11-16 16:36:33 -05:00
92cd1dea01 Fix distance measure for scroll 2022-11-16 16:26:46 -05:00
4303c5f8bf Dont show enchanted item enchantments when take/put in inventory 2022-11-16 16:09:48 -05:00
1e5a76ed18 Add looting support to animalia 2022-11-16 15:53:17 -05:00
96b8369500 Add particles on enchant, formspec gui adjustments for small screens 2022-11-16 14:15:01 -05:00
03ad38cfb3 Update formspec gui 2022-11-15 23:49:09 -05:00
44 changed files with 1204 additions and 170 deletions

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

@ -1,8 +1,7 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@ -55,7 +54,7 @@ modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
@ -111,7 +110,7 @@ modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@ -158,7 +157,7 @@ Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
@ -216,7 +215,7 @@ instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
@ -267,7 +266,7 @@ Library will still fall under Section 6.)
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
@ -329,7 +328,7 @@ restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
@ -370,7 +369,7 @@ subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
@ -422,7 +421,7 @@ conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
@ -454,49 +453,3 @@ 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.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 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.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -6,9 +6,10 @@ Adds Enchanting Mechanics and API.
## Features
* does not add new tools/items, it's using MT API to change existing items instead
* adds enchanting table
* supports all registered tools with known tool groups: pickaxe, shovel, axe, sword, e.g. `groups = {pickaxe = 1}`
* support all bows with group `{bow = 1}`
* supports all bows with group `{bow = 1}`
* supports `default:bookshelf` or anything with `group:bookshelf`
* enchanting trade is for `default:mese_crystal` or anything with `groups = {enchanting_trade = 1}`
* adds enchantability for all MT default tools, for custom tools the enchantability can be set in the item group, e.g. `groups = {enchantability = 15}`
@ -17,8 +18,6 @@ Adds Enchanting Mechanics and API.
* 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
## How To
### Enchanting Setup
@ -97,7 +96,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.
Supported: mobs_monster, mobs_animal
Supported: mobs_monster, mobs_animal, animalia
groups: sword
@ -177,6 +176,7 @@ end
## Optional Dependencies
- xdecor (adjusts conflicting recipe)
- item_drop (make sure fortune will execute before item_drop mod overrides `handle_node_drops`)
## License:
@ -186,7 +186,7 @@ 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
**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
@ -215,7 +215,7 @@ GNU Lesser General Public License v2.1 or later (see included LICENSE file)
- x_enchanting_symbol_25.png
- x_enchanting_symbol_26.png
**LGPL-2.1-or-later, by SaKeL**
**CC-BY-SA-4.0, by SaKeL**
- x_enchanting_image_button_disabled.png
- x_enchanting_image_button.png
@ -226,11 +226,15 @@ GNU Lesser General Public License v2.1 or later (see included LICENSE file)
- x_enchanting_scroll_mesh.png -- Derived from a texture TumeniNodes (CC BY-SA 3.0)
- x_enchanting_scroll_particle.png
- x_enchanting_table.png
- x_enchanting_trade_slot.png
- x_enchanting_gui_slot_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC-BY-SA-4.0)
- x_enchanting_gui_cloth_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC-BY-SA-4.0)
- x_enchanting_gui_cloth_trade_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC-BY-SA-4.0)
- x_enchanting_gui_formbg.png -- Derived from texture Pixel Perfection by XSSheep (CC-BY-SA-4.0)
- x_enchanting_gui_hb_bg.png -- Derived from texture Pixel Perfection by XSSheep (CC-BY-SA-4.0)
### Models
**LGPL-2.1-or-later, by SaKeL**
**CC-BY-SA-4.0, by SaKeL**
- x_enchanting_scroll.b3d
- x_enchanting_table.obj

670
api.lua
View File

@ -1,4 +1,20 @@
default = minetest.global_exists('default') and default --[[@as MtgDefault]]
--[[
X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
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 = minetest.get_translator(minetest.get_current_modname())
@ -64,8 +80,12 @@ XEnchanting = {
},
weight = 10,
groups = {
'sword'
}
'sword',
'book',
'scroll'
},
multiplier_from_scroll = 1,
multiplier_from_item = 1
},
looting = {
name = S('Looting'),
@ -83,8 +103,12 @@ XEnchanting = {
},
weight = 2,
groups = {
'sword'
}
'sword',
'book',
'scroll'
},
multiplier_from_scroll = 2,
multiplier_from_item = 4
},
fortune = {
name = S('Fortune'),
@ -104,9 +128,13 @@ XEnchanting = {
groups = {
'pickaxe',
'shovel',
'axe'
'axe',
'book',
'scroll'
},
incompatible = { 'silk_touch' }
incompatible = { 'silk_touch' },
multiplier_from_scroll = 2,
multiplier_from_item = 4
},
unbreaking = {
name = S('Unbreaking'),
@ -124,7 +152,9 @@ XEnchanting = {
},
weight = 5,
-- all applicable
groups = nil
groups = nil,
multiplier_from_scroll = 1,
multiplier_from_item = 2
},
efficiency = {
name = S('Efficiency'),
@ -134,7 +164,7 @@ XEnchanting = {
[2] = { 11, 61 },
[3] = { 21, 71 },
[4] = { 31, 81 },
[5] = { 41, 91 },
-- [5] = { 41, 91 },
},
-- level definition, `level = percentage increase`
level_def = {
@ -142,14 +172,18 @@ XEnchanting = {
[2] = 30,
[3] = 35,
[4] = 40,
[5] = 45,
-- [5] = 45,
},
weight = 10,
groups = {
'pickaxe',
'shovel',
'axe'
}
'axe',
'book',
'scroll'
},
multiplier_from_scroll = 1,
multiplier_from_item = 1
},
silk_touch = {
name = S('Silk Touch'),
@ -164,9 +198,13 @@ XEnchanting = {
groups = {
'pickaxe',
'shovel',
'axe'
'axe',
'book',
'scroll'
},
incompatible = { 'fortune' }
incompatible = { 'fortune' },
multiplier_from_scroll = 4,
multiplier_from_item = 8
},
curse_of_vanishing = {
name = S('Curse of Vanishing'),
@ -179,7 +217,9 @@ XEnchanting = {
weight = 1,
secondary = true,
-- all applicable
groups = nil
groups = nil,
multiplier_from_scroll = 4,
multiplier_from_item = 8
},
knockback = {
name = S('Knockback'),
@ -194,8 +234,12 @@ XEnchanting = {
},
weight = 5,
groups = {
'sword'
}
'sword',
'book',
'scroll'
},
multiplier_from_scroll = 1,
multiplier_from_item = 2
},
power = {
-- Increases arrow damage.
@ -218,8 +262,12 @@ XEnchanting = {
},
weight = 10,
groups = {
'bow'
}
'bow',
'book',
'scroll'
},
multiplier_from_scroll = 1,
multiplier_from_item = 1
},
punch = {
-- Increases arrow knockback.
@ -236,8 +284,12 @@ XEnchanting = {
},
weight = 2,
groups = {
'bow'
}
'bow',
'book',
'scroll'
},
multiplier_from_scroll = 2,
multiplier_from_item = 4
},
infinity = {
-- Prevents regular arrows from being consumed when shot.
@ -254,8 +306,12 @@ XEnchanting = {
weight = 1,
secondary = true,
groups = {
'bow'
}
'bow',
'book',
'scroll'
},
multiplier_from_scroll = 4,
multiplier_from_item = 8
},
},
form_context = {},
@ -290,6 +346,18 @@ local function get_table_length(table)
return length
end
---Find element v of t satisfying f(v)
local function tableFind(t, f)
for _, v in ipairs(t) do
if f(v) then
return v
end
end
return nil
end
---@diagnostic disable-next-line: unused-local
function XEnchanting.has_tool_group(self, name)
if minetest.get_item_group(name, 'pickaxe') > 0 then
return 'pickaxe'
@ -301,12 +369,14 @@ function XEnchanting.has_tool_group(self, name)
return 'sword'
elseif minetest.get_item_group(name, 'bow') > 0 then
return 'bow'
elseif minetest.get_item_group(name, 'book') > 0 then
return 'book'
end
return false
end
function XEnchanting.set_tool_enchantability(self, tool_def)
function XEnchanting.set_enchantability(self, tool_def)
if minetest.get_item_group(tool_def.name, 'enchantability') > 0 then
-- enchantability is already set, we dont need to override the item
return
@ -323,10 +393,12 @@ function XEnchanting.set_tool_enchantability(self, tool_def)
})
end
---@diagnostic disable-next-line: unused-local
function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantments)
local tool_stack = ItemStack({ name = tool_def.name })
local tool_capabilities = tool_stack:get_tool_capabilities()
---@diagnostic disable-next-line: unused-local
for i, enchantment in ipairs(enchantments) do
-- Efficiency
if enchantment.id == 'efficiency' then
@ -441,6 +513,7 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
return tool_capabilities
end
---@diagnostic disable-next-line: unused-local
function XEnchanting.get_randomseed(self)
return tonumber(tostring(os.time()):reverse():sub(1, 9)) --[[@as integer]]
end
@ -449,6 +522,7 @@ function XEnchanting.get_enchanted_descriptions(self, enchantments)
local enchantments_desc = {}
local enchantments_desc_masked = {}
---@diagnostic disable-next-line: unused-local
for i, enchantment in ipairs(enchantments) do
local add_roman_numbers = true
@ -475,7 +549,7 @@ function XEnchanting.get_enchanted_descriptions(self, enchantments)
enchantments_desc = '\n' .. minetest.colorize('#AE81FF', S('Enchanted'))
.. '\n' .. table.concat(enchantments_desc, '\n')
enchantments_desc_masked = table.concat(enchantments_desc_masked, '') .. '...?'
enchantments_desc_masked = table.concat(enchantments_desc_masked, '') .. '..?'
return {
enchantments_desc = enchantments_desc,
@ -487,17 +561,28 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
local data = self.form_context[player_name].data
if not data then
minetest.log('warning', '[set_enchanted_tool] no data from form context found.')
return
end
local tool_def = minetest.registered_tools[itemstack:get_name()]
or minetest.registered_craftitems[itemstack:get_name()]
if minetest.get_item_group(tool_def.name, 'book') > 0 then
itemstack = ItemStack({ name = 'x_enchanting:scroll_item' })
end
local capabilities = data.slots[level].tool_cap_data
local description = data.slots[level].descriptions.enchantments_desc
local final_enchantments = data.slots[level].final_enchantments
local inv = minetest.get_meta(pos):get_inventory()
local tool_def = minetest.registered_tools[itemstack:get_name()]
local node_meta = minetest.get_meta(pos)
if not tool_def then
minetest.log(
'warning',
'[set_enchanted_tool] no tool definition found in registered_tools/registered_craftitems.'
)
return
end
@ -505,6 +590,7 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
---@type table<string, {["value"]: number}>
local final_enchantments_meta = {}
---@diagnostic disable-next-line: unused-local
for i, enchantment in ipairs(final_enchantments) do
stack_meta:set_float('is_' .. enchantment.id, enchantment.value)
-- store only necessary data, keeping the meta optimized
@ -513,14 +599,38 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
}
end
stack_meta:set_tool_capabilities(capabilities)
if minetest.get_item_group(tool_def.name, 'book') == 0 then
for i, enchantment in ipairs(final_enchantments) do
stack_meta:set_float('is_' .. enchantment.id, enchantment.value)
end
stack_meta:set_tool_capabilities(capabilities)
end
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', minetest.serialize(final_enchantments))
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)
-- set new trade ItemStack
local trade_stack = inv:get_stack('trade', 1)
trade_stack:take_item(level)
inv:set_stack('trade', 1, trade_stack)
@ -528,14 +638,110 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, level, player_name
-- set new seed
self.player_seeds[player_name] = self:get_randomseed()
-- update node formspec
local formspec = self:get_formspec(pos, player_name)
node_meta:set_string('formspec', formspec)
-- play sound
minetest.sound_play('x_enchanting_enchant', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
}, true)
-- particles
local particlespawner_def = {
amount = 50,
time = 0.5,
minpos = { x = pos.x - 1, y = pos.y + 1, z = pos.z - 1 },
maxpos = { x = pos.x + 1, y = pos.y + 1.5, z = pos.z + 1 },
minvel = { x = -0.1, y = -0.5, z = -0.1 },
maxvel = { x = 0.1, y = -1.5, z = 0.1 },
minacc = { x = -0.1, y = -0.5, z = -0.1 },
maxacc = { x = 0.1, y = -1.5, z = 0.1 },
minexptime = 0.5,
maxexptime = 1,
minsize = 0.5,
maxsize = 1,
texture = 'x_enchanting_scroll_particle.png^[colorize:#A179E9:256',
glow = 1
}
if minetest.has_feature({ dynamic_add_media_table = true, particlespawner_tweenable = true }) then
-- new syntax, after v5.6.0
particlespawner_def = {
amount = 50,
time = 0.5,
size = {
min = 0.5,
max = 1,
},
exptime = 2,
pos = {
min = vector.new({ x = pos.x - 1.5, y = pos.y + 1, z = pos.z - 1.5 }),
max = vector.new({ x = pos.x + 1.5, y = pos.y + 1.5, z = pos.z + 1.5 }),
},
attract = {
kind = 'point',
strength = 2,
origin = vector.new({ x = pos.x, y = pos.y + 0.65, z = pos.z }),
die_on_contact = true
},
texture = {
name = 'x_enchanting_scroll_particle.png^[colorize:#A179E9:256',
alpha_tween = {
0.5, 1,
style = 'fwd',
reps = 1
}
},
glow = 1
}
end
minetest.add_particlespawner(particlespawner_def)
end
function XEnchanting.get_enchantments_for_group(self, item_name, meta_enchantments)
local enchantment_defs = table.copy(self.enchantment_defs)
local group_enchantments = {}
local total_cost = 0
if meta_enchantments and #meta_enchantments > 0 then
enchantment_defs = {}
for i, def in pairs(meta_enchantments) do
if self.enchantment_defs[def.id] then
enchantment_defs[def.id] = self.enchantment_defs[def.id]
end
end
end
for enchantment_id, enchantment_def in pairs(enchantment_defs) do
local is_group_compatible = false
if not enchantment_def.groups then
group_enchantments[enchantment_id] = enchantment_def
is_group_compatible = true
else
for i, group in ipairs(enchantment_def.groups) do
if minetest.get_item_group(item_name, group) > 0 then
group_enchantments[enchantment_id] = enchantment_def
is_group_compatible = true
break
end
end
end
if not is_group_compatible then
total_cost = total_cost + 1
end
end
return {
group_enchantments = group_enchantments,
writing_table_cost = total_cost
}
end
function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_def)
@ -561,6 +767,7 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
if not enchantment_def.groups then
group_enchantments[enchantment_name] = enchantment_def
else
---@diagnostic disable-next-line: unused-local
for i, group in ipairs(enchantment_def.groups) do
if minetest.get_item_group(tool_def.name, group) > 0 then
group_enchantments[enchantment_name] = enchantment_def
@ -647,6 +854,7 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
local total_weight = 0
-- calculate total weight
---@diagnostic disable-next-line: unused-local
for j, enchantment in ipairs(possible_enchantments) do
total_weight = total_weight + self.enchantment_defs[enchantment.id].weight
end
@ -691,11 +899,17 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
table.remove(possible_enchantments, idx)
end
end
else
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)
for idx, value in pairs(possible_enchantments) do
@ -743,6 +957,39 @@ function XEnchanting.get_enchantment_data(self, player, nr_of_bookshelfs, tool_d
return data
end
local function get_hotbar_bg(x, y)
local out = ''
for i = 0, 7, 1 do
out = out .. 'image[' .. x + i .. ',' .. y .. ';1,1;x_enchanting_gui_hb_bg.png]'
end
return out
end
local function get_list_bg(x, y)
local out = ''
for row = 0, 2, 1 do
for i = 0, 7, 1 do
out = out .. 'image[' .. x + i .. ',' .. y + row .. ';1,1;x_enchanting_gui_slot_bg.png]'
end
end
return out
end
local function get_formspec_bg(player_name)
local info = minetest.get_player_information(player_name)
local bg = 'background[5,5;1,1;x_enchanting_gui_formbg.png;true]'
if info.formspec_version > 1 then
bg = 'background9[5,5;1,1;x_enchanting_gui_formbg.png;true;10]'
end
return bg
end
function XEnchanting.get_formspec(self, pos, player_name, data)
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
local inv = minetest.get_meta(pos):get_inventory()
@ -754,21 +1001,29 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
local formspec = {
'size[8,9]',
'bgcolor[#080808BB;true]',
'listcolors[#FFFFFF00;#FFFFFF1A;#FFFFFF00;#30434C;#FFF]',
get_formspec_bg(player_name),
'style_type[label;font=mono,bold]',
'style[slot_1,slot_2,slot_3;font=mono,bold;textcolor=#4D413A]',
'label[0, 0;' .. S('Enchant') .. ']',
-- item
'list[nodemeta:' .. spos .. ';item;0, 2.5;1, 1;]',
'image[1,2.5;1,1;x_enchanting_trade_slot.png;]',
'image[0, 2.5;1,1;x_enchanting_gui_cloth_bg.png]',
-- trade
'list[nodemeta:' .. spos .. ';trade;1, 2.5;1, 1;]',
'image[1, 2.5;1,1;x_enchanting_gui_cloth_trade_bg.png]',
-- inventories
'list[current_player;main;0, 4.85;8, 1;]',
'list[current_player;main;0, 6.08;8, 3;8]',
'listring[nodemeta:' .. spos .. ';trade]',
'listring[current_player;main]',
'listring[nodemeta:' .. spos .. ';item]',
'listring[current_player;main]'
'listring[current_player;main]',
}
if default then
formspec[#formspec + 1] = default.get_hotbar_bg(0, 4.85)
end
formspec[#formspec + 1] = get_hotbar_bg(0, 4.85)
formspec[#formspec + 1] = get_list_bg(0, 6.08)
-- data
if data then
@ -778,17 +1033,17 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
if inv:get_stack('trade', 1):get_count() >= i then
---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button.png;slot_' .. i .. ';' .. slot.descriptions.enchantments_desc_masked .. ' ' .. minetest.colorize('#FFFF00', S('level') .. ': ' .. slot.level) .. ']'
formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button.png;slot_' .. i .. ';' .. slot.descriptions.enchantments_desc_masked .. minetest.formspec_escape(' [' .. slot.level .. ']') .. ']'
else
---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';' .. slot.descriptions.enchantments_desc_masked .. ' ' .. minetest.colorize('#FFFF00', S('level') .. ': ' .. slot.level) .. ']'
formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';' .. slot.descriptions.enchantments_desc_masked .. minetest.formspec_escape(' [' .. slot.level .. ']') .. ']'
end
formspec[#formspec + 1] = 'image[2.5,' .. -0.5 + i .. ';1,1;x_enchanting_image_trade_' .. i .. '.png;]'
formspec[#formspec + 1] = 'image[2.3,' .. -0.5 + i .. ';1,1;x_enchanting_image_trade_' .. i .. '.png;]'
else
-- disabled buttons
---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';]'
formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';]'
end
end
@ -797,7 +1052,7 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
for i = 1, 3, 1 do
-- disabled buttons
---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';]'
formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';]'
end
model_scroll_is_open = false
@ -816,3 +1071,338 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
return table.concat(formspec, '')
end
function XEnchanting.get_writing_table_total_data(self, props)
local inv = props.inv
-- item
local stack_item = inv:get_stack('item', 1)
local stack_item_meta = stack_item:get_meta()
local table_uses_item = stack_item_meta:get_int('x_enchanting_writing_table_uses')
print('table_uses_item', table_uses_item)
local prior_use_penalty_item = 2 ^ table_uses_item - 1
local enchantments_item = minetest.deserialize(stack_item_meta:get_string('x_enchanting')) or {}
-- sacrifice
local stack_sacrifice = inv:get_stack('sacrifice', 1)
local stack_sacrifice_meta = stack_sacrifice:get_meta()
local table_uses_sacrifice = stack_sacrifice_meta:get_int('x_enchanting_writing_table_uses')
print('table_uses_sacrifice', table_uses_sacrifice)
local prior_use_penalty_sacrifice = 2 ^ table_uses_sacrifice - 1
local enchantments_sacrifice = minetest.deserialize(stack_sacrifice_meta:get_string('x_enchanting')) or {}
local new_x_enchanting_writing_table_uses = table_uses_item
if table_uses_sacrifice > new_x_enchanting_writing_table_uses then
new_x_enchanting_writing_table_uses = table_uses_sacrifice
end
print('enchantments_sacrifice', dump(enchantments_sacrifice))
local result_data = {
-- Prior Work penalties of both target and sacrifice.
total_cost = prior_use_penalty_item + prior_use_penalty_sacrifice,
show_total_cost = true,
show_crossed_arrow = false
}
if stack_sacrifice:is_empty() or stack_item:is_empty() then
result_data.show_total_cost = false
if not (stack_sacrifice:is_empty() and stack_item:is_empty()) then
result_data.show_crossed_arrow = true
inv:set_stack('result', 1, ItemStack(''))
end
return result_data
end
-- total cost
-- If the sacrifice has enchantments, the enchantment cost.
-- get enchantment defs for item group
-- Add one level cost for every incompatible enchantment on the target
local enchantment_defs_for_group = self:get_enchantments_for_group(stack_item:get_name(), enchantments_sacrifice)
local possible_enchantments_defs = enchantment_defs_for_group.group_enchantments
result_data.total_cost = result_data.total_cost + enchantment_defs_for_group.writing_table_cost
-- remove incompatible enchantments
-- Add one level cost for every incompatible enchantment on the target
for enchanment_id, enchanment_def in pairs(table.copy(possible_enchantments_defs)) do
if enchanment_def.incompatible then
for _, enchantment in ipairs(enchantments_item) do
if table.indexof(enchanment_def.incompatible, enchantment.id) ~= -1 then
possible_enchantments_defs[enchanment_id] = nil
result_data.total_cost = result_data.total_cost + 1
end
end
end
end
-- reduce/sync current sacrifice enchantmets
local enchantments_sacrifice_copy = table.copy(enchantments_sacrifice)
enchantments_sacrifice = {}
for i, enchantment in ipairs(enchantments_sacrifice_copy) do
if possible_enchantments_defs[enchantment.id] then
table.insert(enchantments_sacrifice, enchantment)
end
end
print('enchantments_sacrifice', dump(enchantments_sacrifice))
print('enchantments_item', dump(enchantments_item))
local final_enchantments = {}
-- If the enchantment is compatible with the existing enchantments on the target:
-- add the final level of the enchantment on the resulting item multiplied by the multiplier from the table
for i, enchantment_sacrifice in ipairs(enchantments_sacrifice) do
local enchantment_upgraded = false
local multiplier = possible_enchantments_defs[enchantment_sacrifice.id].multiplier_from_item
if minetest.get_item_group(stack_sacrifice:get_name(), 'scroll') > 0 then
multiplier = possible_enchantments_defs[enchantment_sacrifice.id].multiplier_from_scroll
end
print('multiplier', multiplier)
for _, enchantment_item in ipairs(enchantments_item) do
print(enchantment_item.id .. ' == ' .. enchantment_sacrifice.id)
if enchantment_item.id == enchantment_sacrifice.id then
-- upgrade
print('--- upgrade')
print('enchantment_item', dump(enchantment_item))
print('enchantment_sacrifice', dump(enchantment_sacrifice))
if enchantment_sacrifice.level == enchantment_item.level then
local new_level = enchantment_sacrifice.level + 1
local max_level = #possible_enchantments_defs[enchantment_sacrifice.id].final_level_range
if new_level > max_level then
-- maximum level reached, still add the cost though
new_level = max_level
end
table.insert(final_enchantments, {
id = enchantment_sacrifice.id,
level = new_level,
value = possible_enchantments_defs[enchantment_sacrifice.id].level_def[new_level],
secondary = enchantment_sacrifice.secondary,
incompatible = enchantment_sacrifice.incompatible
})
-- upgrade 1 level up if not already on max level
print(enchantment_item.id, 'upgrade 1 level up')
result_data.total_cost = result_data.total_cost + multiplier * new_level
elseif enchantment_sacrifice.level > enchantment_item.level then
-- upgrade to sacrifice level
print(enchantment_item.id, 'upgrade to' .. enchantment_sacrifice.level .. ' levels up')
table.insert(final_enchantments, {
id = enchantment_sacrifice.id,
level = enchantment_sacrifice.level,
value =
possible_enchantments_defs[enchantment_sacrifice.id].level_def[enchantment_sacrifice.level],
secondary = enchantment_sacrifice.secondary,
incompatible = enchantment_sacrifice.incompatible
})
result_data.total_cost = result_data.total_cost + multiplier * enchantment_sacrifice.level
else
-- dont change enchantment on item but add cost for it
table.insert(final_enchantments, enchantment_item)
result_data.total_cost = result_data.total_cost + multiplier * enchantment_item.level
end
enchantment_upgraded = true
end
end
if not enchantment_upgraded then
-- add
print('--- add')
print('enchantment_sacrifice', dump(enchantment_sacrifice))
table.insert(final_enchantments, enchantment_sacrifice)
result_data.total_cost = result_data.total_cost + multiplier * enchantment_sacrifice.level
end
end
print('final_enchantments 1', dump(final_enchantments))
if #final_enchantments == 0 then
result_data.show_total_cost = false
result_data.show_crossed_arrow = true
inv:set_stack('result', 1, ItemStack(''))
return result_data
end
-- add remaining enchantments from the item
for i, enchantment_item in ipairs(enchantments_item) do
local has_item = false
for j, enchantment_sacrifice in ipairs(enchantments_sacrifice) do
if enchantment_item.id == enchantment_sacrifice.id then
has_item = true
end
end
if not has_item then
table.insert(final_enchantments, enchantment_item)
end
end
print('final_enchantments 2', dump(final_enchantments))
if inv:is_empty('item') and not inv:is_empty('sacrifice')
or inv:is_empty('sacrifice') and not inv:is_empty('item')
or inv:is_empty('trade')
or #final_enchantments == 0
then
result_data.show_crossed_arrow = true
inv:set_stack('result', 1, ItemStack(''))
end
if inv:is_empty('item') or inv:is_empty('sacrifice')
or #final_enchantments == 0
then
result_data.show_total_cost = false
end
result_data.enchantments = final_enchantments
print('result_data', dump(result_data))
local tool_cap_data = self:get_enchanted_tool_capabilities(
minetest.registered_tools[stack_item:get_name()] or minetest.registered_craftitems[stack_item:get_name()],
final_enchantments
)
local descriptions = self:get_enchanted_descriptions(final_enchantments)
local stack_result = ItemStack(stack_item:to_table())
local stack_result_meta = stack_result:get_meta()
print('short_description', stack_result:get_short_description())
if not stack_item:is_empty() and minetest.get_item_group(stack_item:get_name(), 'scroll') == 0 then
-- for i, enchantment in ipairs(final_enchantments) do
-- stack_meta:set_float('is_' .. enchantment.id, enchantment.value)
-- end
print('tool_cap_data', dump(tool_cap_data))
print('stack_result', dump(stack_result:to_table()))
stack_result_meta:set_tool_capabilities(tool_cap_data)
end
print('new_x_enchanting_writing_table_uses 1', new_x_enchanting_writing_table_uses)
stack_result_meta:set_int('x_enchanting_writing_table_uses', new_x_enchanting_writing_table_uses + 1)
print('new_x_enchanting_writing_table_uses 2', stack_result_meta:get_int('x_enchanting_writing_table_uses'))
stack_result_meta:set_string(
'description',
stack_result:get_short_description() .. '\n' .. descriptions.enchantments_desc
)
stack_result_meta:set_string('x_enchanting', minetest.serialize(final_enchantments))
inv:set_stack('result', 1, stack_result)
return result_data
end
function XEnchanting.get_formspec_writing_table(self, pos, props)
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
local inv = minetest.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
local model_scroll_closed = '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_closed_idle[1].x .. ',' .. self.scroll_animations.scroll_closed_idle[1].y .. ';0]'
local model_scroll_is_open
local player_name = props.player_name
local data = props.data or {}
local show_crossed_arrow = data.show_crossed_arrow
local show_total_cost = data.show_total_cost
local formspec = {
'size[8,9]',
'bgcolor[#080808BB;true]',
'listcolors[#FFFFFF00;#FFFFFF1A;#FFFFFF00;#30434C;#FFF]',
get_formspec_bg(player_name),
'style_type[label;font=mono,bold]',
'style[slot_1,slot_2,slot_3;font=mono,bold;textcolor=#4D413A]',
'label[0, 0;' .. S('Enchant') .. ']',
-- item
'list[nodemeta:' .. spos .. ';item;2.75, 0.5;1, 1;]',
'image[2.75, 0.5;1,1;x_enchanting_gui_paper_bg.png]',
'image[3.75, 0.5;1,1;x_enchanting_gui_plus_icon.png]',
-- sacrifice
'list[nodemeta:' .. spos .. ';sacrifice;4.75, 0.5;1, 1;]',
'image[4.75, 0.5;1,1;x_enchanting_gui_paper_scroll_bg.png]',
show_crossed_arrow and 'image[5.75, 0.5;1,1;x_enchanting_gui_arrow_right_crossed_icon.png]'
or 'image[5.75, 0.5;1,1;x_enchanting_gui_arrow_right_icon.png]',
-- result
'list[nodemeta:' .. spos .. ';result;6.75, 0.5;1, 1;]',
'image[6.75, 0.5;1,1;x_enchanting_gui_paper_bg.png]',
-- trade
'list[nodemeta:' .. spos .. ';trade;6.75, 1.5;1, 1;]',
'image[6.75, 1.5;1,1;x_enchanting_gui_paper_trade_bg.png]',
-- inventories
'list[current_player;main;0, 4.85;8, 1;]',
'list[current_player;main;0, 6.08;8, 3;8]',
'listring[nodemeta:' .. spos .. ';trade]',
'listring[current_player;main]',
'listring[nodemeta:' .. spos .. ';sacrifice]',
'listring[current_player;main]',
'listring[nodemeta:' .. spos .. ';item]',
'listring[current_player;main]',
}
formspec[#formspec + 1] = get_hotbar_bg(0, 4.85)
formspec[#formspec + 1] = get_list_bg(0, 6.08)
if show_total_cost then
formspec[#formspec + 1] = 'style_type[label;font=mono,bold;textcolor=#4D413A]'
formspec[#formspec + 1] = 'label[3.125, 1.5;' .. S('Cost') .. ': ' .. data.total_cost .. ']'
end
-- -- data
-- if data then
-- for i, slot in ipairs(data.slots) do
-- if #slot.final_enchantments > 0 then
-- -- show buttons with content
-- 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 .. ']') .. ']'
-- 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 .. ']') .. ']'
-- end
-- formspec[#formspec + 1] = 'image[2.3,' .. -0.5 + i .. ';1,1;x_enchanting_image_trade_' .. i .. '.png;]'
-- else
-- -- disabled buttons
-- ---@diagnostic disable-next-line: codestyle-check
-- formspec[#formspec + 1] = 'image_button[3.125,' .. -0.5 + i .. ';5.125,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';]'
-- end
-- end
-- model_scroll_is_open = true
-- else
-- for i = 1, 3, 1 do
-- -- disabled buttons
-- ---@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 .. ';]'
-- end
-- model_scroll_is_open = false
-- end
-- if model_scroll_is_open then
-- formspec[#formspec + 1] = model_scroll_open
-- else
-- formspec[#formspec + 1] = model_scroll_closed
-- end
-- self.form_context[player_name] = {
-- data = data,
-- pos = pos
-- }
return table.concat(formspec, '')
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

@ -1,11 +1,27 @@
-- X Enchanting
-- by SaKeL
--[[
X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to juraj.vajda@gmail.com
--]]
local path = minetest.get_modpath('x_enchanting')
local mod_start_time = minetest.get_us_time()
dofile(path .. '/api.lua')
dofile(path .. '/table.lua')
dofile(path .. '/writing_table.lua')
---Check if string X starts with string Y
---@param str string
@ -19,18 +35,25 @@ minetest.register_on_mods_loaded(function()
-- Tools override
for name, tool_def in pairs(minetest.registered_tools) do
if XEnchanting:has_tool_group(name) then
XEnchanting:set_tool_enchantability(tool_def)
XEnchanting:set_enchantability(tool_def)
end
end
-- Ores override
-- Craft items override
for name, tool_def in pairs(minetest.registered_craftitems) do
if XEnchanting:has_tool_group(name) then
XEnchanting:set_enchantability(tool_def)
end
end
-- Ores override - Fortune
for _, def in pairs(minetest.registered_ores) do
if not XEnchanting.registered_ores[def.ore] then
XEnchanting.registered_ores[def.ore] = true
end
end
-- Entities override
-- Entities override - Looting
for name, def in pairs(minetest.registered_entities) do
if starts_with(name, 'mobs_animal:')
or starts_with(name, 'mobs_monster:')
@ -95,10 +118,68 @@ minetest.register_on_mods_loaded(function()
end
end
end
elseif starts_with(name, 'animalia:') then
if def.death_func and def.drops then
local prev_death_func = def.death_func
---@param self table
def.death_func = function(self)
local puncher = self._puncher
if not self
or not self.object
or not self.object:get_luaentity()
or not puncher
or not puncher:is_player()
or self._looting_dropped
then
return prev_death_func(self)
end
local wield_stack = puncher:get_wielded_item()
local wield_stack_meta = wield_stack:get_meta()
local looting = wield_stack_meta:get_float('is_looting')
if looting == 0 then
return prev_death_func(self)
end
local pos = self.object:get_pos()
prev_death_func(self)
local death_by_player = puncher and puncher:is_player()
if death_by_player and pos then
local tool_capabilities = wield_stack:get_tool_capabilities()
self._looting_dropped = true
for _, drop in ipairs(def.drops) do
if math.random(10, 100) / 100 < looting / (looting + 1) then
local drop_min = drop.min or 0
local drop_max = drop.max or 0
local count = math.random(drop_min, drop_max)
local stack = ItemStack({
name = drop.name,
count = count
})
local chance = math.random(1, tool_capabilities.max_drop_level)
stack:set_count(stack:get_count() * chance)
if stack:get_count() > 0 then
minetest.item_drop(stack, puncher, pos)
end
end
end
end
end
end
end
end
end)
---@diagnostic disable-next-line: unused-local
minetest.register_on_joinplayer(function(player, last_login)
XEnchanting.form_context[player:get_player_name()] = nil
@ -107,6 +188,7 @@ minetest.register_on_joinplayer(function(player, last_login)
end
end)
---@diagnostic disable-next-line: unused-local
minetest.register_on_leaveplayer(function(player, timed_out)
XEnchanting.form_context[player:get_player_name()] = nil
end)
@ -172,6 +254,7 @@ function minetest.handle_node_drops(pos, drops, digger)
return old_handle_node_drops(pos, drops, digger)
end
---@diagnostic disable-next-line: unused-local
minetest.register_on_player_hpchange(function(player, hp_change, reason)
-- Curse of Vanishing
if (player:get_hp() + hp_change) <= 0 then

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 385 KiB

View File

@ -1,3 +1,21 @@
/**
* Deploy code to CDB
* Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to juraj.vajda@gmail.com
*/
import fetch from 'node-fetch'
import yargs from 'yargs/yargs'
import {hideBin} from 'yargs/helpers'

View File

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

View File

@ -1,3 +1,22 @@
--[[
X Enchanting. Adds Enchanting Mechanics and API.
Copyright (C) 2023 SaKeL <juraj.vajda@gmail.com>
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
--]]
---@diagnostic disable-next-line
screwdriver = minetest.global_exists('screwdriver') and screwdriver --[[@as MtgScrewdriver]]
local S = minetest.get_translator(minetest.get_current_modname())
@ -12,6 +31,7 @@ minetest.register_node('x_enchanting:table', {
drawtype = 'mesh',
mesh = 'x_enchanting_table.obj',
tiles = { 'x_enchanting_table.png' },
use_texture_alpha = 'clip',
paramtype = 'light',
paramtype2 = 'facedir',
walkable = true,
@ -59,6 +79,7 @@ minetest.register_node('x_enchanting:table', {
---@param placer ObjectRef | nil
---@param itemstack ItemStack
---@param pointed_thing PointedThingDef
---@diagnostic disable-next-line: unused-local
after_place_node = function(pos, placer, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
@ -79,6 +100,7 @@ minetest.register_node('x_enchanting:table', {
---@param clicker ObjectRef
---@param itemstack ItemStack
---@param pointed_thing? PointedThingDef
---@diagnostic disable-next-line: unused-local
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local p_name = clicker:get_player_name()
@ -107,7 +129,7 @@ minetest.register_node('x_enchanting:table', {
local data = XEnchanting:get_enchantment_data(
clicker,
#bookshelfs,
minetest.registered_tools[item_stack:get_name()]
minetest.registered_tools[item_stack:get_name()] or minetest.registered_craftitems[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
@ -123,6 +145,7 @@ minetest.register_node('x_enchanting:table', {
---@param pos Vector
---@param intensity? number
---@return table | nil
---@diagnostic disable-next-line: unused-local
on_blast = function(pos, intensity)
if minetest.is_protected(pos, '') then
return
@ -159,11 +182,13 @@ minetest.register_node('x_enchanting:table', {
and inv:is_empty('trade')
and not minetest.is_protected(pos, player:get_player_name())
end,
---@diagnostic disable-next-line: unused-local
on_rotate = function(pos, node, user, mode, new_param2)
return false
end,
---@param pos Vector
---@param elapsed number
---@diagnostic disable-next-line: unused-local
on_timer = function(pos, elapsed)
-- entity
local table_scroll = minetest.get_objects_inside_radius(pos, 0.9)
@ -234,6 +259,7 @@ minetest.register_node('x_enchanting:table', {
end
-- symbol particles
---@diagnostic disable-next-line: unused-local
for i = 1, 10, 1 do
local pos_random = bookshelfs[math.random(1, #bookshelfs)]
local x = pos.x - pos_random.x
@ -275,6 +301,7 @@ minetest.register_node('x_enchanting:table', {
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local st_meta = stack:get_meta()
local st_name = stack:get_name()
@ -302,6 +329,7 @@ minetest.register_node('x_enchanting:table', {
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local st_name = stack:get_name()
@ -325,6 +353,7 @@ minetest.register_node('x_enchanting:table', {
---@param to_index number
---@param count number
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
return 0
end,
@ -333,12 +362,16 @@ minetest.register_node('x_enchanting:table', {
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
on_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
local p_name = player:get_player_name()
local inv = meta:get_inventory()
local item_stack = inv:get_stack('item', 1)
local item_stack_meta = item_stack:get_meta()
local is_enchanted = item_stack_meta:get_int('is_enchanted')
if not inv:is_empty('item') then
if not inv:is_empty('item') and is_enchanted == 0 then
-- bookshelfs
local bookshelfs = minetest.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
@ -346,11 +379,10 @@ minetest.register_node('x_enchanting:table', {
{ 'default:bookshelf', 'group:bookshelf' }
)
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data(
player,
#bookshelfs,
minetest.registered_tools[item_stack:get_name()]
minetest.registered_tools[item_stack:get_name()] or minetest.registered_craftitems[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
@ -366,12 +398,16 @@ minetest.register_node('x_enchanting:table', {
---@param index number
---@param stack ItemStack
---@param player ObjectRef
---@diagnostic disable-next-line: unused-local
on_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
local p_name = player:get_player_name()
local inv = meta:get_inventory()
local item_stack = inv:get_stack('item', 1)
local item_stack_meta = item_stack:get_meta()
local is_enchanted = item_stack_meta:get_int('is_enchanted')
if not inv:is_empty('item') then
if not inv:is_empty('item') and is_enchanted == 0 then
-- bookshelfs
local bookshelfs = minetest.find_nodes_in_area(
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
@ -379,11 +415,10 @@ minetest.register_node('x_enchanting:table', {
{ 'default:bookshelf', 'group:bookshelf' }
)
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data(
player,
#bookshelfs,
minetest.registered_tools[item_stack:get_name()]
minetest.registered_tools[item_stack:get_name()] or minetest.registered_craftitems[item_stack:get_name()]
)
local formspec = XEnchanting:get_formspec(pos, p_name, data)
@ -399,6 +434,7 @@ minetest.register_node('x_enchanting:table', {
---@param formname string
---@param fields table
---@param sender ObjectRef
---@diagnostic disable-next-line: unused-local
on_receive_fields = function(pos, formname, fields, sender)
local p_name = sender:get_player_name()
@ -451,7 +487,7 @@ minetest.register_node('x_enchanting:table', {
})
----
--- Entity (Scroll)
--- Entity - Scroll
----
minetest.register_entity('x_enchanting:table_scroll', {
@ -477,12 +513,14 @@ minetest.register_entity('x_enchanting:table_scroll', {
},
---@param self table
---@param killer ObjectRef
---@diagnostic disable-next-line: unused-local
on_death = function(self, killer)
self.object:remove()
end,
---@param self table
---@param staticdata StringAbstract
---@param dtime_s number
---@diagnostic disable-next-line: unused-local
on_activate = function(self, staticdata, dtime_s)
self._scroll_closed = true
self._tablechecktimer = 5
@ -496,6 +534,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
---@param self table
---@param dtime number
---@param moveresult? table
---@diagnostic disable-next-line: unused-local
on_step = function(self, dtime, moveresult)
local pos = self.object:get_pos()
@ -524,6 +563,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
local found_player = false
if #objects > 0 then
---@diagnostic disable-next-line: unused-local
for i, obj in ipairs(objects) do
if obj:is_player() and obj:get_pos() then
-- player
@ -531,6 +571,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
local distance = vector.distance(pos, obj:get_pos())
if distance < shortest_distance then
shortest_distance = distance
self._player = obj
end
end
@ -584,6 +625,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
---@param dir Vector
---@param damage number
---@return boolean | nil
---@diagnostic disable-next-line: unused-local
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
return true
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 B

After

Width:  |  Height:  |  Size: 108 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 B

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 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 registered_nodes table<string, NodeDef|ItemDef> Map of registered node definitions, indexed by name
---@field registered_ores table<string, table> Map of registered ore definitions, indexed by name
---@field registered_craftitems table<string, table> Map of registered craft items, indexed by name
---@field after fun(time: number|integer, func: fun(...), ...): JobTable Call the function `func` after `time` seconds, may be fractional. Optional: Variable number of arguments that are passed to `func`.
---@field sound_play fun(spec: SimpleSoundSpec|string, parameters: SoundParamDef, ephemeral?: boolean): any Returns a `handle`. Ephemeral sounds will not return a handle and can't be stopped or faded. It is recommend to use this for short sounds that happen in response to player actions (e.g. door closing).
---@field add_particlespawner fun(particlespawner_definition: ParticlespawnerDef): number|integer Add a `ParticleSpawner`, an object that spawns an amount of particles over `time` seconds. Returns an `id`, and -1 if adding didn't succeed.
@ -93,6 +94,8 @@
---@field handle_node_drops fun(pos: Vector, drops: string[], digger: ObjectRef) `drops`: list of itemstrings. Handles drops from nodes after digging: Default action is to put them into digger's inventory. Can be overridden to get different functionality (e.g. dropping items on ground)
---@field register_on_dieplayer fun(func: fun(player: ObjectRef, reason?: string)): nil Called when a player dies. `reason`: a PlayerHPChangeReason table, see register_on_player_hpchange
---@field register_on_player_hpchange fun(func: fun(player, hp_change, reason), modifier): number Called when the player gets damaged or healed. `player`: ObjectRef of the player. `hp_change`: the amount of change. Negative when it is damage.. `reason`: a PlayerHPChangeReason table.. The `type` field will have one of the following values: `set_hp`: A mod or the engine called `set_hp` without giving a type - use this for custom damage types.. `punch`: Was punched. `reason.object` will hold the puncher, or nil if none. `fall`, `node_damage`: `damage_per_second` from a neighbouring node. `reason.node` will hold the node name or nil. `drown` `respawn`. Any of the above types may have additional fields from mods. `reason.from` will be `mod` or `engine`. `modifier`: when true, the function should return the actual `hp_change`. Note: modifiers only get a temporary `hp_change` that can be modified by later modifiers. Modifiers can return true as a second argument to stop the execution of further functions. Non-modifiers receive the final HP change calculated by the modifiers.
---@field itemstring_with_palette fun(item: string | ItemStack, palette_index: integer): string returns an item string. Creates an item string which contains palette index information for hardware colorization. You can use the returned string as an output in a craft recipe. `item`: the item stack which becomes colored. Can be in string, table and native form. `palette_index`: this index is added to the item stack
---@field get_player_information fun(player_name: string): table Table containing information about a player
---Minetest settings
---@class MinetestSettings

View File

@ -20,6 +20,7 @@
---@field walkable boolean If true, objects collide with node.
---@field after_dig_node fun(pos: Vector, oldnode: NodeDef, oldmetadata: table, digger: ObjectRef): nil oldmetadata is in table format. Called after destructing node when node was dug using minetest.node_dig / minetest.dig_node., default: nil
---@field paramtype2 string
---@field palette string Image
---@field can_dig fun(pos: Vector, player?: ObjectRef): boolean | nil Returns true if node can be dug, or false if not.default: nil
---@field on_rotate fun(pos: Vector, node: table, user: ObjectRef, mode: table, new_param2: string): boolean Only for screwdriver mod.
---@field on_construct fun(pos: Vector): nil Node constructor; called after adding node. Can set up metadata and stuff like that. Not called for bulk node placement (i.e. schematics and VoxelManip). default: nil

View File

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

View File

@ -6,7 +6,7 @@
---@field roman_numbers table<number, string> Convert Arabic numbers to Roman numbers
---@field enchantment_defs table<'sharpness' | 'fortune' | 'unbreaking' | 'efficiency' | 'silk_touch' | 'curse_of_vanishing' | 'knockback', EnchantmentDef>
---@field has_tool_group fun(self: XEnchanting, name: string): string | boolean Check if tool has one of the known tool groups, returns `false` otherwise.
---@field set_tool_enchantability fun(self: XEnchanting, tool_def: ItemDef): nil Sets `enchantibility` group and values from base table (`XEnchanting.tools_enchantability`) to all registered tools (atching known group names). If tool has already `enchantibility` group defined it will take the defined value insted.
---@field set_enchantability fun(self: XEnchanting, tool_def: ItemDef): nil Sets `enchantibility` group and values from base table (`XEnchanting.tools_enchantability`) to all registered tools (atching known group names). If tool has already `enchantibility` group defined it will take the defined value insted.
---@field get_enchanted_tool_capabilities fun(self: XEnchanting, tool_def: ItemDef, enchantments: Enchantment[]): ToolCapabilitiesDef Applies enchantments to item tool capabilities.
---@field set_enchanted_tool fun(self: XEnchanting, pos: Vector, itemstack: ItemStack, level: number, player_name: string): nil Set choosen enchantment and its modified tool capabilities to itemstack and `item` inventory. This will also get new `randomseed`.
---@field get_enchantment_data fun(self: XEnchanting, player: ObjectRef, nr_of_bookshelfs: number, tool_def: ItemDef): EnchantmentData Algoritm to get aplicable random enchantments.
@ -15,6 +15,7 @@
---@field scroll_animations table<string, table> Parameters for `ObjectRef` `set_animation` method
---@field player_seeds table<string, number | integer>
---@field registered_ores table<string, boolean> Table with registered ores, `key` ore name
---@field settings {["x_enchanting_small_formspec"]: boolean}
---Enchantment definition
@ -49,5 +50,5 @@
---@class EnchantmentDataSlot
---@field level number
---@field final_enchantments Enchantment[]
---@field tool_cap_data ToolCapabilitiesDef
---@field tool_cap_data ToolCapabilitiesDef | nil
---@field descriptions {["enchantments_desc"]: string, ["enchantments_desc_masked"]: string }

329
writing_table.lua Normal file
View File

@ -0,0 +1,329 @@
screwdriver = minetest.global_exists('screwdriver') and screwdriver --[[@as MtgScrewdriver]]
local S = minetest.get_translator(minetest.get_current_modname())
----
--- Table Node
----
minetest.register_node('x_enchanting:writing_table', {
description = S('Writing Table'),
short_description = S('Writing Table'),
---top, bottom, sides...front
tiles = {
'x_enchanting_writing_table_top.png',
'x_enchanting_writing_table_bottom.png',
'x_enchanting_writing_table_side.png',
'x_enchanting_writing_table_side.png',
'x_enchanting_writing_table_side.png',
'x_enchanting_writing_table_front.png'
},
paramtype = 'light',
paramtype2 = 'facedir',
walkable = true,
wield_scale = { x = 2, y = 2, z = 2 },
sounds = {
footstep = {
name = 'x_enchanting_scroll',
gain = 0.2
},
dug = {
name = 'x_enchanting_scroll',
gain = 1.0
},
place = {
name = 'x_enchanting_scroll',
gain = 1.0
}
},
is_ground_content = false,
groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
mod_origin = 'x_enchanting',
---@param pos Vector
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
meta:set_string('infotext', S('Writing Table'))
meta:set_string('owner', '')
inv:set_size('item', 1)
inv:set_size('sacrifice', 1)
inv:set_size('result', 1)
inv:set_size('trade', 1)
end,
---@param pos Vector
---@param placer ObjectRef | nil
---@param itemstack ItemStack
---@param pointed_thing PointedThingDef
after_place_node = function(pos, placer, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
if not placer then
return
end
local player_name = placer:get_player_name()
local props = {
player_name = player_name
}
meta:set_string('owner', player_name)
meta:set_string('infotext', S('Writing Table') .. ' (' .. S('owned by') .. ' ' .. player_name .. ')')
local formspec = XEnchanting:get_formspec_writing_table(pos, props)
meta:set_string('formspec', formspec)
end,
---@param pos Vector
---@param node NodeDef
---@param clicker ObjectRef
---@param itemstack ItemStack
---@param pointed_thing? PointedThingDef
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local p_name = clicker:get_player_name()
local props = {
player_name = p_name,
inv = inv
}
if minetest.is_protected(pos, p_name) then
return itemstack
end
minetest.sound_play('x_enchanting_scroll', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
}, true)
local data = XEnchanting:get_writing_table_total_data(props)
props.data = data
local formspec = XEnchanting:get_formspec_writing_table(pos, props)
meta:set_string('formspec', formspec)
return itemstack
end,
---@param pos Vector
---@param intensity? number
---@return table | nil
on_blast = function(pos, intensity)
if minetest.is_protected(pos, '') then
return
end
local drops = {}
local inv = minetest.get_meta(pos):get_inventory()
local stack_item = inv:get_stack('item', 1)
local stack_sacrifice = inv:get_stack('sacrifice', 1)
local stack_result = inv:get_stack('result', 1)
local stack_trade = inv:get_stack('trade', 1)
if not stack_item:is_empty() then
drops[#drops + 1] = stack_item:to_table()
end
if not stack_sacrifice:is_empty() then
drops[#drops + 1] = stack_sacrifice:to_table()
end
if not stack_result:is_empty() then
drops[#drops + 1] = stack_result:to_table()
end
if not stack_trade:is_empty() then
drops[#drops + 1] = stack_trade:to_table()
end
drops[#drops + 1] = 'x_enchanting:writing_table'
minetest.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 = minetest.get_meta(pos):get_inventory()
return inv:is_empty('item')
and inv:is_empty('sacrifice')
and inv:is_empty('result')
and inv:is_empty('trade')
and not minetest.is_protected(pos, player:get_player_name())
end,
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
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
print('allow_metadata_inventory_put')
local st_name = stack:get_name()
local st_meta = stack:get_meta()
if listname == 'result' then
return 0
end
if listname == 'item'
and (st_meta:get_int('is_enchanted') > 0 or minetest.get_item_group(st_name, 'scroll') > 0)
then
return stack:get_count()
elseif listname == 'sacrifice'
and st_meta:get_int('is_enchanted') > 0
and minetest.get_item_group(st_name, 'scroll') > 0
then
return stack:get_count()
elseif listname == 'trade'
and (
st_name == 'default:mese_crystal'
or minetest.get_item_group(st_name, 'enchanting_trade') > 0
)
then
return stack:get_count()
end
return 0
end,
---@param pos Vector
---@param listname string
---@param index number
---@param stack ItemStack
---@param player ObjectRef
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
print('allow_metadata_inventory_take')
local st_name = stack:get_name()
if listname == 'item' then
return stack:get_count()
elseif listname == 'sacrifice' then
return stack:get_count()
elseif listname == 'result' then
return stack:get_count()
elseif listname == 'trade'
and (
st_name == 'default:mese_crystal'
or minetest.get_item_group(st_name, 'enchanting_trade') > 0
)
then
return stack:get_count()
end
return 0
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
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
print('allow_metadata_inventory_move')
print('allow_metadata_inventory_move')
print('from_list', from_index, from_list)
print('to_list', to_index, to_list)
if (to_list == 'item' or to_list == 'sacrifice')
and (from_list == 'item' or from_list == 'sacrifice')
then
return count
end
return 0
end,
---@param pos Vector
---@param listname string
---@param index number
---@param stack ItemStack
---@param player ObjectRef
on_metadata_inventory_put = function(pos, listname, index, stack, player)
print('on_metadata_inventory_put')
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local props = {
player_name = player:get_player_name(),
inv = inv
}
local data = XEnchanting:get_writing_table_total_data(props)
props.data = data
local formspec = XEnchanting:get_formspec_writing_table(pos, props)
meta:set_string('formspec', formspec)
end,
---@param pos Vector
---@param listname string
---@param index number
---@param stack ItemStack
---@param player ObjectRef
on_metadata_inventory_take = function(pos, listname, index, stack, player)
print('on_metadata_inventory_take')
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if listname == 'result' then
inv:set_stack('item', 1, ItemStack(''))
inv:set_stack('sacrifice', 1, ItemStack(''))
inv:set_stack('result', 1, ItemStack(''))
return
end
local props = {
player_name = player:get_player_name(),
inv = inv
}
local data = XEnchanting:get_writing_table_total_data(props)
props.data = data
local formspec = XEnchanting:get_formspec_writing_table(pos, props)
meta:set_string('formspec', formspec)
end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
print('on_metadata_inventory_move')
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local props = {
player_name = player:get_player_name(),
inv = inv
}
local data = XEnchanting:get_writing_table_total_data(props)
props.data = data
local formspec = XEnchanting:get_formspec_writing_table(pos, props)
meta:set_string('formspec', formspec)
end,
-- form receive fields
---@param pos Vector
---@param formname string
---@param fields table
---@param sender ObjectRef
on_receive_fields = function(pos, formname, fields, sender)
end
})
-- Scroll Item
minetest.register_craftitem('x_enchanting:scroll_item', {
description = S('Scroll'),
short_description = S('Scroll'),
inventory_image = 'x_enchanting_scroll_item.png^[colorize:#8F00FF:60',
wield_image = 'x_enchanting_scroll_item.png^[transformFXR90^[colorize:#8F00FF:60',
groups = { scroll = 1, flammable = 3 },
stack_max = 1
})