1
0
mirror of https://bitbucket.org/minetest_gamers/x_enchanting.git synced 2025-04-21 17:50:21 +02:00

Add content, assets, scripts, sounds

This commit is contained in:
Juraj Vajda 2022-11-09 15:39:27 -05:00
parent 1e4d8d0663
commit 4b795b7ed3
45 changed files with 2430 additions and 309 deletions

18
.cdb.json Normal file
View File

@ -0,0 +1,18 @@
{
"type": "MOD",
"title": "X Enchanting",
"name": "x_enchanting",
"short_description": "Adds Enchanting Mechanics and API.",
"dev_state": "MAINTENANCE_ONLY",
"tags": [
"pvp",
"shooter",
"survival",
"tools",
"magic"
],
"license": "LGPL-2.1-or-later",
"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"
}

88
.luarc.json Normal file
View File

@ -0,0 +1,88 @@
{
"$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
"runtime": {
"version": "Lua 5.1",
"path": [
"?.lua",
"?/init.lua"
],
"pathStrict": true
},
"workspace": {
"maxPreload": 1600,
"preloadFileSize": 1000,
"ignoreDir": [
"/locale/",
"/libs/",
"/3rd",
"/.vscode",
"/meta",
"/.git",
"/docs",
"/bin"
],
"checkThirdParty": false
},
"typeFormat": {
"config": {
"format_line": "false"
}
},
"type": {
"castNumberToInteger": true
},
"doc": {
"privateName": [
"_*"
]
},
"diagnostics": {
"disable": [
"close-non-object"
],
"groupFileStatus": {
"ambiguity": "Any",
"await": "Any",
"duplicate": "Any",
"global": "Any",
"luadoc": "Any",
"redefined": "Any",
"strict": "Any",
"type-check": "Any",
"unbalanced": "Any",
"unused": "Any"
},
"ignoredFiles": "Disable",
"libraryFiles": "Disable",
"neededFileStatus": {
"codestyle-check": "Any"
},
"disableScheme": [
"git",
"type"
],
"globals": [
"DIR_DELIM",
"INIT",
"minetest",
"core",
"dump",
"dump2",
"Raycast",
"Settings",
"PseudoRandom",
"PerlinNoise",
"VoxelManip",
"SecureRandom",
"VoxelArea",
"PerlinNoiseMap",
"PcgRandom",
"ItemStack",
"AreaStore",
"unpack",
"vector",
"default",
"XEnchanting"
]
}
}

1
.npmrc Normal file
View File

@ -0,0 +1 @@
engine-strict=true

258
README.md
View File

