mirror of
https://git.minetest.land/MineClone2/MineClone2.git
synced 2024-12-27 03:37:31 +01:00
363 lines
14 KiB
Lua
363 lines
14 KiB
Lua
local modpath = minetest.get_modpath("mcl_enchanting")
|
|
local S = minetest.get_translator("mcl_enchanting")
|
|
|
|
mcl_enchanting = {
|
|
book_offset = vector.new(0, 0.75, 0),
|
|
book_animations = {["close"] = 1, ["opening"] = 2, ["open"] = 3, ["closing"] = 4},
|
|
book_animation_steps = {0, 640, 680, 700, 740},
|
|
book_animation_loop = {["open"] = true, ["close"] = true},
|
|
book_animation_speed = 40,
|
|
roman_numerals = dofile(modpath .. "/roman_numerals.lua"), -- https://exercism.io/tracks/lua/exercises/roman-numerals/solutions/73c2fb7521e347209312d115f872fa49
|
|
enchantments = {},
|
|
overlay = "^[colorize:purple:50",
|
|
--overlay = "^[invert:rgb^[multiply:#4df44d:50^[invert:rgb",
|
|
enchanting_lists = {"enchanting", "enchanting_item", "enchanting_lapis"},
|
|
bookshelf_positions = {
|
|
{x = -2, y = 0, z = -2}, {x = -2, y = 1, z = -2},
|
|
{x = -1, y = 0, z = -2}, {x = -1, y = 1, z = -2},
|
|
{x = 0, y = 0, z = -2}, {x = 0, y = 1, z = -2},
|
|
{x = 1, y = 0, z = -2}, {x = 1, y = 1, z = -2},
|
|
{x = 2, y = 0, z = -2}, {x = 2, y = 1, z = -2},
|
|
{x = -2, y = 0, z = 2}, {x = -2, y = 1, z = 2},
|
|
{x = -1, y = 0, z = 2}, {x = -1, y = 1, z = 2},
|
|
{x = 0, y = 0, z = 2}, {x = 0, y = 1, z = 2},
|
|
{x = 1, y = 0, z = 2}, {x = 1, y = 1, z = 2},
|
|
{x = 2, y = 0, z = 2}, {x = 2, y = 1, z = 2},
|
|
-- {x = -2, y = 0, z = -2}, {x = -2, y = 1, z = -2},
|
|
{x = -2, y = 0, z = -1}, {x = -2, y = 1, z = -1},
|
|
{x = -2, y = 0, z = 0}, {x = -2, y = 1, z = 0},
|
|
{x = -2, y = 0, z = 1}, {x = -2, y = 1, z = 1},
|
|
-- {x = -2, y = 0, z = 2}, {x = -2, y = 1, z = 2},
|
|
-- {x = 2, y = 0, z = -2}, {x = 2, y = 1, z = -2},
|
|
{x = 2, y = 0, z = -1}, {x = 2, y = 1, z = -1},
|
|
{x = 2, y = 0, z = 0}, {x = 2, y = 1, z = 0},
|
|
{x = 2, y = 0, z = 1}, {x = 2, y = 1, z = 1},
|
|
-- {x = 2, y = 0, z = 2}, {x = 2, y = 1, z = 2},
|
|
},
|
|
air_positions = {
|
|
{x = -1, y = 0, z = -1}, {x = -1, y = 1, z = -1},
|
|
{x = -1, y = 0, z = -1}, {x = -1, y = 1, z = -1},
|
|
{x = 0, y = 0, z = -1}, {x = 0, y = 1, z = -1},
|
|
{x = 1, y = 0, z = -1}, {x = 1, y = 1, z = -1},
|
|
{x = 1, y = 0, z = -1}, {x = 1, y = 1, z = -1},
|
|
{x = -1, y = 0, z = 1}, {x = -1, y = 1, z = 1},
|
|
{x = -1, y = 0, z = 1}, {x = -1, y = 1, z = 1},
|
|
{x = 0, y = 0, z = 1}, {x = 0, y = 1, z = 1},
|
|
{x = 1, y = 0, z = 1}, {x = 1, y = 1, z = 1},
|
|
{x = 1, y = 0, z = 1}, {x = 1, y = 1, z = 1},
|
|
-- {x = -1, y = 0, z = -1}, {x = -1, y = 1, z = -1},
|
|
{x = -1, y = 0, z = -1}, {x = -1, y = 1, z = -1},
|
|
{x = -1, y = 0, z = 0}, {x = -1, y = 1, z = 0},
|
|
{x = -1, y = 0, z = 1}, {x = -1, y = 1, z = 1},
|
|
-- {x = -1, y = 0, z = 1}, {x = -1, y = 1, z = 1},
|
|
-- {x = 1, y = 0, z = -1}, {x = 1, y = 1, z = -1},
|
|
{x = 1, y = 0, z = -1}, {x = 1, y = 1, z = -1},
|
|
{x = 1, y = 0, z = 0}, {x = 1, y = 1, z = 0},
|
|
{x = 1, y = 0, z = 1}, {x = 1, y = 1, z = 1},
|
|
-- {x = 1, y = 0, z = 1}, {x = 1, y = 1, z = 1},
|
|
},
|
|
}
|
|
|
|
dofile(modpath .. "/engine.lua")
|
|
dofile(modpath .. "/enchantments.lua")
|
|
|
|
minetest.register_chatcommand("enchant", {
|
|
description = S("Enchant an item"),
|
|
params = S("<player> <enchantment> [<level>]"),
|
|
privs = {give = true},
|
|
func = function(_, param)
|
|
local sparam = param:split(" ")
|
|
local target_name = sparam[1]
|
|
local enchantment = sparam[2]
|
|
local level_str = sparam[3]
|
|
local level = tonumber(level_str or "1")
|
|
if not target_name or not enchantment then
|
|
return false, S("Usage: /enchant <player> <enchantment> [<level>]")
|
|
end
|
|
local target = minetest.get_player_by_name(target_name)
|
|
if not target then
|
|
return false, S("Player '@1' cannot be found.", target_name)
|
|
end
|
|
local itemstack = target:get_wielded_item()
|
|
local can_enchant, errorstring, extra_info = mcl_enchanting.can_enchant(itemstack, enchantment, level)
|
|
if not can_enchant then
|
|
if errorstring == "enchantment invalid" then
|
|
return false, S("There is no such enchantment '@1'.", enchantment)
|
|
elseif errorstring == "item missing" then
|
|
return false, S("The target doesn't hold an item.")
|
|
elseif errorstring == "item not supported" then
|
|
return false, S("The selected enchantment can't be added to the target item.")
|
|
elseif errorstring == "level invalid" then
|
|
return false, S("'@1' is not a valid number", level_str)
|
|
elseif errorstring == "level too high" then
|
|
return false, S("The number you have entered (@1) is too big, it must be at most @2.", level_str, extra_info)
|
|
elseif errorstring == "level too small" then
|
|
return false, S("The number you have entered (@1) is too small, it must be at least @2.", level_str, extra_info)
|
|
elseif errorstring == "incompatible" then
|
|
return false, S("@1 can't be combined with @2.", mcl_enchanting.get_enchantment_description(enchantment, level), extra_info)
|
|
end
|
|
else
|
|
target:set_wielded_item(mcl_enchanting.enchant(itemstack, enchantment, level))
|
|
return true, S("Enchanting succeded.")
|
|
end
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("forceenchant", {
|
|
description = S("Forcefully enchant an item"),
|
|
params = S("<player> <enchantment> [<level>]"),
|
|
privs = {give = true},
|
|
func = function(_, param)
|
|
local sparam = param:split(" ")
|
|
local target_name = sparam[1]
|
|
local enchantment = sparam[2]
|
|
local level_str = sparam[3]
|
|
local level = tonumber(level_str or "1")
|
|
if not target_name or not enchantment then
|
|
return false, S("Usage: /forceenchant <player> <enchantment> [<level>]")
|
|
end
|
|
local target = minetest.get_player_by_name(target_name)
|
|
if not target then
|
|
return false, S("Player '@1' cannot be found.", target_name)
|
|
end
|
|
local itemstack = target:get_wielded_item()
|
|
local can_enchant, errorstring, extra_info = mcl_enchanting.can_enchant(itemstack, enchantment, level)
|
|
if errorstring == "enchantment invalid" then
|
|
return false, S("There is no such enchantment '@1'.", enchantment)
|
|
elseif errorstring == "item missing" then
|
|
return false, S("The target doesn't hold an item.")
|
|
elseif errorstring == "item not supported" and not mcl_enchanting.is_enchantable(itemstack:get_name()) then
|
|
return false, S("The target item is not enchantable.")
|
|
elseif errorstring == "level invalid" then
|
|
return false, S("'@1' is not a valid number.", level_str)
|
|
else
|
|
target:set_wielded_item(mcl_enchanting.enchant(itemstack, enchantment, level))
|
|
return true, S("Enchanting succeded.")
|
|
end
|
|
end
|
|
})
|
|
|
|
minetest.register_craftitem("mcl_enchanting:book_enchanted", {
|
|
description = S("Enchanted Book"),
|
|
inventory_image = "mcl_enchanting_book_enchanted.png" .. mcl_enchanting.overlay,
|
|
groups = {enchanted = 1, not_in_creative_inventory = 1, enchantability = 1},
|
|
_mcl_enchanting_enchanted_tool = "mcl_enchanting:book_enchanted",
|
|
stack_max = 1,
|
|
})
|
|
|
|
minetest.register_alias("mcl_books:book_enchanted", "mcl_enchanting:book_enchanted")
|
|
|
|
local spawn_book_entity = function(pos, respawn)
|
|
if respawn then
|
|
-- Check if we already have a book
|
|
local objs = minetest.get_objects_inside_radius(pos, 1)
|
|
for o=1, #objs do
|
|
local obj = objs[o]
|
|
local lua = obj:get_luaentity()
|
|
if lua and lua.name == "mcl_enchanting:book" then
|
|
if lua._table_pos and vector.equals(pos, lua._table_pos) then
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local obj = minetest.add_entity(vector.add(pos, mcl_enchanting.book_offset), "mcl_enchanting:book")
|
|
if obj then
|
|
local lua = obj:get_luaentity()
|
|
if lua then
|
|
lua._table_pos = table.copy(pos)
|
|
end
|
|
end
|
|
end
|
|
|
|
minetest.register_entity("mcl_enchanting:book", {
|
|
initial_properties = {
|
|
visual = "mesh",
|
|
mesh = "mcl_enchanting_book.b3d",
|
|
visual_size = {x = 12.5, y = 12.5},
|
|
collisionbox = {0, 0, 0},
|
|
pointable = false,
|
|
physical = false,
|
|
textures = {"mcl_enchanting_book_entity.png"},
|
|
static_save = false,
|
|
},
|
|
_player_near = false,
|
|
_table_pos = nil,
|
|
on_activate = function(self, staticdata)
|
|
self.object:set_armor_groups({immortal = 1})
|
|
mcl_enchanting.set_book_animation(self, "close")
|
|
end,
|
|
on_step = function(self, dtime)
|
|
local old_player_near = self._player_near
|
|
local player_near = false
|
|
local player
|
|
for _, obj in ipairs(minetest.get_objects_inside_radius(vector.subtract(self.object:get_pos(), mcl_enchanting.book_offset), 2.5)) do
|
|
if obj:is_player() then
|
|
player_near = true
|
|
player = obj
|
|
end
|
|
end
|
|
if player_near and not old_player_near then
|
|
mcl_enchanting.set_book_animation(self, "opening")
|
|
mcl_enchanting.schedule_book_animation(self, "open")
|
|
elseif old_player_near and not player_near then
|
|
mcl_enchanting.set_book_animation(self, "closing")
|
|
mcl_enchanting.schedule_book_animation(self, "close")
|
|
end
|
|
if player then
|
|
mcl_enchanting.look_at(self, player:get_pos())
|
|
end
|
|
self._player_near = player_near
|
|
mcl_enchanting.check_animation_schedule(self, dtime)
|
|
end,
|
|
})
|
|
|
|
local rotate
|
|
if minetest.get_modpath("screwdriver") then
|
|
rotate = screwdriver.rotate_simple
|
|
end
|
|
|
|
minetest.register_node("mcl_enchanting:table", {
|
|
description = S("Enchanting Table"),
|
|
_tt_help = S("Spend experience, and lapis to enchant various items."),
|
|
_doc_items_longdesc = S("Enchanting Tables will let you enchant armors, tools, weapons, and books with various abilities. But, at the cost of some experience, and lapis lazuli."),
|
|
_doc_items_usagehelp =
|
|
S("Rightclick the Enchanting Table to open the enchanting menu.").."\n"..
|
|
S("Place a tool, armor, weapon or book into the top left slot, and then place 1-3 Lapis Lazuli in the slot to the right.").."\n".."\n"..
|
|
S("After placing your items in the slots, the enchanting options will be shown. Hover over the options to read what is available to you.").."\n"..
|
|
S("These options are randomized, and dependent on experience level; but the enchantment strength can be increased.").."\n".."\n"..
|
|
S("To increase the enchantment strength, place bookshelves around the enchanting table. However, you will need to keep 1 air node between the table, & the bookshelves to empower the enchanting table.").."\n".."\n"..
|
|
S("After finally selecting your enchantment; left-click on the selection, and you will see both the lapis lazuli and your experience levels consumed. And, an enchanted item left in its place."),
|
|
_doc_items_hidden = false,
|
|
drawtype = "nodebox",
|
|
tiles = {"mcl_enchanting_table_top.png", "mcl_enchanting_table_bottom.png", "mcl_enchanting_table_side.png", "mcl_enchanting_table_side.png", "mcl_enchanting_table_side.png", "mcl_enchanting_table_side.png"},
|
|
node_box = {
|
|
type = "fixed",
|
|
fixed = {-0.5, -0.5, -0.5, 0.5, 0.25, 0.5},
|
|
},
|
|
sounds = mcl_sounds.node_sound_stone_defaults(),
|
|
groups = {pickaxey = 2, deco_block = 1},
|
|
on_rotate = rotate,
|
|
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
|
local player_meta = clicker:get_meta()
|
|
local table_meta = minetest.get_meta(pos)
|
|
local num_bookshelves = table_meta:get_int("mcl_enchanting:num_bookshelves")
|
|
local table_name = table_meta:get_string("name")
|
|
if table_name == "" then
|
|
table_name = S("Enchant")
|
|
end
|
|
local bookshelves = mcl_enchanting.get_bookshelves(pos)
|
|
player_meta:set_int("mcl_enchanting:num_bookshelves", math.min(15, #bookshelves))
|
|
player_meta:set_string("mcl_enchanting:table_name", table_name)
|
|
mcl_enchanting.show_enchanting_formspec(clicker)
|
|
-- Respawn book entity just in case it got lost
|
|
spawn_book_entity(pos, true)
|
|
end,
|
|
on_construct = function(pos)
|
|
spawn_book_entity(pos)
|
|
end,
|
|
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
|
local dname = (digger and digger:get_player_name()) or ""
|
|
if minetest.is_creative_enabled(dname) then
|
|
return
|
|
end
|
|
local itemstack = ItemStack("mcl_enchanting:table")
|
|
local meta = minetest.get_meta(pos)
|
|
local itemmeta = itemstack:get_meta()
|
|
itemmeta:set_string("name", meta:get_string("name"))
|
|
itemmeta:set_string("description", meta:get_string("description"))
|
|
minetest.add_item(pos, itemstack)
|
|
end,
|
|
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
|
local meta = minetest.get_meta(pos)
|
|
local itemmeta = itemstack:get_meta()
|
|
meta:set_string("name", itemmeta:get_string("name"))
|
|
meta:set_string("description", itemmeta:get_string("description"))
|
|
end,
|
|
after_destruct = function(pos)
|
|
local objs = minetest.get_objects_inside_radius(pos, 1)
|
|
for o=1, #objs do
|
|
local obj = objs[o]
|
|
local lua = obj:get_luaentity()
|
|
if lua and lua.name == "mcl_enchanting:book" then
|
|
if lua._table_pos and vector.equals(pos, lua._table_pos) then
|
|
obj:remove()
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
drop = "",
|
|
_mcl_blast_resistance = 1200,
|
|
_mcl_hardness = 5,
|
|
})
|
|
|
|
minetest.register_craft({
|
|
output = "mcl_enchanting:table",
|
|
recipe = {
|
|
{"", "mcl_books:book", ""},
|
|
{"mcl_core:diamond", "mcl_core:obsidian", "mcl_core:diamond"},
|
|
{"mcl_core:obsidian", "mcl_core:obsidian", "mcl_core:obsidian"}
|
|
}
|
|
})
|
|
|
|
minetest.register_abm({
|
|
label = "Enchanting table bookshelf particles",
|
|
interval = 1,
|
|
chance = 1,
|
|
nodenames = "mcl_enchanting:table",
|
|
action = function(pos)
|
|
local playernames = {}
|
|
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 15)) do
|
|
if obj:is_player() then
|
|
table.insert(playernames, obj:get_player_name())
|
|
end
|
|
end
|
|
if #playernames < 1 then
|
|
return
|
|
end
|
|
local absolute, relative = mcl_enchanting.get_bookshelves(pos)
|
|
for i, ap in ipairs(absolute) do
|
|
if math.random(5) == 1 then
|
|
local rp = relative[i]
|
|
local t = math.random()+1 --time
|
|
local d = {x = rp.x, y=rp.y-0.7, z=rp.z} --distance
|
|
local v = {x = -math.random()*d.x, y = math.random(), z = -math.random()*d.z} --velocity
|
|
local a = {x = 2*(-v.x*t - d.x)/t/t, y = 2*(-v.y*t - d.y)/t/t, z = 2*(-v.z*t - d.z)/t/t} --acceleration
|
|
local s = math.random()+0.9 --size
|
|
t = t - 0.1 --slightly decrease time to avoid texture overlappings
|
|
local tx = "mcl_enchanting_glyph_" .. math.random(18) .. ".png"
|
|
for _, name in pairs(playernames) do
|
|
minetest.add_particle({
|
|
pos = ap,
|
|
velocity = v,
|
|
acceleration = a,
|
|
expirationtime = t,
|
|
size = s,
|
|
texture = tx,
|
|
collisiondetection = false,
|
|
playername = name
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
})
|
|
|
|
minetest.register_lbm({
|
|
label = "(Re-)spawn book entity above enchanting table",
|
|
name = "mcl_enchanting:spawn_book_entity",
|
|
nodenames = {"mcl_enchanting:table"},
|
|
run_at_every_load = true,
|
|
action = function(pos)
|
|
spawn_book_entity(pos)
|
|
end,
|
|
})
|
|
|
|
|
|
minetest.register_on_mods_loaded(mcl_enchanting.initialize)
|
|
minetest.register_on_joinplayer(mcl_enchanting.initialize_player)
|
|
minetest.register_on_player_receive_fields(mcl_enchanting.handle_formspec_fields)
|
|
minetest.register_allow_player_inventory_action(mcl_enchanting.allow_inventory_action)
|
|
minetest.register_on_player_inventory_action(mcl_enchanting.on_inventory_action)
|
|
table.insert(tt.registered_snippets, 1, mcl_enchanting.enchantments_snippet)
|