forked from mtcontrib/x_enchanting
615 lines
19 KiB
Lua
615 lines
19 KiB
Lua
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:table', {
|
|
description = S('Enchanting Table'),
|
|
short_description = S('Enchanting Table'),
|
|
drawtype = 'mesh',
|
|
mesh = 'x_enchanting_table.obj',
|
|
tiles = { 'x_enchanting_table.png' },
|
|
paramtype = 'light',
|
|
paramtype2 = 'facedir',
|
|
walkable = true,
|
|
wield_scale = { x = 2, y = 2, z = 2 },
|
|
selection_box = {
|
|
type = 'fixed',
|
|
fixed = { -1 / 2, -1 / 2, -1 / 2, 1 / 2, 1 / 2 - 4 / 16, 1 / 2 }
|
|
},
|
|
collision_box = {
|
|
type = 'fixed',
|
|
fixed = { -1 / 2, -1 / 2, -1 / 2, 1 / 2, 1 / 2 - 4 / 16, 1 / 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 = { cracky = 1, level = 2 },
|
|
stack_max = 1,
|
|
mod_origin = 'x_enchanting',
|
|
light_source = 6,
|
|
---@param pos Vector
|
|
on_construct = function(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
|
|
meta:set_string('infotext', S('Enchanting Table'))
|
|
meta:set_string('owner', '')
|
|
inv:set_size('item', 1)
|
|
inv:set_size('trade', 1)
|
|
minetest.add_entity({ x = pos.x, y = pos.y + 0.7, z = pos.z }, 'x_enchanting:table_scroll')
|
|
minetest.get_node_timer(pos):start(5)
|
|
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()
|
|
|
|
meta:set_string('owner', player_name)
|
|
meta:set_string('infotext', S('Enchanting Table') .. ' (' .. S('owned by') .. ' ' .. player_name .. ')')
|
|
|
|
local formspec = XEnchanting:get_formspec(pos, player_name)
|
|
meta:set_string('formspec', formspec)
|
|
end,
|
|
---@param pos Vector
|
|
---@param node NodeDef
|
|
---@param clicker ObjectRef
|
|
---@param itemstack ItemStack
|
|
---@param pointed_thing? PointedThingDef
|
|
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
|
local meta = minetest.get_meta(pos)
|
|
local p_name = clicker:get_player_name()
|
|
|
|
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)
|
|
|
|
-- 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' }
|
|
)
|
|
|
|
local inv = minetest.get_meta(pos):get_inventory()
|
|
|
|
if not inv:is_empty('item') and inv:get_stack('item', 1):get_meta():get_int('is_enchanted') ~= 1 then
|
|
local item_stack = inv:get_stack('item', 1)
|
|
local data = XEnchanting:get_enchantment_data(
|
|
clicker,
|
|
#bookshelfs,
|
|
minetest.registered_tools[item_stack:get_name()]
|
|
)
|
|
local formspec = XEnchanting:get_formspec(pos, p_name, data)
|
|
|
|
meta:set_string('formspec', formspec)
|
|
else
|
|
local formspec = XEnchanting:get_formspec(pos, p_name)
|
|
|
|
meta:set_string('formspec', formspec)
|
|
end
|
|
|
|
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_trade = inv:get_stack('trade', 1)
|
|
|
|
if not stack_item:is_empty() then
|
|
drops[#drops + 1] = stack_item:to_table()
|
|
end
|
|
|
|
if not stack_trade:is_empty() then
|
|
drops[#drops + 1] = stack_trade:to_table()
|
|
end
|
|
|
|
drops[#drops + 1] = 'x_enchanting: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('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 elapsed number
|
|
on_timer = function(pos, elapsed)
|
|
-- entity
|
|
local table_scroll = minetest.get_objects_inside_radius(pos, 0.9)
|
|
|
|
if #table_scroll == 0 then
|
|
minetest.add_entity({ x = pos.x, y = pos.y + 0.7, z = pos.z }, 'x_enchanting:table_scroll')
|
|
end
|
|
|
|
local particlespawner_def = {
|
|
amount = 50,
|
|
time = 5,
|
|
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 },
|
|
minvel = { x = -0.1, y = 0.1, z = -0.1 },
|
|
maxvel = { x = 0.1, y = 0.2, z = 0.1 },
|
|
minacc = { x = -0.1, y = 0.1, z = -0.1 },
|
|
maxacc = { x = 0.1, y = 0.2, z = 0.1 },
|
|
minexptime = 1.5,
|
|
maxexptime = 2.5,
|
|
minsize = 0.1,
|
|
maxsize = 0.3,
|
|
texture = 'x_enchanting_scroll_particle.png',
|
|
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 = 5,
|
|
size = {
|
|
min = 0.1,
|
|
max = 0.3,
|
|
},
|
|
exptime = 2,
|
|
pos = {
|
|
min = vector.new({ x = pos.x - 0.5, y = pos.y, z = pos.z - 0.5 }),
|
|
max = vector.new({ x = pos.x + 0.5, y = pos.y, z = pos.z + 0.5 }),
|
|
},
|
|
attract = {
|
|
kind = 'point',
|
|
strength = 0.5,
|
|
origin = vector.new({ x = pos.x, y = pos.y + 0.65, z = pos.z })
|
|
},
|
|
texture = {
|
|
name = 'x_enchanting_scroll_particle.png',
|
|
alpha_tween = {
|
|
0, 1,
|
|
style = 'fwd',
|
|
reps = 1
|
|
}
|
|
},
|
|
glow = 1
|
|
}
|
|
end
|
|
|
|
minetest.add_particlespawner(particlespawner_def)
|
|
|
|
---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' }
|
|
)
|
|
|
|
if #bookshelfs == 0 then
|
|
return true
|
|
end
|
|
|
|
-- symbol particles
|
|
for i = 1, 10, 1 do
|
|
local pos_random = bookshelfs[math.random(1, #bookshelfs)]
|
|
local x = pos.x - pos_random.x
|
|
local y = pos_random.y - pos.y
|
|
local z = pos.z - pos_random.z
|
|
local rand1 = (math.random(150, 250) / 100) * -1
|
|
local rand2 = math.random(10, 500) / 100
|
|
local rand3 = math.random(50, 200) / 100
|
|
|
|
minetest.after(rand2, function()
|
|
minetest.add_particle({
|
|
pos = pos_random,
|
|
velocity = { x = x, y = 2 - y, z = z },
|
|
acceleration = { x = 0, y = rand1, z = 0 },
|
|
expirationtime = 1,
|
|
size = rand3,
|
|
texture = 'x_enchanting_symbol_' .. math.random(1, 26) .. '.png',
|
|
glow = 6
|
|
})
|
|
end)
|
|
end
|
|
|
|
return true
|
|
end,
|
|
---@param pos Vector
|
|
on_destruct = function(pos)
|
|
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 0.9)) do
|
|
if obj
|
|
and obj:get_luaentity()
|
|
and obj:get_luaentity().name == 'x_enchanting:table_scroll'
|
|
then
|
|
obj:remove()
|
|
break
|
|
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')
|
|
|
|
if listname == 'item'
|
|
and minetest.get_item_group(st_name, 'enchantability') > 0
|
|
and is_enchanted ~= 1
|
|
then
|
|
return stack:get_count()
|
|
elseif listname == 'trade'
|
|
and (
|
|
st_name == 'default:mese_crystal'
|
|
or minetest.get_item_group(st_name, 'enchanting_trade') > 0
|
|
)
|
|
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 meta = minetest.get_meta(pos)
|
|
local p_name = player:get_player_name()
|
|
local inv = meta: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', '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()]
|
|
)
|
|
local formspec = XEnchanting:get_formspec(pos, p_name, data)
|
|
|
|
meta:set_string('formspec', formspec)
|
|
else
|
|
local formspec = XEnchanting:get_formspec(pos, p_name)
|
|
|
|
meta:set_string('formspec', 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 meta = minetest.get_meta(pos)
|
|
local p_name = player:get_player_name()
|
|
local inv = meta: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', '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()]
|
|
)
|
|
local formspec = XEnchanting:get_formspec(pos, p_name, data)
|
|
|
|
meta:set_string('formspec', formspec)
|
|
else
|
|
local formspec = XEnchanting:get_formspec(pos, p_name)
|
|
|
|
meta:set_string('formspec', formspec)
|
|
end
|
|
end,
|
|
-- form receive fields
|
|
---@param pos Vector
|
|
---@param formname string
|
|
---@param fields table
|
|
---@param sender ObjectRef
|
|
on_receive_fields = function(pos, formname, fields, sender)
|
|
local p_name = sender:get_player_name()
|
|
|
|
if 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 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)
|
|
|
|
if trade_stack:get_count() < selected_slot 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,
|
|
selected_slot,
|
|
p_name
|
|
)
|
|
end
|
|
})
|
|
|
|
----
|
|
--- Entity (Scroll)
|
|
----
|
|
|
|
minetest.register_entity('x_enchanting:table_scroll', {
|
|
initial_properties = {
|
|
visual = 'mesh',
|
|
mesh = 'x_enchanting_scroll.b3d',
|
|
textures = {
|
|
--- back
|
|
'x_enchanting_scroll_mesh.png',
|
|
-- handles
|
|
'x_enchanting_scroll_handles_mesh.png',
|
|
--- front
|
|
'x_enchanting_scroll_mesh.png',
|
|
},
|
|
collisionbox = { 0, 0, 0, 0, 0, 0 },
|
|
selectionbox = { 0, 0, 0, 0, 0, 0 },
|
|
physical = false,
|
|
hp_max = 1,
|
|
visual_size = { x = 1, y = 1, z = 1 },
|
|
glow = 1,
|
|
pointable = false,
|
|
infotext = S('Scroll of Enchantments'),
|
|
},
|
|
---@param self table
|
|
---@param killer ObjectRef
|
|
on_death = function(self, killer)
|
|
self.object:remove()
|
|
end,
|
|
---@param self table
|
|
---@param staticdata StringAbstract
|
|
---@param dtime_s number
|
|
on_activate = function(self, staticdata, dtime_s)
|
|
self._scroll_closed = true
|
|
self._tablechecktimer = 5
|
|
self._playerchecktimer = 1
|
|
self._player = nil
|
|
self._last_rotation = nil
|
|
|
|
self.object:set_armor_groups({ immortal = 1 })
|
|
self.object:set_animation({ x = 0, y = 0 }, 0, 0, false)
|
|
end,
|
|
---@param self table
|
|
---@param dtime number
|
|
---@param moveresult? table
|
|
on_step = function(self, dtime, moveresult)
|
|
local pos = self.object:get_pos()
|
|
|
|
self._last_rotation = self.object:get_rotation()
|
|
self._tablechecktimer = self._tablechecktimer - dtime
|
|
self._playerchecktimer = self._playerchecktimer - dtime
|
|
|
|
-- table
|
|
if self._tablechecktimer <= 0 then
|
|
self._tablechecktimer = 5
|
|
local node = minetest.get_node({ x = pos.x, y = pos.y - 0.7, z = pos.z })
|
|
|
|
if node.name ~= 'x_enchanting:table' then
|
|
-- remove entity when no table under it
|
|
self.object:remove()
|
|
end
|
|
end
|
|
|
|
-- player
|
|
if self._playerchecktimer <= 0 then
|
|
self._playerchecktimer = 1
|
|
local objects = minetest.get_objects_inside_radius(pos, 5)
|
|
|
|
-- inital value
|
|
local shortest_distance = 10
|
|
local found_player = false
|
|
|
|
if #objects > 0 then
|
|
for i, obj in ipairs(objects) do
|
|
if obj:is_player() and obj:get_pos() then
|
|
-- player
|
|
found_player = true
|
|
local distance = vector.distance(pos, obj:get_pos())
|
|
|
|
if distance < shortest_distance then
|
|
self._player = obj
|
|
end
|
|
end
|
|
end
|
|
else
|
|
self._player = nil
|
|
end
|
|
|
|
if not found_player then
|
|
self._player = nil
|
|
end
|
|
|
|
-- scroll open/close animation
|
|
if self._player and self._scroll_closed then
|
|
self._scroll_closed = false
|
|
self.object:set_animation(unpack(XEnchanting.scroll_animations.scroll_open))
|
|
|
|
minetest.sound_play('x_enchanting_scroll', {
|
|
gain = 0.3,
|
|
pos = pos,
|
|
max_hear_distance = 10
|
|
}, true)
|
|
elseif not self._player and not self._scroll_closed then
|
|
self._scroll_closed = true
|
|
self.object:set_animation(unpack(XEnchanting.scroll_animations.scroll_close))
|
|
|
|
minetest.sound_play('x_enchanting_scroll', {
|
|
gain = 0.3,
|
|
pos = pos,
|
|
max_hear_distance = 10
|
|
}, true)
|
|
end
|
|
end
|
|
|
|
-- rotation
|
|
if self._player and self._player:get_pos() then
|
|
local direction = vector.direction(pos, self._player:get_pos())
|
|
self.object:set_yaw(minetest.dir_to_yaw(direction))
|
|
else
|
|
self.object:set_rotation({
|
|
x = self._last_rotation.x,
|
|
y = self._last_rotation.y + (math.pi / -50),
|
|
z = self._last_rotation.z
|
|
})
|
|
end
|
|
end,
|
|
---@param self table
|
|
---@param puncher ObjectRef
|
|
---@param time_from_last_punch number | nil
|
|
---@param tool_capabilities ToolCapabilitiesDef | nil
|
|
---@param dir Vector
|
|
---@param damage number
|
|
---@return boolean | nil
|
|
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
|
|
return true
|
|
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
|