@ -1,73 +1,66 @@
# Enchanting Table and API [x_enchanting] # Enchanting Table and API [x_enchanting]
Adds bow and arrows to Minetest. The goal of this Mod is to make most complete single bow with arrow what will work with MTG damage system, time from last punch as simple as possible. Eventually due to the damage tiers in MTG additional arrows were added. Adds Enchanting Mechanics and API.
![screenshot](screenshot.1.png) ![screenshot](screenshot.png)
Video: https://youtu.be/pItpltmUoa8
## Features ## Features
* bow will force you sneak when loaded (optional dep. playerphysics) * adds enchanting table
* loaded bow will slightly adjust the player FOV * supports all registered tools with known tool groups: pickaxe, shovel, axe, sword, e.g. `groups = {pickaxe = 1}`
* bow uses minetest tool capabilities - if the bow is not loaded for long enough (time from last puch) the arrow will fly shorter range * supports `default:bookshelf` or anything with `group:bookshelf`
* charged bow in inventory will discharge and give back the arrow when not selected * enchanting trade is for `default:mese_crystal` or anything with `groups = {enchanting_trade = 1}`
* arrow uses raycast * adds enchantability for all MT default tools, for custom tools the enchantability can be set in the item group, e.g. `groups = {enchantability = 15}`
* arrow has chance of critical shots/hits (only on full punch interval) * uses only MT engine mechanics (e.g. tool capabilities)
* arrow uses minetest damage calculation (including 3d_armor) for making damage (no hardcoded values) * enchanted items have detailed enchantments description/short description
* arrows stick to nodes, players and entitites * mesh node model
* arrows remove them self from the world after some time * mesh entity model and animations
* arrows remove them self if there are already too many arrows attached to node, player, entity
* arrow continues to fly downwards when attached node is dug 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
* arrow flies under water for short period of time and then sinks
* arrows adjusts pitch when flying
* arrows can be picked up again after stuck in solid nodes
* registers only one entity reused for all arrows
* target block reduces fall damage by -30
* quiver for more arrow storage (can hold only arrows)
* quiver perks when in inventory (faster arrows, more arrow damage...)
* quiver shows temporarily its inventory in HUD overlay when loading or shooting (quickview)
* quiver item shows its content in infotext (hover over the item)
* X Bows API for creating custom shooters and projectiles
* 3d quiver shown in 3rd person view (compatible with 3d armor)
## How To ## How To
### Bow ### Enchanting Setup
With the bow selected in hotbar and in your hand, press right click on mouse (PC) or the same action as when placing blocks, to load the bow. For maximum level of enchantments you need maximum of 15 bookshelfs around the enchanting table, maximum of 2 blocks away. Maximum level is 30.
For bow to be loaded you have to have arrows in the arrow/quiver inventory - there should be extra tab in your inventory MOD to show arrow and quiver inventory slots.
Arrows and quivers in the players main inventory don't count and will not be used.
You have to have arrows and/or quiver in dedicated arrow/quiver inventory slots in order to charge the bow.
Charging bow will have slight sound effect and can be fired at any time with left click (PC)
or the same action as when you are digging a block. Waiting for full charge of the bow is recommended
as it will give the arrow full speed (maximum shooting distance) and chance for critical arrow (double damage).
There are few indications on how to know when the bow is fully charged: ### Enchanting Table
* there is a distinct "click" sound To be able to enchant items you need a trade item (by default `default:mese_crystal` or anything with `groups = {enchanting_trade = 1}`). Higher level enchantments costs more: level 1,2,3 costs mese crystals 1,2,3.
* each arrow has "charge time" in the description When place item in the enchantment table item slot you can see three buttons with:
* after shooting, arrow will have particle trail - left side the trade cost amount
- middle the enchantment you will get + maybe more enchantments
- right side the base level for the enchantment (depends on how many bookshelfs are in reach)
When you place item in the enchantment table trade slot you will see that some buttons are now enabled and can be clicked on - depends on trade amount available.
The enchantments shown in the enchantment table will be always the same for item - this will change after you enchant an item. After enchanting an item a new enchantments will be shown.
There are few indications on how to know when the arrow is a critical arrow: ### Item Enchantability
* there is a distinct arrow flying sound All default tools have enchantability set. Higher enchantability = higher chance of getting better enchantments.
* after shooting, arrow will have red particle trail Items enchantibility from worst to best:
- stone
- diamond
- steel
- wood / mese
- bronze
If you shoot the arrow before the bow is fully charged the speed/distance will be lower and no arrow particle trail will be shown (also no chance for critical arrow). ### Every Enchantment Explained
Changing the selection in hotbar will unload the bow and give you back arrow from the unloaded bow - this applies also when login in to the game (bow will be discharged and arrow will be returned to inventory) and also when you drop the charged arrow (discharged bow will be dropped with arrow item).
If you have `playerphysics` or `player_monoids` mod installed, charged bow will slow you down until you release the arrow.
### Quiver #### Sharpness
Quiver item can hold inventory of arrows. When player has quiver in his/hers quiver inventory slot - there should be extra tab in your inventory MOD to show arrow and quiver inventory slots, bow can take arrows from quiver, otherwise arrows outside of the quiver are used to load the bow. Increases melee damage.
Though, if arrows from quiver are used to load the bow, the arrows have additional speed and damage.
If we are loading/shooting arrows from quiver, there is temporary quickview HUD overlay shown, peeking in to the quivers inventory from which the arrow was taken. Arrows used from quiver will be faster only when the bow is fully charged - see "How To - Bow" for more information on how to know when bow is fully charged.
There are few indications on how to know when the bow shot arrow from quiver: #### Fortune
* there is temporary HUD overview shown peeking in to the quiver inventory Increases the number and/or chances of specific item drops. This value is not used in the engine; it is the responsibility of the game/mod code to implement this.
* after shooting, arrow will have blue/purple particle trail (if bow was fully charged)
#### Unbreaking
Increases the item's durability.
#### Efficiency
Increases the player's mining speed. Also adds mining groupcaps to item, e.g. enchanted wood pickaxe can mine level 1 nodes (e.g. obsidian) after enchantment.
## Dependencies ## Dependencies
@ -75,21 +68,7 @@ There are few indications on how to know when the bow shot arrow from quiver:
## Optional Dependencies ## Optional Dependencies
- default (recipes) - xdecor (adjusts conflicting recipe)
- farming (bow and target recipes)
- 3d_armor (calculates damage including the armor)
- mesecons (target can be used to trigger mesecon signal)
- playerphysics (force sneak when holding charged bow)
- player_monoids (force sneak when holding charged bow)
- wool (quiver recipe)
- i3
- unified_inventory
- simple_skins
- u_skins
- wardrobe
- sfinv
- skinsdb
- player_api (shows 3d quiver)
## License: ## License:
@ -101,106 +80,75 @@ GNU Lesser General Public License v2.1 or later (see included LICENSE file)
**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_bows_bow_wood.png - x_enchanting_symbol_1.png
- x_bows_bow_wood_charged.png - x_enchanting_symbol_2.png
- x_bows_arrow_wood.png - x_enchanting_symbol_3.png
- x_bows_arrow_particle.png - x_enchanting_symbol_4.png
- x_bows_bubble.png - x_enchanting_symbol_5.png
- x_bows_target.png - x_enchanting_symbol_6.png
- x_enchanting_symbol_7.png
Modified by SaKeL: - x_enchanting_symbol_8.png
- x_enchanting_symbol_9.png
- x_bows_arrow_stone.png - x_enchanting_symbol_10.png
- x_bows_arrow_bronze.png - x_enchanting_symbol_11.png
- x_bows_arrow_steel.png - x_enchanting_symbol_12.png
- x_bows_arrow_mese.png - x_enchanting_symbol_13.png
- x_bows_arrow_diamond.png - x_enchanting_symbol_14.png
- x_enchanting_symbol_15.png
**CC-BY-SA-3.0, by paramat** - x_enchanting_symbol_16.png
- x_enchanting_symbol_17.png
- x_bows_hotbar_selected.png - x_enchanting_symbol_18.png
- x_bows_quiver_hotbar.png - x_enchanting_symbol_19.png
- x_bows_single_hotbar.png - x_enchanting_symbol_20.png
- x_enchanting_symbol_21.png
- x_enchanting_symbol_22.png
- x_enchanting_symbol_23.png
- x_enchanting_symbol_24.png
- x_enchanting_symbol_25.png
- x_enchanting_symbol_26.png
**LGPL-2.1-or-later, by SaKeL** **LGPL-2.1-or-later, by SaKeL**
- x_bows_quiver.png - x_enchanting_image_button_disabled.png
- x_bows_quiver_open.png - x_enchanting_image_button.png
- x_bows_arrow_slot.png - x_enchanting_image_trade_1.png -- Derived from a texture by VanessaE (CC BY-SA 3.0)
- x_bows_arrow_mesh.png - x_enchanting_image_trade_2.png -- Derived from a texture by VanessaE (CC BY-SA 3.0)
- x_bows_quiver_mesh.png - x_enchanting_image_trade_3.png -- Derived from a texture by VanessaE (CC BY-SA 3.0)
- x_bows_quiver_empty_mesh.png - 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_bows_quiver_blank_mesh.png - x_enchanting_scroll_mesh.png -- Derived from a texture TumeniNodes (CC BY-SA 3.0)
- x_bows_quiver_slot.png - x_enchanting_scroll_particle.png
- x_bows_dmg_0.png - x_enchanting_table.png
- x_bows_dmg_1.png - x_enchanting_trade_slot.png
- x_bows_dmg_2.png
- x_bows_dmg_3.png
- x_bows_dmg_4.png
- x_bows_dmg_5.png
- x_bows_dmg_6.png
- x_bows_dmg_7.png
- x_bows_dmg_8.png
- x_bows_dmg_9.png
### Sounds
**Creative Commons License, EminYILDIRIM**, https://freesound.org
- x_bows_bow_load.1.ogg
- x_bows_bow_load.2.ogg
- x_bows_bow_load.3.ogg
**Creative Commons License, bay_area_bob**, https://freesound.org
- x_bows_bow_loaded.ogg
**Creative Commons License**, https://freesound.org
- x_bows_bow_shoot_crit.ogg
**Creative Commons License, robinhood76**, https://freesound.org
- x_bows_arrow_hit.1.ogg
- x_bows_arrow_hit.2.ogg
- x_bows_arrow_hit.3.ogg
**Creative Commons License, brendan89**, https://freesound.org
- x_bows_bow_shoot.1.ogg
**Creative Commons License, natty23**, https://freesound.org
- x_bows_arrow_successful_hit.ogg
**Creative Commons License, Shamewap**, https://freesound.org
- x_bows_quiver.1.ogg
- x_bows_quiver.2.ogg
- x_bows_quiver.3.ogg
- x_bows_quiver.4.ogg
- x_bows_quiver.5.ogg
- x_bows_quiver.6.ogg
- x_bows_quiver.7.ogg
- x_bows_quiver.8.ogg
- x_bows_quiver.9.ogg
### Models ### Models
**LGPL-2.1-or-later, by SaKeL** **LGPL-2.1-or-later, by SaKeL**
- x_bows_arrow.obj - x_enchanting_scroll.b3d
- x_bows_arrow.blend - x_enchanting_table.obj
**Original model by MirceaKitsune (CC BY-SA 3.0).** ### Sounds
**Various alterations and fixes by kilbith, sofar, xunto, Rogier-5, TeTpaAka, Desour, stujones11, An0n3m0us (CC BY-SA 3.0):**
Modified by SaKeL (added quiver): **Mixkit Sound Effects Free License**, Sound effects obtained from https://mixkit.co
- x_bows_3d_armor_character.b3d x_enchanting_enchant.ogg
- x_bows_3d_armor_character.blend
- x_bows_character.b3d **Standard License**, Sound effects obtained from https://mixkit.co
- x_bows_character.blend
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
x_enchanting_scroll.10.ogg
x_enchanting_scroll.11.ogg
x_enchanting_scroll.12.ogg
x_enchanting_scroll.13.ogg
x_enchanting_scroll.14.ogg
## Installation ## Installation

254
api.lua
View File

@ -1,5 +1,7 @@
default = minetest.global_exists('default') and default --[[@as MtgDefault]]
---@type XEnchanting
XEnchanting = { XEnchanting = {
-- add enchantability to default tools
tools_enchantability = { tools_enchantability = {
-- picks -- picks
['default:pick_wood'] = 15, ['default:pick_wood'] = 15,
@ -119,6 +121,14 @@ XEnchanting = {
}, },
weight = 10 weight = 10
} }
},
randomseed = os.time(),
form_context = {},
scroll_animations = {
scroll_open = { { x = 1, y = 40 }, 80, 0, false },
scroll_close = { { x = 45, y = 84 }, 80, 0, false },
scroll_open_idle = { { x = 41, y = 42 }, 0, 0, false },
scroll_closed_idle = { { x = 43, y = 44 }, 0, 0, false }
} }
} }
@ -163,28 +173,51 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
local tool_stack = ItemStack({ name = tool_def.name }) local tool_stack = ItemStack({ name = tool_def.name })
local tool_capabilities = tool_stack:get_tool_capabilities() local tool_capabilities = tool_stack:get_tool_capabilities()
local enchantments_desc = {} local enchantments_desc = {}
local enchantments_desc_masked = {}
-- print('tool_capabilities #1', dump(tool_capabilities))
for i, enchantment in ipairs(enchantments) do for i, enchantment in ipairs(enchantments) do
-- Efficiency -- Efficiency
if enchantment.id == 'efficiency' then if enchantment.id == 'efficiency' then
-- apply enchantment
if tool_capabilities.groupcaps then if tool_capabilities.groupcaps then
-- groupcaps -- groupcaps
for group_name, def in pairs(tool_capabilities.groupcaps) do for group_name, def in pairs(tool_capabilities.groupcaps) do
local highest_cap_level = 0
-- times -- times
if def.times then if def.times then
local old_times = def.times local old_times = def.times
local new_times = {} local new_times = {}
for lvl, old_time in ipairs(old_times) do for lvl, old_time in pairs(old_times) do
local new_time = old_time - (old_time * (enchantment.value / 100)) local new_time = old_time - (old_time * (enchantment.value / 100))
if new_time < 0.15 then if new_time < 0.15 then
new_time = 0.15 new_time = 0.15
end end
table.insert(new_times, lvl, new_time) if highest_cap_level < lvl then
highest_cap_level = lvl
end
new_times[lvl] = new_time
end
-- extend groupcaps levels
while highest_cap_level > 1 do
highest_cap_level = highest_cap_level - 1
if not new_times[highest_cap_level] then
-- add new cap level time
local old_time = new_times[highest_cap_level + 1]
local new_time = old_time * 2
if new_time < 0.15 then
new_time = 0.15
end
new_times[highest_cap_level] = new_time
end
end end
tool_capabilities.groupcaps[group_name].times = new_times tool_capabilities.groupcaps[group_name].times = new_times
@ -213,6 +246,12 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
.. ' ' .. ' '
.. self.roman_numbers[enchantment.level] .. self.roman_numbers[enchantment.level]
if #enchantments_desc_masked == 0 then
enchantments_desc_masked[#enchantments_desc_masked + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
end
end end
end end
@ -243,6 +282,12 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
.. ' ' .. ' '
.. self.roman_numbers[enchantment.level] .. self.roman_numbers[enchantment.level]
if #enchantments_desc_masked == 0 then
enchantments_desc_masked[#enchantments_desc_masked + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
end
end end
end end
@ -258,6 +303,12 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
.. ' ' .. ' '
.. self.roman_numbers[enchantment.level] .. self.roman_numbers[enchantment.level]
if #enchantments_desc_masked == 0 then
enchantments_desc_masked[#enchantments_desc_masked + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
end
end end
-- Fortune -- Fortune
@ -270,29 +321,65 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name enchantments_desc[#enchantments_desc + 1] = self.enchantment_defs[enchantment.id].name
.. ' ' .. ' '
.. self.roman_numbers[enchantment.level] .. self.roman_numbers[enchantment.level]
if #enchantments_desc_masked == 0 then
enchantments_desc_masked[#enchantments_desc_masked + 1] = self.enchantment_defs[enchantment.id].name
.. ' '
.. self.roman_numbers[enchantment.level]
end
end end
end end
enchantments_desc = minetest.colorize('#C70039', '\nEnchanted\n') .. table.concat(enchantments_desc, '\n') enchantments_desc = '\n' .. minetest.colorize('#AE81FF', 'Enchanted')
.. '\n' .. table.concat(enchantments_desc, '\n')
enchantments_desc_masked = table.concat(enchantments_desc_masked, '') .. '...?'
-- print('tool_capabilities #2', dump(tool_capabilities)) return {
-- print('enchantments_desc', enchantments_desc) tool_capabilities = tool_capabilities,
-- return { enchantments_desc = enchantments_desc,
-- tool_capabilities = tool_capabilities, enchantments_desc_masked = enchantments_desc_masked
-- enchantments_desc = enchantments_desc }
-- }
end end
function XEnchanting.set_enchanted_tool_capabilities(self, itemstack, capabilities, description) function XEnchanting.set_enchanted_tool(self, pos, itemstack, capabilities, description, level, player_name)
-- local tool_def = minetest.registered_tools[itemstack:get_name()] local inv = minetest.get_meta(pos):get_inventory()
-- minetest.override_item(tool_def.name, { local tool_def = minetest.registered_tools[itemstack:get_name()]
-- groups = mergeTables(tool_def.groups, { enchantability = 0 }),
-- tool_capabilities = tool_capabilities if not tool_def then
-- }) return
end end
function XEnchanting.get_base_enchantment_level(self, nr_of_bookshelfs, tool_def) local stack_meta = itemstack:get_meta()
stack_meta:set_tool_capabilities(capabilities)
stack_meta:set_string('description', itemstack:get_description() .. '\n' .. description)
stack_meta:set_string('short_description', 'Enchanted ' .. itemstack:get_short_description())
stack_meta:set_int('is_enchanted', 1)
inv:set_stack('item', 1, itemstack)
local trade_stack = inv:get_stack('trade', 1)
trade_stack:take_item(level)
inv:set_stack('trade', 1, trade_stack)
-- set new seed
self.randomseed = tonumber(tostring(os.time()):reverse():sub(1, 9)) --[[@as number]]
local formspec = self:get_formspec(pos, player_name)
minetest.show_formspec(player_name, 'x_enchanting:table', formspec)
minetest.sound_play('x_enchanting_enchant', {
gain = 0.3,
pos = pos,
max_hear_distance = 10
}, true)
end
function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
local _nr_of_bookshelfs = nr_of_bookshelfs local _nr_of_bookshelfs = nr_of_bookshelfs
local data = {
slots = {}
}
if _nr_of_bookshelfs > 15 then if _nr_of_bookshelfs > 15 then
_nr_of_bookshelfs = 15 _nr_of_bookshelfs = 15
@ -303,34 +390,28 @@ function XEnchanting.get_base_enchantment_level(self, nr_of_bookshelfs, tool_def
---- ----
-- Base enchantment -- Base enchantment
math.randomseed(self.randomseed)
local base = math.random(1, 8) + math.floor(_nr_of_bookshelfs / 2) + math.random(0, _nr_of_bookshelfs) local base = math.random(1, 8) + math.floor(_nr_of_bookshelfs / 2) + math.random(0, _nr_of_bookshelfs)
local top_slot_base_level = math.floor(math.max(base / 3, 1)) local top_slot_base_level = math.floor(math.max(base / 3, 1))
local middle_slot_base_level = math.floor((base * 2) / 3 + 1) local middle_slot_base_level = math.floor((base * 2) / 3 + 1)
local bottom_slot_base_level = math.floor(math.max(base, _nr_of_bookshelfs * 2)) local bottom_slot_base_level = math.floor(math.max(base, _nr_of_bookshelfs * 2))
-- print('top_slot_base_level', top_slot_base_level) for i, slot_lvl in ipairs({ top_slot_base_level, middle_slot_base_level, bottom_slot_base_level }) do
-- print('middle_slot_base_level', middle_slot_base_level)
-- print('bottom_slot_base_level', bottom_slot_base_level)
---- ----
-- 1 Applying modifiers to the enchantment level -- 1 Applying modifiers to the enchantment level
---- ----
-- Just for testing, this has to come from formspec local chosen_enchantment_level = slot_lvl
local chosen_enchantment_level = 30
-- Applying modifiers to the enchantment level -- Applying modifiers to the enchantment level
local enchantability = minetest.get_item_group(tool_def.name, 'enchantability') local enchantability = minetest.get_item_group(tool_def.name, 'enchantability')
-- Generate a random number between 1 and 1+(enchantability/2), with a triangular distribution -- Generate a random number between 1 and 1+(enchantability/2), with a triangular distribution
-- print('-------------------') math.randomseed(self.randomseed)
-- print('enchantability', enchantability)
local rand_enchantability = 1 + math.random(enchantability / 4 + 1) + math.random(enchantability / 4 + 1) local rand_enchantability = 1 + math.random(enchantability / 4 + 1) + math.random(enchantability / 4 + 1)
-- print('rand_enchantability', rand_enchantability)
-- Choose the enchantment level -- Choose the enchantment level
local k = chosen_enchantment_level + rand_enchantability local k = chosen_enchantment_level + rand_enchantability
-- print('k', k)
-- A random bonus, between .85 and 1.15 -- A random bonus, between .85 and 1.15
math.randomseed(self.randomseed)
local rand_bonus_percent = 1 + ((math.random(0, 99) / 100) + (math.random(0, 99) / 100) - 1) * 0.15 local rand_bonus_percent = 1 + ((math.random(0, 99) / 100) + (math.random(0, 99) / 100) - 1) * 0.15
-- print('rand_bonus_percent', rand_bonus_percent)
-- Finally, we calculate the level -- Finally, we calculate the level
local final_level = math.round(k * rand_bonus_percent) local final_level = math.round(k * rand_bonus_percent)
@ -338,11 +419,11 @@ function XEnchanting.get_base_enchantment_level(self, nr_of_bookshelfs, tool_def
final_level = 1 final_level = 1
end end
-- print('final_level', final_level)
---- ----
-- 2 Find possible enchantments -- 2 Find possible enchantments
---- ----
---@type {["id"]: string, ["value"]: number | table, ["level"]: number}[]
local possible_enchantments = {} local possible_enchantments = {}
-- Get level -- Get level
@ -362,36 +443,34 @@ function XEnchanting.get_base_enchantment_level(self, nr_of_bookshelfs, tool_def
local level = levels[#levels] local level = levels[#levels]
-- print('levels', enchantment_name, dump(levels)) if level then
-- print('level', enchantment_name, level)
table.insert(possible_enchantments, { table.insert(possible_enchantments, {
id = enchantment_name, id = enchantment_name,
value = enchantment_def.level_def[level], value = enchantment_def.level_def[level],
level = level level = level
}) })
end end
end
-- print('possible_enchantments', dump(possible_enchantments))
---- ----
-- 3 Select a set of enchantments from the list -- 3 Select a set of enchantments from the list
---- ----
---@type {["id"]: string, ["value"]: number | table, ["level"]: number}[]
local final_enchantments = {} local final_enchantments = {}
local total_weight = 0 local total_weight = 0
-- calculate total weight -- calculate total weight
for i, enchantment in ipairs(possible_enchantments) do for j, enchantment in ipairs(possible_enchantments) do
total_weight = total_weight + self.enchantment_defs[enchantment.id].weight total_weight = total_weight + self.enchantment_defs[enchantment.id].weight
end end
math.randomseed(self.randomseed)
local rand_weight = math.random(0, total_weight / 2) local rand_weight = math.random(0, total_weight / 2)
-- print('total_weight', total_weight)
-- print('rand_weight', rand_weight)
-- select final enchantments -- select final enchantments
for i = 1, #possible_enchantments, 1 do for j = 1, #possible_enchantments, 1 do
math.randomseed(self.randomseed)
local rand_ench_idx = math.random(1, #possible_enchantments) local rand_ench_idx = math.random(1, #possible_enchantments)
local rand_ench = possible_enchantments[rand_ench_idx] local rand_ench = possible_enchantments[rand_ench_idx]
@ -399,12 +478,99 @@ function XEnchanting.get_base_enchantment_level(self, nr_of_bookshelfs, tool_def
rand_weight = rand_weight - self.enchantment_defs[rand_ench.id].weight rand_weight = rand_weight - self.enchantment_defs[rand_ench.id].weight
table.insert(final_enchantments, rand_ench) table.insert(final_enchantments, rand_ench)
-- If `rand_weight` is now negative, select the current enchantment and stop.
if rand_weight < 0 then if rand_weight < 0 then
break break
end end
end end
-- print('final_enchantments', dump(final_enchantments)) local tool_cap_data = self:get_enchanted_tool_capabilities(tool_def, final_enchantments)
self:get_enchanted_tool_capabilities(tool_def, final_enchantments) table.insert(data.slots, i, {
level = slot_lvl,
final_enchantments = final_enchantments,
tool_cap_data = tool_cap_data
})
end
return data
end
---Build form
---@param pos Vector
---@param player_name string
---@param data? table
---@return string
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()
---@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 formspec = {
'size[8,9]',
'label[0, 0;Enchant]',
'list[nodemeta:' .. spos .. ';item;0, 2.5;1, 1;]',
'image[1,2.5;1,1;x_enchanting_trade_slot.png;]',
'list[nodemeta:' .. spos .. ';trade;1, 2.5;1, 1;]',
'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]'
}
if default then
formspec[#formspec + 1] = default.get_hotbar_bg(0, 4.85)
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[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button.png;slot_' .. i .. ';' .. slot.tool_cap_data.enchantments_desc_masked .. ' ' .. minetest.colorize('#FFFF00', 'level: ' .. slot.level) .. ']'
else
---@diagnostic disable-next-line: codestyle-check
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';' .. slot.tool_cap_data.enchantments_desc_masked .. ' ' .. minetest.colorize('#FFFF00', 'level: ' .. slot.level) .. ']'
end
formspec[#formspec + 1] = 'image[2.5,' .. -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 .. ';]'
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[2.5,' .. -0.5 + i .. ';5,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 end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

107
bitbucket-pipelines.yml Normal file
View File

@ -0,0 +1,107 @@
image: atlassian/default-image:3
pipelines:
pull-requests:
"**":
- 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
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:
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
- step:
name: Deploy to ContentDB
caches:
- node-modules
- npm
- nvm
script:
- nvm use v17.2.0
- npm run push:ci -- --token=$CONTENT_DB_X_ENCHATING_TOKEN --title=$BITBUCKET_TAG
definitions:
caches:
node-modules: ./node_modules
npm: ~/.npm
nvm: ~/.nvm

View File

@ -11,12 +11,18 @@ minetest.register_on_mods_loaded(function()
for name, tool_def in pairs(minetest.registered_tools) do for name, tool_def in pairs(minetest.registered_tools) do
if XEnchanting:has_tool_group(name) then if XEnchanting:has_tool_group(name) then
XEnchanting:set_tool_enchantability(tool_def) XEnchanting:set_tool_enchantability(tool_def)
-- print(name, dump(minetest.registered_tools[name]._x_enchanting))
-- print(name, dump(minetest.registered_tools[name].groups))
end end
end end
end) end)
minetest.register_on_joinplayer(function(player, last_login)
XEnchanting.form_context[player:get_player_name()] = nil
end)
minetest.register_on_leaveplayer(function(player, timed_out)
XEnchanting.form_context[player:get_player_name()] = nil
end)
local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000 local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000
print('[Mod] x_enchanting loaded.. [' .. mod_end_time .. 's]') print('[Mod] x_enchanting loaded.. [' .. mod_end_time .. 's]')

View File

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

1388
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

34
package.json Normal file
View File

@ -0,0 +1,34 @@
{
"name": "x_enchanting",
"version": "1.0.0",
"description": "Adds Enchanting Mechanics and API.",
"main": "index.js",
"type": "module",
"directories": {
"doc": "docs"
},
"engines": {
"node": ">=16.0.0",
"npm": ">=8.0.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"push:ci": "node ./scripts/deploy",
"lua-diagnostics": "node ./scripts/lls-check"
},
"repository": {
"type": "git",
"url": "git+https://juraj_vajda@bitbucket.org/minetest_gamers/x_enchanting.git"
},
"author": "SaKeL",
"license": "LGPL-2.1-or-later",
"bugs": {
"url": "https://bitbucket.org/minetest_gamers/x_enchanting/issues"
},
"homepage": "https://bitbucket.org/minetest_gamers/x_enchanting#readme",
"devDependencies": {
"jaguar": "^6.0.1",
"node-fetch": "^3.2.10",
"yargs": "^17.6.1"
}
}

32
scripts/deploy.js Normal file
View File

@ -0,0 +1,32 @@
import fetch from 'node-fetch'
import yargs from 'yargs/yargs'
import {hideBin} from 'yargs/helpers'
const argv = yargs(hideBin(process.argv)).argv
try {
const body = {
method: 'git',
title: argv.title,
ref: 'master'
}
const response = await fetch('https://content.minetest.net/api/packages/SaKeL/x_enchanting/releases/new/', {
method: 'POST',
body: JSON.stringify(body),
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${argv.token}`
}
})
const data = await response.json()
console.log(data)
if (!data.success) {
process.exit(1)
}
} catch (error) {
console.log(error)
process.exit(1)
}

93
scripts/lls-check.js Normal file
View File

@ -0,0 +1,93 @@
import * as path from 'node:path'
import * as fs from 'node:fs'
import {exec} from 'node:child_process'
import yargs from 'yargs/yargs'
import {hideBin} from 'yargs/helpers'
import jaguar from 'jaguar'
const argv = yargs(hideBin(process.argv)).argv
const cwd = process.cwd()
const logPath = path.join(cwd, 'logs')
// Extract lua language server
const from = path.join(cwd, 'bin/lua-language-server-3.5.6-linux-x64.tar.gz');
const to = path.join(cwd, 'bin', 'lua-language-server-3.5.6-linux-x64');
const extract = jaguar.extract(from, to)
// extract.on('file', (name) => {
// console.log(name)
// })
extract.on('start', () => {
console.log('Extracting...')
})
// extract.on('progress', (percent) => {
// console.log(percent + '%')
// })
extract.on('error', (error) => {
console.error(error)
process.exit(1)
})
extract.on('end', () => {
console.log('Extracting: Done')
// Delete directory recursively
try {
fs.rmSync(logPath, { recursive: true, force: true })
console.log(`Removed folder: ${logPath}`)
} catch (err) {
console.error(`Error while deleting ${logPath}.`)
console.error(err)
}
let command = './bin/lua-language-server-3.5.6-linux-x64/bin/lua-language-server'
if (argv.local) {
command = 'lua-language-server'
}
exec(`${command} --logpath "${logPath}" --check "${cwd}"`, (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`)
process.exit(1)
}
if (stderr) {
console.log(`stderr: ${stderr}`)
process.exit(1)
}
console.log(`\n${stdout}`)
if (fs.existsSync('./logs/check.json')) {
const rawdata = fs.readFileSync('./logs/check.json')
const diagnosticsJson = JSON.parse(rawdata)
Object.keys(diagnosticsJson).forEach((key) => {
console.log(key)
diagnosticsJson[key].forEach((errObj) => {
console.log(`line: ${errObj.range.start.line} - ${errObj.message}`)
})
})
console.error('Fix the errors/warnings above.')
process.exit(1)
}
// Delete directory recursively
const llsFolder = path.join(cwd, 'bin', 'lua-language-server-3.5.6-linux-x64')
try {
fs.rmSync(llsFolder, { recursive: true, force: true })
console.log(`Removed folder: ${llsFolder}`)
} catch (err) {
console.error(`Error while deleting ${llsFolder}.`)
console.error(err)
process.exit(1)
}
})
})

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.

278
table.lua
View File

@ -1,33 +1,9 @@
screwdriver = minetest.global_exists('screwdriver') and screwdriver --[[@as MtgScrewdriver]] screwdriver = minetest.global_exists('screwdriver') and screwdriver --[[@as MtgScrewdriver]]
local scroll_animations = { ----
scroll_open = { { x = 1, y = 40 }, 80, 0, false },
scroll_close = { { x = 45, y = 84 }, 80, 0, false },
scroll_open_idle = { { x = 41, y = 42 }, 0, 0, false },
scroll_closed_idle = { { x = 43, y = 43 }, 0, 0, false }
}
local function get_formspec(pos)
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
local formspec =
'size[8,9]' ..
'label[0, 0;Enchant]' ..
---@diagnostic disable-next-line: codestyle-check
'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;' .. scroll_animations.scroll_open_idle[1].x .. ',' .. scroll_animations.scroll_open_idle[1].y .. ';0]' ..
'list[nodemeta:' .. spos .. ';item;0, 2.5;1, 1;]' ..
'image[1,2.5;1,1;x_enchanting_trade_slot.png;]' ..
'list[nodemeta:' .. spos .. ';trade;1, 2.5;1, 1;]' ..
'list[current_player;main;0, 4.85;8, 1;]' ..
'list[current_player;main;0, 6.08;8, 3;8]' ..
'listring[nodemeta:' .. spos .. ';item]' ..
'listring[nodemeta:' .. spos .. ';trade]' ..
'listring[current_player;main]' ..
default.get_hotbar_bg(0, 4.85)
return formspec
end
--- Table Node --- Table Node
----
minetest.register_node('x_enchanting:table', { minetest.register_node('x_enchanting:table', {
description = 'Enchating Table', description = 'Enchating Table',
short_description = 'Enchating Table', short_description = 'Enchating Table',
@ -48,15 +24,15 @@ minetest.register_node('x_enchanting:table', {
}, },
sounds = { sounds = {
footstep = { footstep = {
name = 'x_enchanting_table_hard_footstep', name = 'x_enchanting_scroll',
gain = 0.2 gain = 0.2
}, },
dug = { dug = {
name = 'x_enchanting_table_hard_footstep', name = 'x_enchanting_scroll',
gain = 1.0 gain = 1.0
}, },
place = { place = {
name = 'x_enchanting_table_place_node_hard', name = 'x_enchanting_scroll',
gain = 1.0 gain = 1.0
} }
}, },
@ -105,15 +81,34 @@ minetest.register_node('x_enchanting:table', {
return itemstack return itemstack
end end
minetest.sound_play('default_dig_choppy', { minetest.sound_play('x_enchanting_scroll', {
gain = 0.3, gain = 0.3,
pos = pos, pos = pos,
max_hear_distance = 10 max_hear_distance = 10
}, true) }, true)
local formspec = get_formspec(pos) -- bookshelfs
local bookshelfs = minetest.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' }
)
minetest.show_formspec(clicker:get_player_name(), 'x_enchanting:table', formspec) local inv = minetest.get_meta(pos):get_inventory()
if not inv:is_empty('item') then
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data(#bookshelfs, minetest.registered_tools[item_stack:get_name()])
local formspec = XEnchanting:get_formspec(pos, p_name, data)
minetest.show_formspec(p_name, 'x_enchanting:table', formspec)
else
local formspec = XEnchanting:get_formspec(pos, p_name)
minetest.show_formspec(p_name, 'x_enchanting:table', formspec)
end
return itemstack
end, end,
---@param pos Vector ---@param pos Vector
---@param intensity? number ---@param intensity? number
@ -168,7 +163,7 @@ minetest.register_node('x_enchanting:table', {
end end
local particlespawner_def = { local particlespawner_def = {
amount = 30, amount = 50,
time = 5, time = 5,
minpos = { x = pos.x - 0.1, y = pos.y + 0.2, z = pos.z - 0.1 }, minpos = { x = pos.x - 0.1, y = pos.y + 0.2, z = pos.z - 0.1 },
maxpos = { x = pos.x + 0.1, y = pos.y + 0.3, z = pos.z + 0.1 }, maxpos = { x = pos.x + 0.1, y = pos.y + 0.3, z = pos.z + 0.1 },
@ -187,7 +182,7 @@ minetest.register_node('x_enchanting:table', {
if minetest.has_feature({ dynamic_add_media_table = true, particlespawner_tweenable = true }) then if minetest.has_feature({ dynamic_add_media_table = true, particlespawner_tweenable = true }) then
-- new syntax, after v5.6.0 -- new syntax, after v5.6.0
particlespawner_def = { particlespawner_def = {
amount = 30, amount = 50,
time = 5, time = 5,
size = { size = {
min = 0.1, min = 0.1,
@ -228,9 +223,6 @@ minetest.register_node('x_enchanting:table', {
return true return true
end end
-- just for testing
XEnchanting:get_base_enchantment_level(#bookshelfs, minetest.registered_tools['default:pick_mese'])
-- symbol particles -- symbol particles
for i = 1, 10, 1 do for i = 1, 10, 1 do
local pos_random = bookshelfs[math.random(1, #bookshelfs)] local pos_random = bookshelfs[math.random(1, #bookshelfs)]
@ -268,16 +260,184 @@ minetest.register_node('x_enchanting:table', {
end end
end end
end, 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)
local st_meta = stack:get_meta()
local st_name = stack:get_name()
local is_enchanted = st_meta:get_int('is_enchanted')
-- on_receive_fields = enchanting.fields, if listname == 'item'
-- on_metadata_inventory_put = enchanting.on_put, and minetest.get_item_group(st_name, 'enchantability') > 0
-- on_metadata_inventory_take = enchanting.on_take, and is_enchanted ~= 1
-- allow_metadata_inventory_put = enchanting.put, then
-- allow_metadata_inventory_take = enchanting.take, return stack:get_count()
-- allow_metadata_inventory_move = function() return 0 end, elseif listname == 'trade'
and (
st_name == 'default:mese_crystal'
or minetest.get_item_group(st_name, 'enchanting_trade') > 0
)
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
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local st_name = stack:get_name()
if listname == 'item' 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)
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)
local p_name = player:get_player_name()
local inv = minetest.get_meta(pos):get_inventory()
if not inv:is_empty('item') then
-- bookshelfs
local bookshelfs = minetest.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' }
)
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data(#bookshelfs, minetest.registered_tools[item_stack:get_name()])
local formspec = XEnchanting:get_formspec(pos, p_name, data)
minetest.show_formspec(p_name, 'x_enchanting:table', formspec)
else
local formspec = XEnchanting:get_formspec(pos, p_name)
minetest.show_formspec(p_name, 'x_enchanting:table', formspec)
end
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)
local p_name = player:get_player_name()
local inv = minetest.get_meta(pos):get_inventory()
if not inv:is_empty('item') then
-- bookshelfs
local bookshelfs = minetest.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' }
)
local item_stack = inv:get_stack('item', 1)
local data = XEnchanting:get_enchantment_data(#bookshelfs, minetest.registered_tools[item_stack:get_name()])
local formspec = XEnchanting:get_formspec(pos, p_name, data)
minetest.show_formspec(p_name, 'x_enchanting:table', formspec)
else
local formspec = XEnchanting:get_formspec(pos, p_name)
minetest.show_formspec(p_name, 'x_enchanting:table', formspec)
end
end
}) })
---Scroll Entity -- form receive fields
---@param player ObjectRef
---@param formname string
---@param fields table
minetest.register_on_player_receive_fields(function(player, formname, fields)
local p_name = player:get_player_name()
if formname ~= 'x_enchanting:table' or fields.quit then
XEnchanting.form_context[p_name] = nil
return
end
local selected_slot
if fields.slot_1 and fields.slot_1 ~= '' then
selected_slot = 1
elseif fields.slot_2 and fields.slot_2 ~= '' then
selected_slot = 2
elseif fields.slot_3 and fields.slot_3 ~= '' then
selected_slot = 3
end
if not XEnchanting.form_context[p_name] or not selected_slot then
return
end
local pos = XEnchanting.form_context[p_name].pos
local inv = minetest.get_meta(pos):get_inventory()
if inv:is_empty('trade') or inv:is_empty('item') then
return
end
local trade_stack = inv:get_stack('trade', 1)
local data = XEnchanting.form_context[p_name].data
if trade_stack:get_count() < selected_slot or not data then
return
end
local item_stack = inv:get_stack('item', 1)
local is_enchanted = item_stack:get_meta():get_int('is_enchanted')
if is_enchanted == 1 then
return
end
-- Enchant item
XEnchanting:set_enchanted_tool(
pos,
item_stack,
data.slots[selected_slot].tool_cap_data.tool_capabilities,
data.slots[selected_slot].tool_cap_data.enchantments_desc,
selected_slot,
p_name
)
end)
----
--- Entity (Scroll)
----
minetest.register_entity('x_enchanting:table_scroll', { minetest.register_entity('x_enchanting:table_scroll', {
initial_properties = { initial_properties = {
visual = 'mesh', visual = 'mesh',
@ -374,10 +534,10 @@ minetest.register_entity('x_enchanting:table_scroll', {
-- scroll open/close animation -- scroll open/close animation
if self._player and self._scroll_closed then if self._player and self._scroll_closed then
self._scroll_closed = false self._scroll_closed = false
self.object:set_animation(unpack(scroll_animations.scroll_open)) self.object:set_animation(unpack(XEnchanting.scroll_animations.scroll_open))
elseif not self._player and not self._scroll_closed then elseif not self._player and not self._scroll_closed then
self._scroll_closed = true self._scroll_closed = true
self.object:set_animation(unpack(scroll_animations.scroll_close)) self.object:set_animation(unpack(XEnchanting.scroll_animations.scroll_close))
end end
end end
@ -404,3 +564,27 @@ minetest.register_entity('x_enchanting:table_scroll', {
return true return true
end end
}) })
----
-- Recipe
---
if minetest.get_modpath('xdecor') then
minetest.register_craft({
output = 'x_enchanting:table',
recipe = {
{ 'default:book', '', '' },
{ 'default:diamond', 'default:obsidian', 'default:diamond' },
{ 'default:obsidian', 'default:obsidian', 'default:obsidian' }
}
})
else
minetest.register_craft({
output = 'x_enchanting:table',
recipe = {
{ '', 'default:book', '' },
{ 'default:diamond', 'default:obsidian', 'default:diamond' },
{ 'default:obsidian', 'default:obsidian', 'default:obsidian' }
}
})
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

View File

@ -29,6 +29,7 @@
---@field on_use fun(itemstack: ItemStack, user: ObjectRef|nil, pointed_thing: PointedThingDef): ItemStack|nil default: nil. When user pressed the 'punch/mine' key with the item in hand. Function must return either nil if inventory shall not be modified, or an itemstack to replace the original itemstack. e.g. itemstack:take_item(); return itemstack. Otherwise, the function is free to do what it wants. The user may be any ObjectRef or nil. The default functions handle regular use cases. ---@field on_use fun(itemstack: ItemStack, user: ObjectRef|nil, pointed_thing: PointedThingDef): ItemStack|nil default: nil. When user pressed the 'punch/mine' key with the item in hand. Function must return either nil if inventory shall not be modified, or an itemstack to replace the original itemstack. e.g. itemstack:take_item(); return itemstack. Otherwise, the function is free to do what it wants. The user may be any ObjectRef or nil. The default functions handle regular use cases.
---@field after_use fun(itemstack: ItemStack, user: ObjectRef|nil, node: NodeDef, digparams: DigParamsDef): ItemStack|nil default: nil. If defined, should return an itemstack and will be called instead of wearing out the item (if tool). If returns nil, does nothing. ---@field after_use fun(itemstack: ItemStack, user: ObjectRef|nil, node: NodeDef, digparams: DigParamsDef): ItemStack|nil default: nil. If defined, should return an itemstack and will be called instead of wearing out the item (if tool). If returns nil, does nothing.
---@field soil table Only for farming ---@field soil table Only for farming
---@field name string
---Tool capabilities definition ---Tool capabilities definition
---@class ToolCapabilitiesDef ---@class ToolCapabilitiesDef

View File

@ -1,3 +1,58 @@
---@diagnostic disable: codestyle-check ---@diagnostic disable: codestyle-check
---Base class XBows
---Base class XEnchanting
---@class XEnchanting ---@class XEnchanting
---@field tools_enchantability table<string, number> Add enchantability to default tools. key/value = tool_name/enchantibility value
---@field roman_numbers table<number, string> Convert Arabic numbers to Roman numbers
---@field enchantment_defs table<'sharpness' | 'fortune' | 'unbreaking' | 'efficiency', EnchantmentDef>
---@field has_tool_group fun(self: XEnchanting, name: string): string | boolean Check if tool has one of the known tool groups, returns `false` otherwise.
---@field set_tool_enchantability fun(self: XEnchanting, tool_def: ItemDef): nil Sets `enchantibility` group and values from base table (`XEnchanting.tools_enchantability`) to all registered tools (atching known group names). If tool has already `enchantibility` group defined it will take the defined value insted.
---@field get_enchanted_tool_capabilities fun(self: XEnchanting, tool_def: ItemDef, enchantments: Enchantments[]): GetEnchantedToolCapabilitiesReturn Applies enchantments to item tool capabilities.
---@field set_enchanted_tool fun(self: XEnchanting, pos: Vector, itemstack: ItemStack, capabilities: ToolCapabilitiesDef, description: string, level: number, player_name: string): nil Set choosen enchantment and its modified tool capabilities to itemstack and `item` inventory. This will also get new `randomseed`.
---@field get_enchantment_data fun(self: XEnchanting, nr_of_bookshelfs: number, tool_def: ItemDef): EnchantmentData Algoritm to get aplicable random enchantments.
---@field get_formspec fun(self: XEnchanting, pos, player_name, data): string Builds and returns `formspec` string
---Enchantment definition
---@class EnchantmentDef
---@field name string Readable name of the enchantment
---@field final_level_range table<number, table<number, number[]>> Level range
---@field level_def table<number, number> Level bonus
---@field weight number Enchantment weight
---@field randomseed number Math random seed. Will change after item was enchanted.
---@field form_context table<string, FormContextDef> Additional form data for player.
---@field scroll_animations table<string, table> Parameters for `ObjectRef` `set_animation` method
---Form context
---@class FormContextDef
---@field pos Vector Formspec/node form position
---@field data table Enchantment data
---@class GetEnchantedToolCapabilitiesReturn
---@field tool_capabilities ToolCapabilitiesDef
---@field enchantments_desc string
---@field enchantments_desc_masked string
---@class EnchantmentData
---@field slots EnchantmentDataSlot[]
---@class Enchantments
---@field id string
---@field value number
---@field level number
---@class ToolCapData
---@field enchantments_desc string Masket description before enchating
---@field enchantments_desc_masked string Description added to item description definition after enchanting
---@field tool_capabilities ToolCapabilitiesDef Modified tool capabilities with applied enchantment
---@class EnchantmentDataSlot
---@field final_enchantments Enchantments[]
---@field tool_cap_data ToolCapData
---@field level number