Merge pull request 'Formspec Refactoring' (#2635) from AFCMS/MineClone2:formspec-v4 into master

Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/2635
Reviewed-by: 𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 <mrrar@noreply.git.minetest.land>
Reviewed-by: ancientmarinerdev <ancientmariner_dev@proton.me>
AFCMS 2023-08-26 07:27:25 +00:00
commit f008fa3323
47 changed files with 2413 additions and 1397 deletions

@ -1,3 +1,5 @@
---@diagnostic disable
unused_args = false
allow_defined_top = true
max_line_length = false

22
.luarc.json Normal file

@ -0,0 +1,22 @@
{
"runtime.version": "LuaJIT",
"diagnostics": { "disable": ["lowercase-global"] },
"diagnostics.globals": [
"minetest",
"dump",
"dump2",
"Raycast",
"Settings",
"PseudoRandom",
"PerlinNoise",
"VoxelManip",
"SecureRandom",
"VoxelArea",
"PerlinNoiseMap",
"PcgRandom",
"ItemStack",
"AreaStore",
"vector"
],
"workspace.ignoreDir": [".luacheckrc"]
}

@ -147,13 +147,13 @@ function mcl_experience.throw_xp(pos, total_xp)
obj:set_velocity(vector.new(
math.random(-2, 2) * math.random(),
math.random( 2, 5),
math.random(2, 5),
math.random(-2, 2) * math.random()
))
i = i + xp
j = j + 1
table.insert(obs,obj)
table.insert(obs, obj)
end
return obs
end
@ -179,18 +179,18 @@ function mcl_experience.setup_hud(player)
if not minetest.is_creative_enabled(player:get_player_name()) then
hud_bars[player] = player:hud_add({
hud_elem_type = "image",
position = {x = 0.5, y = 1},
offset = {x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5)},
scale = {x = 0.35, y = 0.375},
alignment = {x = 1, y = 1},
position = { x = 0.5, y = 1 },
offset = { x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5) },
scale = { x = 0.35, y = 0.375 },
alignment = { x = 1, y = 1 },
z_index = 11,
})
hud_levels[player] = player:hud_add({
hud_elem_type = "text",
position = {x = 0.5, y = 1},
position = { x = 0.5, y = 1 },
number = 0x80FF20,
offset = {x = 0, y = -(48 + 24 + 24)},
offset = { x = 0, y = -(48 + 24 + 24) },
z_index = 12,
})
end
@ -221,7 +221,7 @@ function mcl_experience.update(player)
end
function mcl_experience.register_on_add_xp(func, priority)
table.insert(mcl_experience.on_add_xp, {func = func, priority = priority or 0})
table.insert(mcl_experience.on_add_xp, { func = func, priority = priority or 0 })
end
-- callbacks
@ -247,3 +247,12 @@ end)
minetest.register_on_mods_loaded(function()
table.sort(mcl_experience.on_add_xp, function(a, b) return a.priority < b.priority end)
end)
mcl_gamemode.register_on_gamemode_change(function(player, old_gamemode, new_gamemode)
if new_gamemode == "survival" then
mcl_experience.setup_hud(player)
mcl_experience.update(player)
elseif new_gamemode == "creative" then
mcl_experience.remove_hud(player)
end
end)

@ -1,3 +1,4 @@
name = mcl_experience
author = oilboi
description = eXPerience mod
depends = mcl_gamemode

@ -0,0 +1,40 @@
# MineClone2 Formspec API
## `mcl_formspec.label_color`
Contains the color used for formspec labels, currently `#313131`.
## `mcl_formspec.get_itemslot_bg(x, y, w, h)`
Get the background of inventory slots (formspec version = 1)
ex:
```lua
local formspec = table.concat({
mcl_formspec.get_itemslot_bg(0, 0, 5, 2),
"list[current_player;super_inventory;0,0;5,2;]",
})
```
## `mcl_formspec.get_itemslot_bg_v4(x, y, w, h, size, texture)`
Get the background of inventory slots (formspec version > 1)
Works basically the same as `mcl_formspec.get_itemslot_bg(x, y, w, h)` but have more customisation options:
- `size`: allow you to customize the size of the slot borders, default is 0.05
- `texture`: allow you to specify a custom texture tu use instead of the default one
ex:
```lua
local formspec = table.concat({
mcl_formspec.get_itemslot_bg_v4(0.375, 0.375, 5, 2, 0.1, "super_slot_background.png"),
"list[current_player;super_inventory;0.375,0.375;5,2;]",
})
```
## `mcl_formspec.itemslot_border_size`
Contains the default item slot border size used by `mcl_formspec.get_itemslot_bg_v4`, currently 0.05

@ -0,0 +1,21 @@
# MineClone2 Formspec Guide
**_This guide will teach you the rules for creating formspecs for the MineClone2 game._**
Formspecs are an important part of game and mod development.
First of all, MineClone2 aims to support ONLY last formspec version. Many utility functions will not work with formspec v1 or v2.
The typical width of an 9 slots width inventory formspec is `0.375 + 9 + ((9-1) * 0.25) + 0.375 = 11.75`
Margins are 0.375.
The labels color is `mcl_formspec.label_color`
Space between 1st inventory line and the rest of inventory is 0.45
Labels should have 0.375 space above if there is no other stuff above and 0.45 between content
- 0.375 under
According to minetest modding book, table.concat is faster than string concatenation, so this method should be prefered (the code is also more clear)

@ -1,10 +1,55 @@
mcl_formspec = {}
mcl_formspec.label_color = "#313131"
---Get the background of inventory slots (formspec version = 1)
---@param x number
---@param y number
---@param w number
---@param h number
---@return string
function mcl_formspec.get_itemslot_bg(x, y, w, h)
local out = ""
for i = 0, w - 1, 1 do
for j = 0, h - 1, 1 do
out = out .."image["..x+i..","..y+j..";1,1;mcl_formspec_itemslot.png]"
out = out .. "image[" .. x + i .. "," .. y + j .. ";1,1;mcl_formspec_itemslot.png]"
end
end
return out
end
---This function will replace mcl_formspec.get_itemslot_bg then every formspec will be upgrade to version 4
---@param x number
---@param y number
---@param size number
---@param texture? string
---@return string
---@nodiscard
local function get_slot(x, y, size, texture)
local t = "image[" .. x - size .. "," .. y - size .. ";" .. 1 + (size * 2) ..
"," .. 1 + (size * 2) .. ";" .. (texture and texture or "mcl_formspec_itemslot.png") .. "]"
return t
end
mcl_formspec.itemslot_border_size = 0.05
---Get the background of inventory slots (formspec version > 1)
---@param x number
---@param y number
---@param w integer
---@param h integer
---@param size? number Optional size of the slot border (default: 0.05)
---@param texture? string Optional texture to replace the default one
---@return string
---@nodiscard
function mcl_formspec.get_itemslot_bg_v4(x, y, w, h, size, texture)
if not size then
size = mcl_formspec.itemslot_border_size
end
local out = ""
for i = 0, w - 1, 1 do
for j = 0, h - 1, 1 do
out = out .. get_slot(x + i + (i * 0.25), y + j + (j * 0.25), size, texture)
end
end
return out

@ -0,0 +1,35 @@
# `mcl_inventory`
## `mcl_inventory.register_survival_inventory_tab(def)`
```lua
mcl_inventory.register_survival_inventory_tab({
-- Page identifier
-- Used to uniquely identify the tab
id = "test",
-- The tab description, can be translated
description = "Test",
-- The name of the item that will be used as icon
item_icon = "mcl_core:stone",
-- If true, the main inventory will be shown at the bottom of the tab
-- Listrings need to be added by hand
show_inventory = true,
-- This function must return the tab's formspec for the player
build = function(player)
return "label[1,1;Hello hello]button[2,2;2,2;Hello;hey]"
end,
-- This function will be called in the on_player_receive_fields callback if the tab is currently open
handle = function(player, fields)
print(dump(fields))
end,
-- This function will be called to know if a player can see the tab
-- Returns true by default
access = function(player)
end,
```

@ -3,19 +3,35 @@ local F = minetest.formspec_escape
local C = minetest.colorize
-- Prepare player info table
---@type table<string, {page: string, filter: string, start_i: integer, inv_size: integer}>
local players = {}
-- Containing all the items for each Creative Mode tab
---@type table<string, string[]>
local inventory_lists = {}
--local mod_player = minetest.get_modpath("mcl_player")
-- Create tables
local builtin_filter_ids = {"blocks","deco","redstone","rail","food","tools","combat","mobs","brew","matr","misc","all"}
---@type string[]
local builtin_filter_ids = {
"blocks",
"deco",
"redstone",
"rail",
"food",
"tools",
"combat",
"mobs",
"brew",
"matr",
"misc",
"all"
}
for _, f in pairs(builtin_filter_ids) do
inventory_lists[f] = {}
end
---@param tbl string[]
local function replace_enchanted_books(tbl)
for k, item in ipairs(tbl) do
if item:find("mcl_enchanting:book_enchanted") == 1 then
@ -28,20 +44,32 @@ local function replace_enchanted_books(tbl)
end
end
--[[ Populate all the item tables. We only do this once. Note this code must be
executed after loading all the other mods in order to work. ]]
-- Populate all the item tables. We only do this once.
-- Note this code must be executed after loading all the other mods in order to work.
minetest.register_on_mods_loaded(function()
for name,def in pairs(minetest.registered_items) do
if (not def.groups.not_in_creative_inventory or def.groups.not_in_creative_inventory == 0) and def.description and def.description ~= "" then
for name, def in pairs(minetest.registered_items) do
if (not def.groups.not_in_creative_inventory or def.groups.not_in_creative_inventory == 0) and def.description and
def.description ~= "" then
---@param def mt.ItemDef|mt.NodeDef
local function is_redstone(def)
return def.mesecons or def.groups.mesecon or def.groups.mesecon_conductor_craftable or def.groups.mesecon_effecor_off
return def.mesecons or def.groups.mesecon or def.groups.mesecon_conductor_craftable or
def.groups.mesecon_effecor_off
end
---@param def mt.ItemDef|mt.NodeDef
local function is_tool(def)
return def.groups.tool or (def.tool_capabilities and def.tool_capabilities.damage_groups == nil)
end
---@param def mt.ItemDef|mt.NodeDef
local function is_weapon_or_armor(def)
return def.groups.weapon or def.groups.weapon_ranged or def.groups.ammo or def.groups.combat_item or ((def.groups.armor_head or def.groups.armor_torso or def.groups.armor_legs or def.groups.armor_feet or def.groups.horse_armor) and def.groups.non_combat_armor ~= 1)
return def.groups.weapon or def.groups.weapon_ranged or def.groups.ammo or def.groups.combat_item or
(
(
def.groups.armor_head or def.groups.armor_torso or def.groups.armor_legs or def.groups.armor_feet or
def.groups.horse_armor) and def.groups.non_combat_armor ~= 1)
end
-- Is set to true if it was added in any category besides misc
local nonmisc = false
if def.groups.building_block then
@ -110,6 +138,11 @@ minetest.register_on_mods_loaded(function()
end
end)
---@param name string
---@param description string
---@param lang mt.LangCode
---@param filter string
---@return integer
local function filter_item(name, description, lang, filter)
local desc
if not lang then
@ -120,13 +153,16 @@ local function filter_item(name, description, lang, filter)
return string.find(name, filter, nil, true) or string.find(desc, filter, nil, true)
end
---@param filter string
---@param player mt.PlayerObjectRef
local function set_inv_search(filter, player)
local playername = player:get_player_name()
local inv = minetest.get_inventory({type="detached", name="creative_"..playername})
local inv = minetest.get_inventory({ type = "detached", name = "creative_" .. playername })
local creative_list = {}
local lang = minetest.get_player_information(playername).lang_code
for name,def in pairs(minetest.registered_items) do
if (not def.groups.not_in_creative_inventory or def.groups.not_in_creative_inventory == 0) and def.description and def.description ~= "" then
for name, def in pairs(minetest.registered_items) do
if (not def.groups.not_in_creative_inventory or def.groups.not_in_creative_inventory == 0) and def.description and
def.description ~= "" then
if filter_item(string.lower(def.name), def.description, lang, filter) then
table.insert(creative_list, name)
end
@ -147,9 +183,11 @@ local function set_inv_search(filter, player)
inv:set_list("main", creative_list)
end
---@param page string
---@param player mt.PlayerObjectRef
local function set_inv_page(page, player)
local playername = player:get_player_name()
local inv = minetest.get_inventory({type="detached", name="creative_"..playername})
local inv = minetest.get_inventory({ type = "detached", name = "creative_" .. playername })
inv:set_size("main", 0)
local creative_list = {}
if inventory_lists[page] then -- Standard filter
@ -160,9 +198,11 @@ local function set_inv_page(page, player)
inv:set_list("main", creative_list)
end
---@param player mt.PlayerObjectRef
local function init(player)
local playername = player:get_player_name()
minetest.create_detached_inventory("creative_"..playername, {
minetest.create_detached_inventory("creative_" .. playername, {
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
if minetest.is_creative_enabled(playername) then
return count
@ -197,24 +237,45 @@ local trash = minetest.create_detached_inventory("trash", {
inv:set_stack(listname, index, "")
end,
})
trash:set_size("main", 1)
local noffset = {} -- numeric tab offset
local offset = {} -- string offset:
local boffset = {} --
local hoch = {}
local filtername = {}
--local bg = {}
------------------------------
-- Formspec Precalculations --
------------------------------
local noffset_x_start = -0.24
-- Numeric position of tab background image, indexed by tab name
---@type table<string, {[0]: number, [1]: number}>
local noffset = {}
-- String position of tab button background image, indexed by tab name
---@type table<string, string>
local offset = {}
-- String position of tab button, indexed by tab name
---@type table<string, string>
local boffset = {}
-- Used to determine the tab button background image
---@type table<string, ""|"_down">
local button_bg_postfix = {}
-- Tab caption/tooltip translated string, indexed by tab name
---@type table<string, string>
local filtername = {}
local noffset_x_start = 0.2
local noffset_x = noffset_x_start
local noffset_y = -0.25
local noffset_y = -1.34
---@param id string
---@param right? boolean
local function next_noffset(id, right)
if right then
noffset[id] = { 8.94, noffset_y }
noffset[id] = { 11.3, noffset_y }
else
noffset[id] = { noffset_x, noffset_y }
noffset_x = noffset_x + 1.25
noffset_x = noffset_x + 1.6
end
end
@ -228,7 +289,7 @@ next_noffset("misc")
next_noffset("nix", true)
noffset_x = noffset_x_start
noffset_y = 8.12
noffset_y = 8.64
-- Lower row
next_noffset("food")
@ -238,25 +299,25 @@ next_noffset("mobs")
next_noffset("matr")
next_noffset("inv", true)
for k,v in pairs(noffset) do
for k, v in pairs(noffset) do
offset[k] = tostring(v[1]) .. "," .. tostring(v[2])
boffset[k] = tostring(v[1]+0.19) .. "," .. tostring(v[2]+0.25)
boffset[k] = tostring(v[1] + 0.24) .. "," .. tostring(v[2] + 0.25)
end
hoch["blocks"] = ""
hoch["deco"] = ""
hoch["redstone"] = ""
hoch["rail"] = ""
hoch["brew"] = ""
hoch["misc"] = ""
hoch["nix"] = ""
hoch["default"] = ""
hoch["food"] = "_down"
hoch["tools"] = "_down"
hoch["combat"] = "_down"
hoch["mobs"] = "_down"
hoch["matr"] = "_down"
hoch["inv"] = "_down"
button_bg_postfix["blocks"] = ""
button_bg_postfix["deco"] = ""
button_bg_postfix["redstone"] = ""
button_bg_postfix["rail"] = ""
button_bg_postfix["brew"] = ""
button_bg_postfix["misc"] = ""
button_bg_postfix["nix"] = ""
button_bg_postfix["default"] = ""
button_bg_postfix["food"] = "_down"
button_bg_postfix["tools"] = "_down"
button_bg_postfix["combat"] = "_down"
button_bg_postfix["mobs"] = "_down"
button_bg_postfix["matr"] = "_down"
button_bg_postfix["inv"] = "_down"
filtername["blocks"] = S("Building Blocks")
filtername["deco"] = S("Decoration Blocks")
@ -291,121 +352,9 @@ filtername["inv"] = S("Survival Inventory")
bg["default"] = dark_bg
end]]
local function get_stack_size(player)
return player:get_meta():get_int("mcl_inventory:switch_stack")
end
local function set_stack_size(player, n)
player:get_meta():set_int("mcl_inventory:switch_stack", n)
end
minetest.register_on_joinplayer(function (player)
if get_stack_size(player) == 0 then
set_stack_size(player, 64)
end
end)
function mcl_inventory.set_creative_formspec(player)
local playername = player:get_player_name()
if not players[playername] then return end
local start_i = players[playername].start_i
local pagenum = start_i / (9*5) + 1
local name = players[playername].page
local inv_size = players[playername].inv_size
local filter = players[playername].filter
local pagemax = math.max(1, math.floor((inv_size-1) / (9*5) + 1))
local main_list
local listrings = "listring[detached:creative_"..playername..";main]"..
"listring[current_player;main]"..
"listring[detached:trash;main]"
local inv_bg = "crafting_inventory_creative.png"
if name == "inv" then
inv_bg = "crafting_inventory_creative_survival.png"
-- Background images for armor slots (hide if occupied)
local armor_slot_imgs = ""
local inv = player:get_inventory()
if inv:get_stack("armor", 2):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[2.5,1.3;1,1;mcl_inventory_empty_armor_slot_helmet.png]"
end
if inv:get_stack("armor", 3):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[2.5,2.75;1,1;mcl_inventory_empty_armor_slot_chestplate.png]"
end
if inv:get_stack("armor", 4):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[5.5,1.3;1,1;mcl_inventory_empty_armor_slot_leggings.png]"
end
if inv:get_stack("armor", 5):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[5.5,2.75;1,1;mcl_inventory_empty_armor_slot_boots.png]"
end
if inv:get_stack("offhand", 1):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[1.5,2.025;1,1;mcl_inventory_empty_armor_slot_shield.png]"
end
local stack_size = get_stack_size(player)
-- Survival inventory slots
main_list = "list[current_player;main;0,3.75;9,3;9]" ..
mcl_formspec.get_itemslot_bg(0, 3.75, 9, 3) ..
-- Armor
"list[current_player;armor;2.5,1.3;1,1;1]" ..
"list[current_player;armor;2.5,2.75;1,1;2]" ..
"list[current_player;armor;5.5,1.3;1,1;3]" ..
"list[current_player;armor;5.5,2.75;1,1;4]" ..
mcl_formspec.get_itemslot_bg(2.5, 1.3, 1, 1) ..
mcl_formspec.get_itemslot_bg(2.5, 2.75, 1, 1) ..
mcl_formspec.get_itemslot_bg(5.5, 1.3, 1, 1) ..
mcl_formspec.get_itemslot_bg(5.5, 2.75, 1, 1) ..
"list[current_player;offhand;1.5,2.025;1,1]" ..
mcl_formspec.get_itemslot_bg(1.5, 2.025, 1, 1) ..
armor_slot_imgs ..
-- Player preview
mcl_player.get_player_formspec_model(player, 3.9, 1.4, 1.2333, 2.4666, "") ..
-- Crafting guide button
"image_button[9,1;1,1;craftguide_book.png;__mcl_craftguide;]" ..
"tooltip[__mcl_craftguide;"..F(S("Recipe book")) .. "]" ..
-- Help button
"image_button[9,2;1,1;doc_button_icon_lores.png;__mcl_doc;]" ..
"tooltip[__mcl_doc;" .. F(S("Help")) .. "]" ..
-- Achievements button
"image_button[9,3;1,1;mcl_achievements_button.png;__mcl_achievements;]" ..
--"style_type[image_button;border=;bgimg=;bgimg_pressed=]" ..
"tooltip[__mcl_achievements;"..F(S("Advancements")) .. "]" ..
-- Switch stack size button
"image_button[9,4;1,1;default_apple.png;__switch_stack;]" ..
"label[9.4,4.4;" .. F(C("#FFFFFF", stack_size ~= 1 and stack_size or "")) .. "]" ..
"tooltip[__switch_stack;" .. F(S("Switch stack size")) .. "]"
-- Skins button
if minetest.global_exists("mcl_skins") then
main_list = main_list ..
"image_button[9,5;1,1;mcl_skins_button.png;__mcl_skins;]" ..
"tooltip[__mcl_skins;"..F(S("Select player skin")) .. "]"
end
-- For shortcuts
listrings = listrings ..
"listring[detached:"..playername.."_armor;armor]"..
"listring[current_player;main]"
else
-- Creative inventory slots
main_list = "list[detached:creative_"..playername..";main;0,1.75;9,5;"..tostring(start_i).."]"..
mcl_formspec.get_itemslot_bg(0,1.75,9,5)..
-- Page buttons
"label[9.0,5.5;"..F(S("@1/@2", pagenum, pagemax)).."]"..
"image_button[9.0,6.0;0.7,0.7;crafting_creative_prev.png;creative_prev;]"..
"image_button[9.5,6.0;0.7,0.7;crafting_creative_next.png;creative_next;]"
end
local tab_icon = {
-- Item name representing a tab, indexed by tab name
---@type table<string, string>
local tab_icon = {
blocks = "mcl_core:brick_block",
deco = "mcl_flowers:peony",
redstone = "mesecons:redstone",
@ -419,69 +368,236 @@ function mcl_inventory.set_creative_formspec(player)
brew = "mcl_potions:dragon_breath",
matr = "mcl_core:stick",
inv = "mcl_chests:chest",
}
}
-- Get the player configured stack size when taking items from creative inventory
---@param player mt.PlayerObjectRef
---@return integer
local function get_stack_size(player)
return player:get_meta():get_int("mcl_inventory:switch_stack")
end
-- Set the player configured stack size when taking items from creative inventory
---@param player mt.PlayerObjectRef
---@param n integer
local function set_stack_size(player, n)
player:get_meta():set_int("mcl_inventory:switch_stack", n)
end
minetest.register_on_joinplayer(function(player)
if get_stack_size(player) == 0 then
set_stack_size(player, 64)
end
end)
---@param player mt.PlayerObjectRef
function mcl_inventory.set_creative_formspec(player)
local playername = player:get_player_name()
if not players[playername] then return end
local start_i = players[playername].start_i
local pagenum = start_i / (9 * 5) + 1
local page = players[playername].page
local inv_size = players[playername].inv_size
local filter = players[playername].filter
if not inv_size then
if page == "nix" then
local inv = minetest.get_inventory({ type = "detached", name = "creative_" .. playername })
inv_size = inv:get_size("main")
elseif page and page ~= "inv" then
inv_size = #(inventory_lists[page])
else
inv_size = 0
end
end
local pagemax = math.max(1, math.floor((inv_size - 1) / (9 * 5) + 1))
local name = "nix"
local main_list
local listrings = table.concat({
"listring[detached:creative_" .. playername .. ";main]",
"listring[current_player;main]",
"listring[detached:trash;main]",
})
if page then
name = page
if players[playername] then
players[playername].page = page
end
end
if name == "inv" then
-- Background images for armor slots (hide if occupied)
local armor_slot_imgs = ""
local inv = player:get_inventory()
if inv:get_stack("armor", 2):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[3.5,0.375;1,1;mcl_inventory_empty_armor_slot_helmet.png]"
end
if inv:get_stack("armor", 3):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[3.5,2.125;1,1;mcl_inventory_empty_armor_slot_chestplate.png]"
end
if inv:get_stack("armor", 4):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[7.25,0.375;1,1;mcl_inventory_empty_armor_slot_leggings.png]"
end
if inv:get_stack("armor", 5):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[7.25,2.125;1,1;mcl_inventory_empty_armor_slot_boots.png]"
end
if inv:get_stack("offhand", 1):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[2.25,1.25;1,1;mcl_inventory_empty_armor_slot_shield.png]"
end
local stack_size = get_stack_size(player)
-- Survival inventory slots
main_list = table.concat({
mcl_formspec.get_itemslot_bg_v4(0.375, 3.375, 9, 3),
"list[current_player;main;0.375,3.375;9,3;9]",
-- Armor
mcl_formspec.get_itemslot_bg_v4(3.5, 0.375, 1, 1),
mcl_formspec.get_itemslot_bg_v4(3.5, 2.125, 1, 1),
mcl_formspec.get_itemslot_bg_v4(7.25, 0.375, 1, 1),
mcl_formspec.get_itemslot_bg_v4(7.25, 2.125, 1, 1),
"list[current_player;armor;3.5,0.375;1,1;1]",
"list[current_player;armor;3.5,2.125;1,1;2]",
"list[current_player;armor;7.25,0.375;1,1;3]",
"list[current_player;armor;7.25,2.125;1,1;4]",
-- Offhand
mcl_formspec.get_itemslot_bg_v4(2.25, 1.25, 1, 1),
"list[current_player;offhand;2.25,1.25;1,1]",
armor_slot_imgs,
-- Player preview
"image[4.75,0.33;2.25,2.83;mcl_inventory_background9.png;2]",
mcl_player.get_player_formspec_model(player, 4.75, 0.45, 2.25, 2.75, ""),
-- Crafting guide button
"image_button[11.575,0.825;1.1,1.1;craftguide_book.png;__mcl_craftguide;]",
"tooltip[__mcl_craftguide;" .. F(S("Recipe book")) .. "]",
-- Help button
"image_button[11.575,2.075;1.1,1.1;doc_button_icon_lores.png;__mcl_doc;]",
"tooltip[__mcl_doc;" .. F(S("Help")) .. "]",
-- Advancements button
"image_button[11.575,3.325;1.1,1.1;mcl_achievements_button.png;__mcl_achievements;]",
--"style_type[image_button;border=;bgimg=;bgimg_pressed=]",
"tooltip[__mcl_achievements;" .. F(S("Advancements")) .. "]",
-- Switch stack size button
"image_button[11.575,4.575;1.1,1.1;default_apple.png;__switch_stack;]",
"label[12.275,5.35;" .. F(C("#FFFFFF", tostring(stack_size ~= 1 and stack_size or ""))) .. "]",
"tooltip[__switch_stack;" .. F(S("Switch stack size")) .. "]",
-- Skins button
"image_button[11.575,5.825;1.1,1.1;mcl_skins_button.png;__mcl_skins;]",
"tooltip[__mcl_skins;" .. F(S("Select player skin")) .. "]",
})
-- For shortcuts
listrings = listrings ..
"listring[detached:" .. playername .. "_armor;armor]" ..
"listring[current_player;main]"
else
--local nb_lines = math.ceil(inv_size / 9)
-- Creative inventory slots
main_list = table.concat({
mcl_formspec.get_itemslot_bg_v4(0.375, 0.875, 9, 5),
-- Basic code to replace buttons by scrollbar
-- Require Minetest 5.8
--
--"scroll_container[0.375,0.875;11.575,6;scroll;vertical;1.25]",
--"list[detached:creative_" .. playername .. ";main;0,0;9," .. nb_lines .. ";]",
--"scroll_container_end[]",
--"scrollbaroptions[min=0;max=" .. math.max(nb_lines - 5, 0) .. ";smallstep=1;largesteps=1;arrows=hide]",
--"scrollbar[11.75,0.825;0.75,6.1;vertical;scroll;0]",
"list[detached:creative_" .. playername .. ";main;0.375,0.875;9,5;" .. tostring(start_i) .. "]",
-- Page buttons
"label[11.65,4.33;" .. F(S("@1 / @2", pagenum, pagemax)) .. "]",
"image_button[11.575,4.58;1.1,1.1;crafting_creative_prev.png^[transformR270;creative_prev;]",
"image_button[11.575,5.83;1.1,1.1;crafting_creative_next.png^[transformR270;creative_next;]",
})
end
---@param current_tab string
---@param this_tab string
---@return string
local function tab(current_tab, this_tab)
local bg_img
if current_tab == this_tab then
bg_img = "crafting_creative_active"..hoch[this_tab]..".png"
bg_img = "crafting_creative_active" .. button_bg_postfix[this_tab] .. ".png"
else
bg_img = "crafting_creative_inactive"..hoch[this_tab]..".png"
bg_img = "crafting_creative_inactive" .. button_bg_postfix[this_tab] .. ".png"
end
return
"style["..this_tab..";border=false;bgimg=;bgimg_pressed=]"..
"item_image_button[" .. boffset[this_tab] ..";1,1;"..tab_icon[this_tab]..";"..this_tab..";]"..
"image[" .. offset[this_tab] .. ";1.5,1.44;" .. bg_img .. "]"
return table.concat({
"style[" .. this_tab .. ";border=false;bgimg=;bgimg_pressed=;noclip=true]",
"image[" .. offset[this_tab] .. ";1.5,1.44;" .. bg_img .. "]",
"item_image_button[" .. boffset[this_tab] .. ";1,1;" .. tab_icon[this_tab] .. ";" .. this_tab .. ";]",
"tooltip[blocks;" .. F(filtername[this_tab]) .. "]"
})
end
local caption = ""
if name ~= "inv" and filtername[name] then
caption = "label[0,1.2;"..F(minetest.colorize("#313131", filtername[name])).."]"
caption = "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, filtername[name])) .. "]"
end
local formspec = "size[10,9.3]"..
"no_prepend[]"..
mcl_vars.gui_nonbg..mcl_vars.gui_bg_color..
"background[-0.19,-0.25;10.5,9.87;"..inv_bg.."]"..
"label[-5,-5;"..name.."]"..
tab(name, "blocks") ..
"tooltip[blocks;"..F(filtername["blocks"]).."]"..
tab(name, "deco") ..
"tooltip[deco;"..F(filtername["deco"]).."]"..
tab(name, "redstone") ..
"tooltip[redstone;"..F(filtername["redstone"]).."]"..
tab(name, "rail") ..
"tooltip[rail;"..F(filtername["rail"]).."]"..
tab(name, "misc") ..
"tooltip[misc;"..F(filtername["misc"]).."]"..
tab(name, "nix") ..
"tooltip[nix;"..F(filtername["nix"]).."]"..
caption..
"list[current_player;main;0,7;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7,9,1)..
main_list..
tab(name, "food") ..
"tooltip[food;"..F(filtername["food"]).."]"..
tab(name, "tools") ..
"tooltip[tools;"..F(filtername["tools"]).."]"..
tab(name, "combat") ..
"tooltip[combat;"..F(filtername["combat"]).."]"..
tab(name, "mobs") ..
"tooltip[mobs;"..F(filtername["mobs"]).."]"..
tab(name, "brew") ..
"tooltip[brew;"..F(filtername["brew"]).."]"..
tab(name, "matr") ..
"tooltip[matr;"..F(filtername["matr"]).."]"..
tab(name, "inv") ..
"tooltip[inv;"..F(filtername["inv"]).."]"..
"list[detached:trash;main;9,7;1,1;]"..
mcl_formspec.get_itemslot_bg(9,7,1,1)..
"image[9,7;1,1;crafting_creative_trash.png]"..
listrings
local formspec = table.concat({
"formspec_version[6]",
"size[13,8.75]",
"style_type[image;noclip=true]",
-- Hotbar
mcl_formspec.get_itemslot_bg_v4(0.375, 7.375, 9, 1),
"list[current_player;main;0.375,7.375;9,1;]",
-- Trash
mcl_formspec.get_itemslot_bg_v4(11.625, 7.375, 1, 1, nil, "crafting_creative_trash.png"),
"list[detached:trash;main;11.625,7.375;1,1;]",
main_list,
caption,
listrings,
tab(name, "blocks"),
tab(name, "deco"),
tab(name, "redstone"),
tab(name, "rail"),
tab(name, "misc"),
tab(name, "nix"),
tab(name, "food"),
tab(name, "tools"),
tab(name, "combat"),
tab(name, "mobs"),
tab(name, "brew"),
tab(name, "matr"),
tab(name, "inv"),
})
if name == "nix" then
formspec = formspec .. "field[5.3,1.34;4,0.75;search;;"..minetest.formspec_escape(filter).."]"
formspec = formspec .. "field_close_on_enter[search;false]"
if filter == nil then
filter = ""
end
if pagenum then formspec = formspec .. "p"..tostring(pagenum) end
formspec = formspec .. table.concat({
"field[5.325,0.15;6.1,0.6;search;;" .. minetest.formspec_escape(filter) .. "]",
"field_close_on_enter[search;false]",
"set_focus[search;true]",
})
end
if pagenum then formspec = formspec .. "p" .. tostring(pagenum) end
player:set_inventory_formspec(formspec)
end
@ -500,50 +616,50 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.blocks then
if players[name].page == "blocks" then return end
set_inv_page("blocks",player)
set_inv_page("blocks", player)
page = "blocks"
elseif fields.deco then
if players[name].page == "deco" then return end
set_inv_page("deco",player)
set_inv_page("deco", player)
page = "deco"
elseif fields.redstone then
if players[name].page == "redstone" then return end
set_inv_page("redstone",player)
set_inv_page("redstone", player)
page = "redstone"
elseif fields.rail then
if players[name].page == "rail" then return end
set_inv_page("rail",player)
set_inv_page("rail", player)
page = "rail"
elseif fields.misc then
if players[name].page == "misc" then return end
set_inv_page("misc",player)
set_inv_page("misc", player)
page = "misc"
elseif fields.nix then
set_inv_page("all",player)
set_inv_page("all", player)
page = "nix"
elseif fields.food then
if players[name].page == "food" then return end
set_inv_page("food",player)
set_inv_page("food", player)
page = "food"
elseif fields.tools then
if players[name].page == "tools" then return end
set_inv_page("tools",player)
set_inv_page("tools", player)
page = "tools"
elseif fields.combat then
if players[name].page == "combat" then return end
set_inv_page("combat",player)
set_inv_page("combat", player)
page = "combat"
elseif fields.mobs then
if players[name].page == "mobs" then return end
set_inv_page("mobs",player)
set_inv_page("mobs", player)
page = "mobs"
elseif fields.brew then
if players[name].page == "brew" then return end
set_inv_page("brew",player)
set_inv_page("brew", player)
page = "brew"
elseif fields.matr then
if players[name].page == "matr" then return end
set_inv_page("matr",player)
set_inv_page("matr", player)
page = "matr"
elseif fields.inv then
if players[name].page == "inv" then return end
@ -552,7 +668,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
set_inv_page("all", player)
page = "nix"
elseif fields.search and not fields.creative_next and not fields.creative_prev then
set_inv_search(string.lower(fields.search),player)
set_inv_search(string.lower(fields.search), player)
page = "nix"
elseif fields.__switch_stack then
local switch = 1
@ -570,20 +686,20 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local start_i = players[name].start_i
if fields.creative_prev then
start_i = start_i - 9*5
start_i = start_i - 9 * 5
elseif fields.creative_next then
start_i = start_i + 9*5
start_i = start_i + 9 * 5
else
-- Reset scroll bar if not scrolled
start_i = 0
end
if start_i < 0 then
start_i = start_i + 9*5
start_i = start_i + 9 * 5
end
local inv_size
if page == "nix" then
local inv = minetest.get_inventory({type="detached", name="creative_"..name})
local inv = minetest.get_inventory({ type = "detached", name = "creative_" .. name })
inv_size = inv:get_size("main")
elseif page and page ~= "inv" then
inv_size = #(inventory_lists[page])
@ -593,7 +709,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
players[name].inv_size = inv_size
if start_i >= inv_size then
start_i = start_i - 9*5
start_i = start_i - 9 * 5
end
if start_i < 0 or start_i >= inv_size then
start_i = 0
@ -609,11 +725,34 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
mcl_inventory.set_creative_formspec(player)
end)
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack)
return placer and placer:is_player() and minetest.is_creative_enabled(placer:get_player_name())
end)
if minetest.is_creative_enabled("") then
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack)
-- Place infinite nodes, except for shulker boxes
local group = minetest.get_item_group(itemstack:get_name(), "shulker_box")
return group == 0 or group == nil
end)
function minetest.handle_node_drops(pos, drops, digger)
if not digger or not digger:is_player() then
for _, item in ipairs(drops) do
minetest.add_item(pos, item)
end
end
local inv = digger:get_inventory()
if inv then
for _, item in ipairs(drops) do
if not inv:contains_item("main", item, true) then
inv:add_item("main", item)
end
end
end
end
end
minetest.register_on_joinplayer(function(player)
-- Initialize variables and inventory
local name = player:get_player_name()
@ -629,7 +768,8 @@ minetest.register_on_joinplayer(function(player)
end)
minetest.register_on_player_inventory_action(function(player, action, inventory, inventory_info)
if minetest.is_creative_enabled(player:get_player_name()) and get_stack_size(player) == 64 and action == "put" and inventory_info.listname == "main" then
if minetest.is_creative_enabled(player:get_player_name()) and get_stack_size(player) == 64 and action == "put" and
inventory_info.listname == "main" then
local stack = inventory_info.stack
stack:set_count(stack:get_stack_max())
player:get_inventory():set_stack("main", inventory_info.index, stack)

@ -1,13 +1,17 @@
local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape
mcl_inventory = {}
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/creative.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/survival.lua")
--local mod_player = minetest.get_modpath("mcl_player")
--local mod_craftguide = minetest.get_modpath("mcl_craftguide")
-- Returns a single itemstack in the given inventory to the main inventory, or drop it when there's no space left
function return_item(itemstack, dropper, pos, inv)
---Returns a single itemstack in the given inventory to the main inventory, or drop it when there's no space left.
---@param itemstack mt.ItemStack
---@param dropper mt.ObjectRef
---@param pos mt.Vector
---@param inv mt.InvRef
local function return_item(itemstack, dropper, pos, inv)
if dropper:is_player() then
-- Return to main inventory
if inv:room_for_item("main", itemstack) then
@ -15,14 +19,14 @@ function return_item(itemstack, dropper, pos, inv)
else
-- Drop item on the ground
local v = dropper:get_look_dir()
local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
p.x = p.x+(math.random(1,3)*0.2)
p.z = p.z+(math.random(1,3)*0.2)
local p = vector.offset(pos, 0, 1.2, 0)
p.x = p.x + (math.random(1, 3) * 0.2)
p.z = p.z + (math.random(1, 3) * 0.2)
local obj = minetest.add_item(p, itemstack)
if obj then
v.x = v.x*4
v.y = v.y*4 + 2
v.z = v.z*4
v.x = v.x * 4
v.y = v.y * 4 + 2
v.z = v.z * 4
obj:set_velocity(v)
obj:get_luaentity()._insta_collect = false
end
@ -34,111 +38,53 @@ function return_item(itemstack, dropper, pos, inv)
return itemstack
end
-- Return items in the given inventory list (name) to the main inventory, or drop them if there is no space left
function return_fields(player, name)
---Return items in the given inventory list (name) to the main inventory, or drop them if there is no space left.
---@param player mt.PlayerObjectRef
---@param name string
local function return_fields(player, name)
local inv = player:get_inventory()
local list = inv:get_list(name)
if not list then return end
for i,stack in ipairs(list) do
for i, stack in ipairs(list) do
return_item(stack, player, player:get_pos(), inv)
stack:clear()
inv:set_stack(name, i, stack)
end
end
local function set_inventory(player)
---@param player mt.PlayerObjectRef
---@param armor_change_only? boolean
local function set_inventory(player, armor_change_only)
if minetest.is_creative_enabled(player:get_player_name()) then
mcl_inventory.set_creative_formspec(player)
if armor_change_only then
-- Stay on survival inventory plage if only the armor has been changed
mcl_inventory.set_creative_formspec(player, 0, 0, nil, nil, "inv")
else
mcl_inventory.set_creative_formspec(player, 0, 1)
end
return
end
local inv = player:get_inventory()
inv:set_width("craft", 2)
inv:set_size("craft", 4)
local armor_slots = {"helmet", "chestplate", "leggings", "boots"}
local armor_slot_imgs = ""
for a=1,4 do
if inv:get_stack("armor", a+1):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[0,"..(a-1)..";1,1;mcl_inventory_empty_armor_slot_"..armor_slots[a]..".png]"
end
end
if inv:get_stack("offhand", 1):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[3,2;1,1;mcl_inventory_empty_armor_slot_shield.png]"
end
local form = "size[9,8.75]" ..
"background[-0.19,-0.25;9.41,9.49;crafting_formspec_bg.png]" ..
mcl_player.get_player_formspec_model(player, 1.0, 0.0, 2.25, 4.5, "") ..
-- Armor
"list[current_player;armor;0,0;1,1;1]" ..
"list[current_player;armor;0,1;1,1;2]" ..
"list[current_player;armor;0,2;1,1;3]" ..
"list[current_player;armor;0,3;1,1;4]" ..
mcl_formspec.get_itemslot_bg(0,0,1,1) ..
mcl_formspec.get_itemslot_bg(0,1,1,1) ..
mcl_formspec.get_itemslot_bg(0,2,1,1) ..
mcl_formspec.get_itemslot_bg(0,3,1,1) ..
"list[current_player;offhand;3,2;1,1]" ..
mcl_formspec.get_itemslot_bg(3,2,1,1) ..
armor_slot_imgs ..
-- Craft and inventory
"label[0,4;"..F(minetest.colorize("#313131", S("Inventory"))) .. "]" ..
"list[current_player;main;0,4.5;9,3;9]" ..
"list[current_player;main;0,7.74;9,1;]" ..
"label[4,0.5;"..F(minetest.colorize("#313131", S("Crafting"))) .. "]" ..
"list[current_player;craft;4,1;2,2]" ..
"list[current_player;craftpreview;7,1.5;1,1;]" ..
mcl_formspec.get_itemslot_bg(0, 4.5, 9, 3) ..
mcl_formspec.get_itemslot_bg(0, 7.74, 9, 1) ..
mcl_formspec.get_itemslot_bg(4, 1,2, 2) ..
mcl_formspec.get_itemslot_bg(7, 1.5, 1, 1) ..
-- Crafting guide button
"image_button[4.5,3;1,1;craftguide_book.png;__mcl_craftguide;]" ..
"tooltip[__mcl_craftguide;"..F(S("Recipe book")) .. "]" ..
-- Help button
"image_button[8,3;1,1;doc_button_icon_lores.png;__mcl_doc;]" ..
"tooltip[__mcl_doc;" .. F(S("Help")) .. "]"
-- Skins button
if minetest.global_exists("mcl_skins") then
form = form ..
"image_button[3,3;1,1;mcl_skins_button.png;__mcl_skins;]" ..
"tooltip[__mcl_skins;" .. F(S("Select player skin")) .. "]"
end
form = form ..
-- Achievements button
"image_button[7,3;1,1;mcl_achievements_button.png;__mcl_achievements;]" ..
"tooltip[__mcl_achievements;" .. F(S("Advancements")) .. "]" ..
-- For shortcuts
"listring[current_player;main]" ..
"listring[current_player;armor]" ..
"listring[current_player;main]" ..
"listring[current_player;craft]" ..
"listring[current_player;main]"
player:set_inventory_formspec(form)
player:set_inventory_formspec(mcl_inventory.build_survival_formspec(player))
end
-- Drop items in craft grid and reset inventory on closing
minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.quit then
return_fields(player,"craft")
return_fields(player,"enchanting_lapis")
return_fields(player,"enchanting_item")
return_fields(player, "craft")
return_fields(player, "enchanting_lapis")
return_fields(player, "enchanting_item")
if not minetest.is_creative_enabled(player:get_player_name()) and (formname == "" or formname == "main") then
set_inventory(player)
end
end
end)
mcl_inventory.update_inventory_formspec = set_inventory
function mcl_inventory.update_inventory_formspec(player)
set_inventory(player)
end
-- Drop crafting grid items on leaving
minetest.register_on_leaveplayer(function(player)
@ -150,6 +96,7 @@ end)
minetest.register_on_joinplayer(function(player)
--init inventory
local inv = player:get_inventory()
inv:set_width("main", 9)
inv:set_size("main", 36)
inv:set_size("offhand", 1)
@ -174,73 +121,18 @@ minetest.register_on_joinplayer(function(player)
return_fields(player, "enchanting_lapis")
end)
dofile(minetest.get_modpath(minetest.get_current_modname()).."/creative.lua")
---@param player mt.PlayerObjectRef
function mcl_inventory.update_inventory(player)
local player_gamemode = mcl_gamemode.get_gamemode(player)
if player_gamemode == "creative" then
mcl_inventory.set_creative_formspec(player)
elseif player_gamemode == "survival" then
player:set_inventory_formspec(mcl_inventory.build_survival_formspec(player))
end
end
mcl_gamemode.register_on_gamemode_change(function(player, old_gamemode, new_gamemode)
set_inventory(player)
end)
mcl_player.register_on_visual_change(mcl_inventory.update_inventory_formspec)
local mt_is_creative_enabled = minetest.is_creative_enabled
function minetest.is_creative_enabled(name)
if mt_is_creative_enabled(name) then return true end
if not name then return false end
local p = minetest.get_player_by_name(name)
if p then
return p:get_meta():get_string("gamemode") == "creative"
end
return false
end
local function in_table(n,h)
for k,v in pairs(h) do
if v == n then return true end
end
return false
end
local gamemodes = {
"survival",
"creative"
}
function mcl_inventory.player_set_gamemode(p,g)
local m = p:get_meta()
m:set_string("gamemode",g)
if g == "survival" then
mcl_experience.setup_hud(p)
mcl_experience.update(p)
elseif g == "creative" then
mcl_experience.remove_hud(p)
end
mcl_meshhand.update_player(p)
set_inventory(p)
end
minetest.register_chatcommand("gamemode",{
params = S("[<gamemode>] [<player>]"),
description = S("Change gamemode (survival/creative) for yourself or player"),
privs = { server = true },
func = function(n,param)
-- Full input validation ( just for @erlehmann <3 )
local p
local args = param:split(" ")
if args[2] ~= nil then
p = minetest.get_player_by_name(args[2])
n = args[2]
else
p = minetest.get_player_by_name(n)
end
if not p then
return false, S("Player not online")
end
if args[1] ~= nil and not in_table(args[1],gamemodes) then
return false, S("Gamemode " .. args[1] .. " does not exist.")
elseif args[1] ~= nil then
mcl_inventory.player_set_gamemode(p,args[1])
end
--Result message - show effective game mode
local gm = p:get_meta():get_string("gamemode")
if gm == "" then gm = gamemodes[1] end
return true, S("Gamemode for player ")..n..S(": "..gm)
end
})

@ -1,5 +1,5 @@
name = mcl_inventory
author = BlockMen
description = Adds the player inventory and creative inventory.
depends = mcl_init, mcl_formspec, mcl_enchanting, mcl_player
optional_depends = mcl_armor, mcl_brewing, mcl_potions, mcl_enchanting, mcl_craftguide
depends = mcl_init, mcl_formspec, mcl_enchanting, mcl_gamemode
optional_depends = mcl_armor, mcl_brewing, mcl_potions, mcl_enchanting, mcl_craftguide, mcl_player

@ -0,0 +1,224 @@
---@diagnostic disable need-check-nil
local table = table
local ipairs = ipairs
local S = minetest.get_translator("mcl_inventory")
local F = minetest.formspec_escape
---@type {id: string, description: string, item_icon: string, build: (fun(player: ObjectRef): string), handle: fun(player: ObjectRef, fields: table), access: (fun(player): boolean), show_inventory: boolean}[]
mcl_inventory.registered_survival_inventory_tabs = {}
---@param def {id: string, description: string, item_icon: string, build: (fun(player: ObjectRef): string), handle: fun(player: ObjectRef, fields: table), access: (fun(player): boolean), show_inventory: boolean}
function mcl_inventory.register_survival_inventory_tab(def)
if #mcl_inventory.registered_survival_inventory_tabs == 7 then
error("Too much tabs registered!")
end
assert(def.id)
assert(def.description)
assert(def.item_icon)
assert(def.build)
assert(def.handle)
for _, d in ipairs(mcl_inventory.registered_survival_inventory_tabs) do
assert(d.id ~= def.id, "Another tab exists with the same name!")
end
if not def.access then
function def.access(player)
return true
end
end
if def.show_inventory == nil then
def.show_inventory = true
end
table.insert(mcl_inventory.registered_survival_inventory_tabs, def)
end
local player_current_tab = {}
minetest.register_on_joinplayer(function(player, last_login)
player_current_tab[player] = "main"
end)
minetest.register_on_leaveplayer(function(player, timed_out)
player_current_tab[player] = nil
end)
---@param player ObjectRef
---@param content string
---@param inventory boolean
---@param tabname string
local function build_page(player, content, inventory, tabname)
local tab_buttons = "style_type[image;noclip=true]"
if #mcl_inventory.registered_survival_inventory_tabs ~= 1 then
for i, d in ipairs(mcl_inventory.registered_survival_inventory_tabs) do
local btn_name = "tab_" .. d.id
tab_buttons = tab_buttons .. table.concat({
"style[" .. btn_name .. ";border=false;bgimg=;bgimg_pressed=;noclip=true]",
"image[" ..
(0.2 + (i - 1) * 1.6) ..
",-1.34;1.5,1.44;" .. (tabname == d.id and "crafting_creative_active.png" or "crafting_creative_inactive.png") ..
"]",
"item_image_button[" .. (0.44 + (i - 1) * 1.6) .. ",-1.1;1,1;" .. d.item_icon .. ";" .. btn_name .. ";]",
"tooltip[" .. btn_name .. ";" .. F(d.description) .. "]"
})
end
end
return table.concat({
"formspec_version[6]",
"size[11.75,10.9]",
inventory and table.concat({
--Main inventory
mcl_formspec.get_itemslot_bg_v4(0.375, 5.575, 9, 3),
"list[current_player;main;0.375,5.575;9,3;9]",
--Hotbar
mcl_formspec.get_itemslot_bg_v4(0.375, 9.525, 9, 1),
"list[current_player;main;0.375,9.525;9,1;]"
}) or "",
content,
tab_buttons,
})
end
local main_page_static = table.concat({
--Armor slots
mcl_formspec.get_itemslot_bg_v4(0.375, 0.375, 1, 4),
"list[current_player;armor;0.375,0.375;1,1;1]",
"list[current_player;armor;0.375,1.625;1,1;2]",
"list[current_player;armor;0.375,2.875;1,1;3]",
"list[current_player;armor;0.375,4.125;1,1;4]",
--Player model background
"image[1.57,0.343;3.62,4.85;mcl_inventory_background9.png;2]",
--Offhand
mcl_formspec.get_itemslot_bg_v4(5.375, 4.125, 1, 1),
"list[current_player;offhand;5.375,4.125;1,1]",
--Craft grid
"label[6.61,0.5;" .. F(minetest.colorize(mcl_formspec.label_color, S("Crafting"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(6.625, 0.875, 2, 2),
"list[current_player;craft;6.625,0.875;2,2]",
"image[9.125,1.5;1,1;crafting_formspec_arrow.png]",
mcl_formspec.get_itemslot_bg_v4(10.375, 1.5, 1, 1),
"list[current_player;craftpreview;10.375,1.5;1,1;]",
--Crafting guide button
"image_button[6.575,4.075;1.1,1.1;craftguide_book.png;__mcl_craftguide;]",
"tooltip[__mcl_craftguide;" .. F(S("Recipe book")) .. "]",
--Help button
"image_button[7.825,4.075;1.1,1.1;doc_button_icon_lores.png;__mcl_doc;]",
"tooltip[__mcl_doc;" .. F(S("Help")) .. "]",
--Skins button
"image_button[9.075,4.075;1.1,1.1;mcl_skins_button.png;__mcl_skins;]",
"tooltip[__mcl_skins;" .. F(S("Select player skin")) .. "]",
--Achievements button
"image_button[10.325,4.075;1.1,1.1;mcl_achievements_button.png;__mcl_achievements;]",
"tooltip[__mcl_achievements;" .. F(S("Achievements")) .. "]",
--Listring
"listring[current_player;main]",
"listring[current_player;armor]",
"listring[current_player;main]",
"listring[current_player;craft]",
"listring[current_player;main]",
})
mcl_inventory.register_survival_inventory_tab({
id = "main",
description = "Main Inventory",
item_icon = "mcl_crafting_table:crafting_table",
show_inventory = true,
build = function(player)
local inv = player:get_inventory()
local armor_slots = { "helmet", "chestplate", "leggings", "boots" }
local armor_slot_imgs = ""
for a = 1, 4 do
if inv:get_stack("armor", a + 1):is_empty() then
armor_slot_imgs = armor_slot_imgs ..
"image[0.375," .. (0.375 + (a - 1) * 1.25) .. ";1,1;mcl_inventory_empty_armor_slot_" .. armor_slots[a] .. ".png]"
end
end
if inv:get_stack("offhand", 1):is_empty() then
armor_slot_imgs = armor_slot_imgs .. "image[5.375,4.125;1,1;mcl_inventory_empty_armor_slot_shield.png]"
end
return main_page_static .. armor_slot_imgs .. mcl_player.get_player_formspec_model(player, 1.57, 0.4, 3.62, 4.85, "")
end,
handle = function() end,
})
--[[
mcl_inventory.register_survival_inventory_tab({
id = "test",
description = "Test",
item_icon = "mcl_core:stone",
show_inventory = true,
build = function(player)
return "label[1,1;Hello hello]button[2,2;2,2;Hello;hey]"
end,
handle = function(player, fields)
print(dump(fields))
end,
})]]
---@param player ObjectRef
function mcl_inventory.build_survival_formspec(player)
local inv = player:get_inventory()
inv:set_width("craft", 2)
inv:set_size("craft", 4)
local tab = player_current_tab[player]
local tab_def = nil
for _, d in ipairs(mcl_inventory.registered_survival_inventory_tabs) do
if tab == d.id then
tab_def = d
break
end
end
local form = build_page(player, tab_def.build(player), tab_def.show_inventory, tab)
return form
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == "" and #mcl_inventory.registered_survival_inventory_tabs ~= 1 and
mcl_gamemode.get_gamemode(player) == "survival" then
for _, d in ipairs(mcl_inventory.registered_survival_inventory_tabs) do
if fields["tab_" .. d.id] and d.access(player) then
player_current_tab[player] = d.id
mcl_inventory.update_inventory(player)
return
end
end
for _, d in ipairs(mcl_inventory.registered_survival_inventory_tabs) do
if player_current_tab[player] == d.id and d.access(player) then
d.handle(player, fields)
return
end
end
end
end)

@ -8,23 +8,36 @@ All node definitions share a lot of code, so this is the reason why there
are so many weird tables below.
]]
local S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
local F = minetest.formspec_escape
-- For after_place_node
local dispenser_formspec = table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[4.125,0.375;" .. F(C(mcl_formspec.label_color, S("Dispenser"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(4.125, 0.75, 3, 3),
"list[context;main;4.125,0.75;3,3;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
"listring[context;main]",
"listring[current_player;main]",
})
---For after_place_node
---@param pos Vector
local function setup_dispenser(pos)
-- Set formspec and inventory
local form = "size[9,8.75]"..
"label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"label[3,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Dispenser"))).."]"..
"list[context;main;3,0.5;3,3;]"..
mcl_formspec.get_itemslot_bg(3,0.5,3,3)..
"listring[context;main]"..
"listring[current_player;main]"
local meta = minetest.get_meta(pos)
meta:set_string("formspec", form)
meta:set_string("formspec", dispenser_formspec)
local inv = meta:get_inventory()
inv:set_size("main", 9)
end
@ -38,9 +51,9 @@ local function orientate_dispenser(pos, placer)
local node = minetest.get_node(pos)
if pitch > 55 then
minetest.swap_node(pos, {name="mcl_dispensers:dispenser_up", param2 = node.param2})
minetest.swap_node(pos, { name = "mcl_dispensers:dispenser_up", param2 = node.param2 })
elseif pitch < -55 then
minetest.swap_node(pos, {name="mcl_dispensers:dispenser_down", param2 = node.param2})
minetest.swap_node(pos, { name = "mcl_dispensers:dispenser_down", param2 = node.param2 })
end
end
@ -85,11 +98,10 @@ local dispenserdef = {
local meta2 = meta:to_table()
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for i=1, inv:get_size("main") do
for i = 1, inv:get_size("main") do
local stack = inv:get_stack("main", i)
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
minetest.add_item(p, stack)
minetest.add_item(vector.offset(pos, math.random(0, 10) / 10 - 0.5, 0, math.random(0, 10) / 10 - 0.5), stack)
end
end
meta:from_table(meta2)
@ -107,19 +119,19 @@ local dispenserdef = {
dropdir = vector.multiply(minetest.facedir_to_dir(node.param2), -1)
droppos = vector.add(pos, dropdir)
elseif node.name == "mcl_dispensers:dispenser_up" then
dropdir = {x=0, y=1, z=0}
droppos = {x=pos.x, y=pos.y+1, z=pos.z}
dropdir = vector.new(0, 1, 0)
droppos = vector.offset(pos, 0, 1, 0)
elseif node.name == "mcl_dispensers:dispenser_down" then
dropdir = {x=0, y=-1, z=0}
droppos = {x=pos.x, y=pos.y-1, z=pos.z}
dropdir = vector.new(0, -1, 0)
droppos = vector.offset(pos, 0, -1, 0)
end
local dropnode = minetest.get_node(droppos)
local dropnodedef = minetest.registered_nodes[dropnode.name]
local stacks = {}
for i=1,inv:get_size("main") do
for i = 1, inv:get_size("main") do
local stack = inv:get_stack("main", i)
if not stack:is_empty() then
table.insert(stacks, {stack = stack, stackpos = i})
table.insert(stacks, { stack = stack, stackpos = i })
end
end
if #stacks >= 1 then
@ -143,9 +155,10 @@ local dispenserdef = {
-- Armor, mob heads and pumpkins
if igroups.armor then
local droppos_below = {x = droppos.x, y = droppos.y - 1, z = droppos.z}
local droppos_below = vector.offset(droppos, 0, -1, 0)
for _, objs in ipairs({minetest.get_objects_inside_radius(droppos, 1), minetest.get_objects_inside_radius(droppos_below, 1)}) do
for _, objs in ipairs({ minetest.get_objects_inside_radius(droppos, 1),
minetest.get_objects_inside_radius(droppos_below, 1) }) do
for _, obj in ipairs(objs) do
stack = mcl_armor.equip(stack, obj)
if stack:is_empty() then
@ -161,7 +174,7 @@ local dispenserdef = {
if not stack:is_empty() then
if igroups.head or iname == "mcl_farming:pumpkin_face" then
if dropnodedef.buildable_to then
minetest.set_node(droppos, {name = iname, param2 = node.param2})
minetest.set_node(droppos, { name = iname, param2 = node.param2 })
stack:take_item()
end
end
@ -263,7 +276,7 @@ local dispenserdef = {
local item_entity = minetest.add_item(droppos, dropitem)
local drop_vel = vector.subtract(droppos, pos)
local speed = 3
item_entity:set_velocity(vector.multiply(drop_vel,speed))
item_entity:set_velocity(vector.multiply(drop_vel, speed))
end
else
stack:take_item()
@ -280,7 +293,7 @@ local dispenserdef = {
local item_entity = minetest.add_item(droppos, dropitem)
local drop_vel = vector.subtract(droppos, pos)
local speed = 3
item_entity:set_velocity(vector.multiply(drop_vel,speed))
item_entity:set_velocity(vector.multiply(drop_vel, speed))
stack:take_item()
inv:set_stack("main", stack_id, stack)
end
@ -299,27 +312,28 @@ local dispenserdef = {
local horizontal_def = table.copy(dispenserdef)
horizontal_def.description = S("Dispenser")
horizontal_def._tt_help = S("9 inventory slots").."\n"..S("Launches item when powered by redstone power")
horizontal_def._tt_help = S("9 inventory slots") .. "\n" .. S("Launches item when powered by redstone power")
horizontal_def._doc_items_longdesc = S("A dispenser is a block which acts as a redstone component which, when powered with redstone power, dispenses an item. It has a container with 9 inventory slots.")
horizontal_def._doc_items_usagehelp = S("Place the dispenser in one of 6 possible directions. The “hole” is where items will fly out of the dispenser. Use the dispenser to access its inventory. Insert the items you wish to dispense. Supply the dispenser with redstone energy once to dispense a random item.").."\n\n"..
horizontal_def._doc_items_usagehelp = S("Place the dispenser in one of 6 possible directions. The “hole” is where items will fly out of the dispenser. Use the dispenser to access its inventory. Insert the items you wish to dispense. Supply the dispenser with redstone energy once to dispense a random item.")
.. "\n\n" ..
S("The dispenser will do different things, depending on the dispensed item:").."\n\n"..
S("The dispenser will do different things, depending on the dispensed item:") .. "\n\n" ..
S("• Arrows: Are launched").."\n"..
S("• Eggs and snowballs: Are thrown").."\n"..
S("• Fire charges: Are fired in a straight line").."\n"..
S("• Armor: Will be equipped to players and armor stands").."\n"..
S("• Boats: Are placed on water or are dropped").."\n"..
S("• Minecart: Are placed on rails or are dropped").."\n"..
S("• Bone meal: Is applied on the block it is facing").."\n"..
S("• Empty buckets: Are used to collect a liquid source").."\n"..
S("• Filled buckets: Are used to place a liquid source").."\n"..
S("• Heads, pumpkins: Equipped to players and armor stands, or placed as a block").."\n"..
S("• Shulker boxes: Are placed as a block").."\n"..
S("• TNT: Is placed and ignited").."\n"..
S("• Flint and steel: Is used to ignite a fire in air and to ignite TNT").."\n"..
S("• Spawn eggs: Will summon the mob they contain").."\n"..
S("• Other items: Are simply dropped")
S("• Arrows: Are launched") .. "\n" ..
S("• Eggs and snowballs: Are thrown") .. "\n" ..
S("• Fire charges: Are fired in a straight line") .. "\n" ..
S("• Armor: Will be equipped to players and armor stands") .. "\n" ..
S("• Boats: Are placed on water or are dropped") .. "\n" ..
S("• Minecart: Are placed on rails or are dropped") .. "\n" ..
S("• Bone meal: Is applied on the block it is facing") .. "\n" ..
S("• Empty buckets: Are used to collect a liquid source") .. "\n" ..
S("• Filled buckets: Are used to place a liquid source") .. "\n" ..
S("• Heads, pumpkins: Equipped to players and armor stands, or placed as a block") .. "\n" ..
S("• Shulker boxes: Are placed as a block") .. "\n" ..
S("• TNT: Is placed and ignited") .. "\n" ..
S("• Flint and steel: Is used to ignite a fire in air and to ignite TNT") .. "\n" ..
S("• Spawn eggs: Will summon the mob they contain") .. "\n" ..
S("• Other items: Are simply dropped")
function horizontal_def.after_place_node(pos, placer, itemstack, pointed_thing)
setup_dispenser(pos)
@ -332,7 +346,7 @@ horizontal_def.tiles = {
"default_furnace_side.png", "mcl_dispensers_dispenser_front_horizontal.png"
}
horizontal_def.paramtype2 = "facedir"
horizontal_def.groups = {pickaxey=1, container=2, material_stone=1}
horizontal_def.groups = { pickaxey = 1, container = 2, material_stone = 1 }
minetest.register_node("mcl_dispensers:dispenser", horizontal_def)
@ -345,7 +359,7 @@ down_def.tiles = {
"default_furnace_side.png", "default_furnace_side.png",
"default_furnace_side.png", "default_furnace_side.png"
}
down_def.groups = {pickaxey=1, container=2,not_in_creative_inventory=1, material_stone=1}
down_def.groups = { pickaxey = 1, container = 2, not_in_creative_inventory = 1, material_stone = 1 }
down_def._doc_items_create_entry = false
down_def.drop = "mcl_dispensers:dispenser"
minetest.register_node("mcl_dispensers:dispenser_down", down_def)
@ -365,9 +379,9 @@ minetest.register_node("mcl_dispensers:dispenser_up", up_def)
minetest.register_craft({
output = "mcl_dispensers:dispenser",
recipe = {
{"mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble",},
{"mcl_core:cobble", "mcl_bows:bow", "mcl_core:cobble",},
{"mcl_core:cobble", "mesecons:redstone", "mcl_core:cobble",},
{ "mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble", },
{ "mcl_core:cobble", "mcl_bows:bow", "mcl_core:cobble", },
{ "mcl_core:cobble", "mesecons:redstone", "mcl_core:cobble", },
}
})
@ -384,6 +398,6 @@ minetest.register_lbm({
nodenames = { "mcl_dispensers:dispenser", "mcl_dispensers:dispenser_down", "mcl_dispensers:dispenser_up" },
action = function(pos, node)
setup_dispenser(pos)
minetest.log("action", "[mcl_dispenser] Node formspec updated at "..minetest.pos_to_string(pos))
minetest.log("action", "[mcl_dispenser] Node formspec updated at " .. minetest.pos_to_string(pos))
end,
})

@ -9,23 +9,36 @@ are so many weird tables below.
]]
local S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
local F = minetest.formspec_escape
-- For after_place_node
local dropper_formspec = table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[4.125,0.375;" .. F(C(mcl_formspec.label_color, S("Dropper"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(4.125, 0.75, 3, 3),
"list[context;main;4.125,0.75;3,3;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
"listring[context;main]",
"listring[current_player;main]",
})
---For after_place_node
---@param pos Vector
local function setup_dropper(pos)
-- Set formspec and inventory
local form = "size[9,8.75]"..
"label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"label[3,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Dropper"))).."]"..
"list[context;main;3,0.5;3,3;]"..
mcl_formspec.get_itemslot_bg(3,0.5,3,3)..
"listring[context;main]"..
"listring[current_player;main]"
local meta = minetest.get_meta(pos)
meta:set_string("formspec", form)
meta:set_string("formspec", dropper_formspec)
local inv = meta:get_inventory()
inv:set_size("main", 9)
end
@ -38,9 +51,9 @@ local function orientate_dropper(pos, placer)
local pitch = placer:get_look_vertical() * (180 / math.pi)
if pitch > 55 then
minetest.swap_node(pos, {name="mcl_droppers:dropper_up"})
minetest.swap_node(pos, { name = "mcl_droppers:dropper_up" })
elseif pitch < -55 then
minetest.swap_node(pos, {name="mcl_droppers:dropper_down"})
minetest.swap_node(pos, { name = "mcl_droppers:dropper_down" })
end
end
@ -58,11 +71,10 @@ local dropperdef = {
local meta2 = meta:to_table()
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for i=1, inv:get_size("main") do
for i = 1, inv:get_size("main") do
local stack = inv:get_stack("main", i)
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
minetest.add_item(p, stack)
minetest.add_item(vector.offset(pos, math.random(0, 10) / 10 - 0.5, 0, math.random(0, 10) / 10 - 0.5), stack)
end
end
meta:from_table(meta2)
@ -96,7 +108,7 @@ local dropperdef = {
end,
_mcl_blast_resistance = 3.5,
_mcl_hardness = 3.5,
mesecons = {effector = {
mesecons = { effector = {
-- Drop random item when triggered
action_on = function(pos, node)
if not pos then return end
@ -106,9 +118,9 @@ local dropperdef = {
if node.name == "mcl_droppers:dropper" then
droppos = vector.subtract(pos, minetest.facedir_to_dir(node.param2))
elseif node.name == "mcl_droppers:dropper_up" then
droppos = {x=pos.x, y=pos.y+1, z=pos.z}
droppos = vector.offset(pos, 0, 1, 0)
elseif node.name == "mcl_droppers:dropper_down" then
droppos = {x=pos.x, y=pos.y-1, z=pos.z}
droppos = vector.offset(pos, 0, -1, 0)
end
local dropnode = minetest.get_node(droppos)
-- Do not drop into solid nodes, unless they are containers
@ -117,10 +129,10 @@ local dropperdef = {
return
end
local stacks = {}
for i=1,inv:get_size("main") do
for i = 1, inv:get_size("main") do
local stack = inv:get_stack("main", i)
if not stack:is_empty() then
table.insert(stacks, {stack = stack, stackpos = i})
table.insert(stacks, { stack = stack, stackpos = i })
end
end
if #stacks >= 1 then
@ -136,22 +148,22 @@ local dropperdef = {
if not dropped and not dropnodedef.groups.container then
-- Drop item normally
local pos_variation = 100
droppos = {
x = droppos.x + math.random(-pos_variation, pos_variation) / 1000,
y = droppos.y + math.random(-pos_variation, pos_variation) / 1000,
z = droppos.z + math.random(-pos_variation, pos_variation) / 1000,
}
droppos = vector.offset(droppos,
math.random(-pos_variation, pos_variation) / 1000,
math.random(-pos_variation, pos_variation) / 1000,
math.random(-pos_variation, pos_variation) / 1000
)
local item_entity = minetest.add_item(droppos, dropitem)
local drop_vel = vector.subtract(droppos, pos)
local speed = 3
item_entity:set_velocity(vector.multiply(drop_vel,speed))
item_entity:set_velocity(vector.multiply(drop_vel, speed))
stack:take_item()
inv:set_stack("main", stack_id, stack)
end
end
end,
rules = mesecon.rules.alldirs,
}},
} },
on_rotate = on_rotate,
}
@ -159,20 +171,21 @@ local dropperdef = {
local horizontal_def = table.copy(dropperdef)
horizontal_def.description = S("Dropper")
horizontal_def._tt_help = S("9 inventory slots").."\n"..S("Drops item when powered by redstone power")
horizontal_def._tt_help = S("9 inventory slots") .. "\n" .. S("Drops item when powered by redstone power")
horizontal_def._doc_items_longdesc = S("A dropper is a redstone component and a container with 9 inventory slots which, when supplied with redstone power, drops an item or puts it into a container in front of it.")
horizontal_def._doc_items_usagehelp = S("Droppers can be placed in 6 possible directions, items will be dropped out of the hole. Use the dropper to access its inventory. Supply it with redstone energy once to make the dropper drop or transfer a random item.")
function horizontal_def.after_place_node(pos, placer, itemstack, pointed_thing)
setup_dropper(pos)
orientate_dropper(pos, placer)
end
horizontal_def.tiles = {
"default_furnace_top.png", "default_furnace_bottom.png",
"default_furnace_side.png", "default_furnace_side.png",
"default_furnace_side.png", "mcl_droppers_dropper_front_horizontal.png"
}
horizontal_def.paramtype2 = "facedir"
horizontal_def.groups = {pickaxey=1, container=2, material_stone=1}
horizontal_def.groups = { pickaxey = 1, container = 2, material_stone = 1 }
minetest.register_node("mcl_droppers:dropper", horizontal_def)
@ -185,7 +198,7 @@ down_def.tiles = {
"default_furnace_side.png", "default_furnace_side.png",
"default_furnace_side.png", "default_furnace_side.png"
}
down_def.groups = {pickaxey=1, container=2,not_in_creative_inventory=1, material_stone=1}
down_def.groups = { pickaxey = 1, container = 2, not_in_creative_inventory = 1, material_stone = 1 }
down_def._doc_items_create_entry = false
down_def.drop = "mcl_droppers:dropper"
minetest.register_node("mcl_droppers:dropper_down", down_def)
@ -207,9 +220,9 @@ minetest.register_node("mcl_droppers:dropper_up", up_def)
minetest.register_craft({
output = "mcl_droppers:dropper",
recipe = {
{"mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble",},
{"mcl_core:cobble", "", "mcl_core:cobble",},
{"mcl_core:cobble", "mesecons:redstone", "mcl_core:cobble",},
{ "mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble", },
{ "mcl_core:cobble", "", "mcl_core:cobble", },
{ "mcl_core:cobble", "mesecons:redstone", "mcl_core:cobble", },
}
})
@ -226,6 +239,6 @@ minetest.register_lbm({
nodenames = { "mcl_droppers:dropper", "mcl_droppers:dropper_down", "mcl_droppers:dropper_up" },
action = function(pos, node)
setup_dropper(pos)
minetest.log("action", "[mcl_droppers] Node formspec updated at "..minetest.pos_to_string(pos))
minetest.log("action", "[mcl_droppers] Node formspec updated at " .. minetest.pos_to_string(pos))
end,
})

@ -1,4 +1,6 @@
local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape
local C = minetest.colorize
local MAX_NAME_LENGTH = 35
local MAX_WEAR = 65535
@ -10,41 +12,65 @@ local MATERIAL_TOOL_REPAIR_BOOST = {
MAX_WEAR, -- 100%
}
---@param set_name? string
local function get_anvil_formspec(set_name)
if not set_name then
set_name = ""
end
return "size[9,8.75]"..
"background[-0.19,-0.25;9.41,9.49;mcl_anvils_inventory.png]"..
"label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"list[context;input;1,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(1,2.5,1,1)..
"list[context;input;4,2.5;1,1;1]"..
mcl_formspec.get_itemslot_bg(4,2.5,1,1)..
"list[context;output;8,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(8,2.5,1,1)..
"label[3,0.1;"..minetest.formspec_escape(minetest.colorize("#313131", S("Repair and Name"))).."]"..
"field[3.25,1;4,1;name;;"..minetest.formspec_escape(set_name).."]"..
"field_close_on_enter[name;false]"..
"button[7,0.7;2,1;name_button;"..minetest.formspec_escape(S("Set Name")).."]"..
"listring[context;output]"..
"listring[current_player;main]"..
"listring[context;input]"..
"listring[current_player;main]"
return table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[4.125,0.375;" .. F(C(mcl_formspec.label_color, S("Repair and Name"))) .. "]",
"image[0.875,0.375;1.75,1.75;mcl_anvils_inventory_hammer.png]",
"field[4.125,0.75;7.25,1;name;;" .. F(set_name) .. "]",
"field_close_on_enter[name;false]",
"set_focus[name;true]",
mcl_formspec.get_itemslot_bg_v4(1.625, 2.6, 1, 1),
"list[context;input;1.625,2.6;1,1;]",
"image[3.5,2.6;1,1;mcl_anvils_inventory_cross.png]",
mcl_formspec.get_itemslot_bg_v4(5.375, 2.6, 1, 1),
"list[context;input;5.375,2.6;1,1;1]",
"image[6.75,2.6;2,1;mcl_anvils_inventory_arrow.png]",
mcl_formspec.get_itemslot_bg_v4(9.125, 2.6, 1, 1),
"list[context;output;9.125,2.6;1,1;]",
-- Player Inventory
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
-- Listrings
"listring[context;output]",
"listring[current_player;main]",
"listring[context;input]",
"listring[current_player;main]",
})
end
-- Given a tool and material stack, returns how many items of the material stack
-- needs to be used up to repair the tool.
---@param tool ItemStack
---@param material ItemStack
---@return integer
local function get_consumed_materials(tool, material)
local wear = tool:get_wear()
--local health = (MAX_WEAR - wear)
local matsize = material:get_count()
local materials_used = 0
for m=1, math.min(4, matsize) do
for m = 1, math.min(4, matsize) do
materials_used = materials_used + 1
if (wear - MATERIAL_TOOL_REPAIR_BOOST[m]) <= 0 then
break
@ -53,27 +79,20 @@ local function get_consumed_materials(tool, material)
return materials_used
end
local function contains(table, value)
for _, i in pairs(table) do
if i == value then
return true
end
end
return false
end
-- Given 2 input stacks, tells you which is the tool and which is the material.
-- Returns ("tool", input1, input2) if input1 is tool and input2 is material.
-- Returns ("material", input2, input1) if input1 is material and input2 is tool.
-- Returns nil otherwise.
---@param input1 ItemStack
---@param input2 ItemStack
local function distinguish_tool_and_material(input1, input2)
local def1 = input1:get_definition()
local def2 = input2:get_definition()
local r1 = def1._repair_material
local r2 = def2._repair_material
if def1.type == "tool" and r1 and type(r1) == "table" and contains(r1, input2) then
if def1.type == "tool" and r1 and type(r1) == "table" and table.indexof(r1, input2) ~= -1 then
return "tool", input1, input2
elseif def2.type == "tool" and r2 and type(r2) == "table" and contains(r2, input1) then
elseif def2.type == "tool" and r2 and type(r2) == "table" and table.indexof(r1, input1) ~= -1 then
return "material", input2, input1
elseif def1.type == "tool" and r1 then
return "tool", input1, input2
@ -84,7 +103,8 @@ local function distinguish_tool_and_material(input1, input2)
end
end
-- Helper function to make sure update_anvil_slots NEVER overstacks the output slot
---Helper function to make sure update_anvil_slots NEVER overstacks the output slot
---@param stack ItemStack
local function fix_stack_size(stack)
if not stack or stack == "" then return "" end
local count = stack:get_count()
@ -99,6 +119,7 @@ end
-- Update the inventory slots of an anvil node.
-- meta: Metadata of anvil node
---@param meta NodeMetaRef
local function update_anvil_slots(meta)
local inv = meta:get_inventory()
local new_name = meta:get_string("set_name")
@ -159,7 +180,7 @@ local function update_anvil_slots(meta)
has_correct_material = true
end
else
if contains(repair, material_name) then
if table.indexof(repair, material_name) ~= -1 then
has_correct_material = true
else
for _, r in pairs(repair) do
@ -231,28 +252,32 @@ local function update_anvil_slots(meta)
end
end
-- Drop input items of anvil at pos with metadata meta
---Drop input items of anvil at pos with metadata meta
---@param pos Vector
---@param meta NodeMetaRef
local function drop_anvil_items(pos, meta)
local inv = meta:get_inventory()
for i=1, inv:get_size("input") do
for i = 1, inv:get_size("input") do
local stack = inv:get_stack("input", i)
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
local p = vector.offset(pos, math.random(0, 10) / 10 - 0.5, 0, math.random(0, 10) / 10 - 0.5)
minetest.add_item(p, stack)
end
end
end
---@param pos Vector
---@param node node
local function damage_particles(pos, node)
minetest.add_particlespawner({
amount = 30,
time = 0.1,
minpos = vector.add(pos, {x=-0.5, y=-0.5, z=-0.5}),
maxpos = vector.add(pos, {x=0.5, y=-0.25, z=0.5}),
minvel = {x=-0.5, y=0.05, z=-0.5},
maxvel = {x=0.5, y=0.3, z=0.5},
minacc = {x=0, y=-9.81, z=0},
maxacc = {x=0, y=-9.81, z=0},
minpos = vector.offset(pos, -0.5, -0.5, -0.5),
maxpos = vector.offset(pos, 0.5, -0.25, 0.5),
minvel = vector.new(-0.5, 0.05, -0.5),
maxvel = vector.new(0.5, 0.3, 0.5),
minacc = vector.new(0, -9.81, 0),
maxacc = vector.new(0, -9.81, 0),
minexptime = 0.1,
maxexptime = 0.5,
minsize = 0.4,
@ -267,12 +292,12 @@ local function destroy_particles(pos, node)
minetest.add_particlespawner({
amount = math.random(20, 30),
time = 0.1,
minpos = vector.add(pos, {x=-0.4, y=-0.4, z=-0.4}),
maxpos = vector.add(pos, {x=0.4, y=0.4, z=0.4}),
minvel = {x=-0.5, y=-0.1, z=-0.5},
maxvel = {x=0.5, y=0.2, z=0.5},
minacc = {x=0, y=-9.81, z=0},
maxacc = {x=0, y=-9.81, z=0},
minpos = vector.offset(pos, -0.4, -0.4, -0.4),
maxpos = vector.offset(pos, 0.4, 0.4, 0.4),
minvel = vector.new(-0.5, -0.1, -0.5),
maxvel = vector.new(0.5, 0.2, 0.5),
minacc = vector.new(0, -9.81, 0),
maxacc = vector.new(0, -9.81, 0),
minexptime = 0.2,
maxexptime = 0.65,
minsize = 0.8,
@ -289,28 +314,29 @@ end
local function damage_anvil(pos)
local node = minetest.get_node(pos)
if node.name == "mcl_anvils:anvil" then
minetest.swap_node(pos, {name="mcl_anvils:anvil_damage_1", param2=node.param2})
minetest.swap_node(pos, { name = "mcl_anvils:anvil_damage_1", param2 = node.param2 })
damage_particles(pos, node)
minetest.sound_play(mcl_sounds.node_sound_metal_defaults().dig, {pos=pos, max_hear_distance=16}, true)
minetest.sound_play(mcl_sounds.node_sound_metal_defaults().dig, { pos = pos, max_hear_distance = 16 }, true)
return false
elseif node.name == "mcl_anvils:anvil_damage_1" then
minetest.swap_node(pos, {name="mcl_anvils:anvil_damage_2", param2=node.param2})
minetest.swap_node(pos, { name = "mcl_anvils:anvil_damage_2", param2 = node.param2 })
damage_particles(pos, node)
minetest.sound_play(mcl_sounds.node_sound_metal_defaults().dig, {pos=pos, max_hear_distance=16}, true)
minetest.sound_play(mcl_sounds.node_sound_metal_defaults().dig, { pos = pos, max_hear_distance = 16 }, true)
return false
elseif node.name == "mcl_anvils:anvil_damage_2" then
-- Destroy anvil
local meta = minetest.get_meta(pos)
drop_anvil_items(pos, meta)
minetest.sound_play(mcl_sounds.node_sound_metal_defaults().dug, {pos=pos, max_hear_distance=16}, true)
minetest.sound_play(mcl_sounds.node_sound_metal_defaults().dug, { pos = pos, max_hear_distance = 16 }, true)
minetest.remove_node(pos)
destroy_particles(pos, node)
minetest.check_single_for_falling({x=pos.x, y=pos.y+1, z=pos.z})
minetest.check_single_for_falling(vector.offset(pos, 0, 1, 0))
return true
end
end
-- Roll a virtual dice and damage anvil at a low chance.
---Roll a virtual dice and damage anvil at a low chance.
---@param pos Vector
local function damage_anvil_by_using(pos)
local r = math.random(1, 100)
-- 12% chance
@ -321,25 +347,30 @@ local function damage_anvil_by_using(pos)
end
end
---@param pos Vector
---@param distance number
local function damage_anvil_by_falling(pos, distance)
local r = math.random(1, 100)
if distance > 1 then
if r <= (5*distance) then
if r <= (5 * distance) then
damage_anvil(pos)
end
end
end
---@type nodebox
local anvilbox = {
type = "fixed",
fixed = {
{ -8 / 16, -8 / 16, -6 / 16, 8 / 16, 8 / 16, 6 / 16 },
},
}
---@type node_definition
local anvildef = {
groups = {pickaxey=1, falling_node=1, falling_node_damage=1, crush_after_fall=1, deco_block=1, anvil=1},
tiles = {"mcl_anvils_anvil_top_damaged_0.png^[transformR90", "mcl_anvils_anvil_base.png", "mcl_anvils_anvil_side.png"},
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
groups = { pickaxey = 1, falling_node = 1, falling_node_damage = 1, crush_after_fall = 1, deco_block = 1, anvil = 1 },
tiles = { "mcl_anvils_anvil_top_damaged_0.png^[transformR90", "mcl_anvils_anvil_base.png", "mcl_anvils_anvil_side.png" },
use_texture_alpha = "opaque",
_tt_help = S("Repair and rename items"),
paramtype = "light",
sunlight_propagates = true,
@ -353,7 +384,7 @@ local anvildef = {
{ -5 / 16, -4 / 16, -4 / 16, 5 / 16, -3 / 16, 4 / 16 },
{ -4 / 16, -3 / 16, -2 / 16, 4 / 16, 2 / 16, 2 / 16 },
{ -8 / 16, 2 / 16, -5 / 16, 8 / 16, 8 / 16, 5 / 16 },
}
},
},
selection_box = anvilbox,
collision_box = anvilbox,
@ -416,7 +447,7 @@ local anvildef = {
local meta = minetest.get_meta(pos)
if from_list == "output" and to_list == "input" then
local inv = meta:get_inventory()
for i=1, inv:get_size("input") do
for i = 1, inv:get_size("input") do
if i ~= to_index then
local istack = inv:get_stack("input", i)
istack:set_count(math.max(0, istack:get_count() - count))
@ -504,22 +535,20 @@ local anvildef = {
minetest.record_protection_violation(pos, sender_name)
return
end
if fields.name_button or fields.name then
local set_name
if fields.name == nil then
set_name = ""
else
set_name = fields.name
end
if fields.name then
local meta = minetest.get_meta(pos)
-- Limit name length
set_name = string.sub(set_name, 1, MAX_NAME_LENGTH)
local set_name = string.sub(fields.name, 1, MAX_NAME_LENGTH)
meta:set_string("set_name", set_name)
update_anvil_slots(meta)
meta:set_string("formspec", get_anvil_formspec(set_name))
end
end,
}
if minetest.get_modpath("screwdriver") then
anvildef.on_rotate = screwdriver.rotate_simple
end
@ -527,29 +556,34 @@ end
local anvildef0 = table.copy(anvildef)
anvildef0.description = S("Anvil")
anvildef0._doc_items_longdesc =
S("The anvil allows you to repair tools and armor, and to give names to items. It has a limited durability, however. Don't let it fall on your head, it could be quite painful!")
S("The anvil allows you to repair tools and armor, and to give names to items. It has a limited durability, however. Don't let it fall on your head, it could be quite painful!")
anvildef0._doc_items_usagehelp =
S("To use an anvil, rightclick it. An anvil has 2 input slots (on the left) and one output slot.").."\n"..
S("To rename items, put an item stack in one of the item slots while keeping the other input slot empty. Type in a name, hit enter or “Set Name”, then take the renamed item from the output slot.").."\n"..
S("There are two possibilities to repair tools (and armor):").."\n"..
S("• Tool + Tool: Place two tools of the same type in the input slots. The “health” of the repaired tool is the sum of the “health” of both input tools, plus a 12% bonus.").."\n"..
S("• Tool + Material: Some tools can also be repaired by combining them with an item that it's made of. For example, iron pickaxes can be repaired with iron ingots. This repairs the tool by 25%.").."\n"..
S("Armor counts as a tool. It is possible to repair and rename a tool in a single step.").."\n\n"..
S("The anvil has limited durability and 3 damage levels: undamaged, slightly damaged and very damaged. Each time you repair or rename something, there is a 12% chance the anvil gets damaged. Anvils also have a chance of being damaged when they fall by more than 1 block. If a very damaged anvil is damaged again, it is destroyed.")
S("To use an anvil, rightclick it. An anvil has 2 input slots (on the left) and one output slot.") .. "\n" ..
S("To rename items, put an item stack in one of the item slots while keeping the other input slot empty. Type in a name, hit enter or “Set Name”, then take the renamed item from the output slot.")
.. "\n" ..
S("There are two possibilities to repair tools (and armor):") .. "\n" ..
S("• Tool + Tool: Place two tools of the same type in the input slots. The “health” of the repaired tool is the sum of the “health” of both input tools, plus a 12% bonus.")
.. "\n" ..
S("• Tool + Material: Some tools can also be repaired by combining them with an item that it's made of. For example, iron pickaxes can be repaired with iron ingots. This repairs the tool by 25%.")
.. "\n" ..
S("Armor counts as a tool. It is possible to repair and rename a tool in a single step.") .. "\n\n" ..
S("The anvil has limited durability and 3 damage levels: undamaged, slightly damaged and very damaged. Each time you repair or rename something, there is a 12% chance the anvil gets damaged. Anvils also have a chance of being damaged when they fall by more than 1 block. If a very damaged anvil is damaged again, it is destroyed.")
local anvildef1 = table.copy(anvildef)
anvildef1.description = S("Slightly Damaged Anvil")
anvildef1._doc_items_create_entry = false
anvildef1.groups.anvil = 2
anvildef1._doc_items_create_entry = false
anvildef1.tiles = {"mcl_anvils_anvil_top_damaged_1.png^[transformR90", "mcl_anvils_anvil_base.png", "mcl_anvils_anvil_side.png"}
anvildef1.tiles = { "mcl_anvils_anvil_top_damaged_1.png^[transformR90", "mcl_anvils_anvil_base.png",
"mcl_anvils_anvil_side.png" }
local anvildef2 = table.copy(anvildef)
anvildef2.description = S("Very Damaged Anvil")
anvildef2._doc_items_create_entry = false
anvildef2.groups.anvil = 3
anvildef2._doc_items_create_entry = false
anvildef2.tiles = {"mcl_anvils_anvil_top_damaged_2.png^[transformR90", "mcl_anvils_anvil_base.png", "mcl_anvils_anvil_side.png"}
anvildef2.tiles = { "mcl_anvils_anvil_top_damaged_2.png^[transformR90", "mcl_anvils_anvil_base.png",
"mcl_anvils_anvil_side.png" }
minetest.register_node("mcl_anvils:anvil", anvildef0)
minetest.register_node("mcl_anvils:anvil_damage_1", anvildef1)
@ -562,7 +596,7 @@ if minetest.get_modpath("mcl_core") then
{ "mcl_core:ironblock", "mcl_core:ironblock", "mcl_core:ironblock" },
{ "", "mcl_core:iron_ingot", "" },
{ "mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot" },
}
},
})
end

@ -8,6 +8,7 @@ local open_barrels = {}
local drop_content = mcl_util.drop_items_from_meta_container("main")
---@param pos Vector
local function on_blast(pos)
local node = minetest.get_node(pos)
drop_content(pos, node)
@ -45,30 +46,34 @@ local function barrel_open(pos, node, clicker)
local playername = clicker:get_player_name()
minetest.show_formspec(playername,
"mcl_barrels:barrel_"..pos.x.."_"..pos.y.."_"..pos.z,
"mcl_barrels:barrel_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z,
table.concat({
"size[9,8.75]",
"label[0,0;"..F(C("#313131", name)).."]",
"list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;0,0.5;9,3;]",
mcl_formspec.get_itemslot_bg(0, 0.5, 9, 3),
"label[0,4.0;"..F(C("#313131", S("Inventory"))).."]",
"list[current_player;main;0,4.5;9,3;9]",
mcl_formspec.get_itemslot_bg(0, 4.5, 9, 3),
"list[current_player;main;0,7.74;9,1;]",
mcl_formspec.get_itemslot_bg(0, 7.74, 9, 1),
"listring[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main]",
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3),
"list[nodemeta:" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ";main;0.375,0.75;9,3;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
"listring[nodemeta:" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ";main]",
"listring[current_player;main]",
})
)
minetest.swap_node(pos, { name = "mcl_barrels:barrel_open", param2 = node.param2 })
open_barrels[playername] = pos
minetest.sound_play({name="mcl_barrels_default_barrel_open", pos=pos, gain=0.5, max_hear_distance=16}, true)
minetest.sound_play({ name = "mcl_barrels_default_barrel_open", pos = pos, gain = 0.5, max_hear_distance = 16 }, true)
end
---@param pos Vector
local function close_forms(pos)
local players = minetest.get_connected_players()
local formname = "mcl_barrels:barrel_"..pos.x.."_"..pos.y.."_"..pos.z
local formname = "mcl_barrels:barrel_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z
for p = 1, #players do
if vector.distance(players[p]:get_pos(), pos) <= 30 then
minetest.close_formspec(players[p]:get_player_name(), formname)
@ -76,15 +81,18 @@ local function close_forms(pos)
end
end
---@param pos Vector
local function update_after_close(pos)
local node = minetest.get_node_or_nil(pos)
if not node then return end
if node.name == "mcl_barrels:barrel_open" then
minetest.swap_node(pos, {name = "mcl_barrels:barrel_closed", param2 = node.param2})
minetest.sound_play({name="mcl_barrels_default_barrel_close", pos=pos, gain=0.5, max_hear_distance=16}, true)
minetest.swap_node(pos, { name = "mcl_barrels:barrel_closed", param2 = node.param2 })
minetest.sound_play({ name = "mcl_barrels_default_barrel_close", pos = pos, gain = 0.5, max_hear_distance = 16 },
true)
end
end
---@param player ObjectRef
local function close_barrel(player)
local name = player:get_player_name()
local open = open_barrels[name]
@ -102,20 +110,22 @@ minetest.register_node("mcl_barrels:barrel_closed", {
_tt_help = S("27 inventory slots"),
_doc_items_longdesc = S("Barrels are containers which provide 27 inventory slots."),
_doc_items_usagehelp = S("To access its inventory, rightclick it. When broken, the items will drop out."),
tiles = {"mcl_barrels_barrel_top.png^[transformR270", "mcl_barrels_barrel_bottom.png", "mcl_barrels_barrel_side.png"},
tiles = { "mcl_barrels_barrel_top.png^[transformR270", "mcl_barrels_barrel_bottom.png", "mcl_barrels_barrel_side.png" },
paramtype = "light",
paramtype2 = "facedir",
on_place = function(itemstack, placer, pointed_thing)
minetest.rotate_and_place(itemstack, placer, pointed_thing, minetest.is_creative_enabled(placer:get_player_name()), {}, false)
minetest.rotate_and_place(itemstack, placer, pointed_thing,
minetest.is_creative_enabled(placer:get_player_name()), {}
, false)
return itemstack
end,
stack_max = 64,
sounds = mcl_sounds.node_sound_wood_defaults(),
groups = {handy = 1, axey = 1, container = 2, material_wood = 1, flammable = -1, deco_block = 1},
groups = { handy = 1, axey = 1, container = 2, material_wood = 1, flammable = -1, deco_block = 1 },
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("main", 9*3)
inv:set_size("main", 9 * 3)
end,
after_place_node = function(pos, placer, itemstack, pointed_thing)
minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name"))
@ -124,16 +134,16 @@ minetest.register_node("mcl_barrels:barrel_closed", {
allow_metadata_inventory_take = protection_check_put_take,
allow_metadata_inventory_put = protection_check_put_take,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", player:get_player_name()..
" moves stuff in barrel at "..minetest.pos_to_string(pos))
minetest.log("action", player:get_player_name() ..
" moves stuff in barrel at " .. minetest.pos_to_string(pos))
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name()..
" moves stuff to barrel at "..minetest.pos_to_string(pos))
minetest.log("action", player:get_player_name() ..
" moves stuff to barrel at " .. minetest.pos_to_string(pos))
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name()..
" takes stuff from barrel at "..minetest.pos_to_string(pos))
minetest.log("action", player:get_player_name() ..
" takes stuff from barrel at " .. minetest.pos_to_string(pos))
end,
after_dig_node = drop_content,
on_blast = on_blast,
@ -149,27 +159,35 @@ minetest.register_node("mcl_barrels:barrel_open", {
_doc_items_longdesc = S("Barrels are containers which provide 27 inventory slots."),
_doc_items_usagehelp = S("To access its inventory, rightclick it. When broken, the items will drop out."),
_doc_items_create_entry = false,
tiles = {"mcl_barrels_barrel_top_open.png", "mcl_barrels_barrel_bottom.png", "mcl_barrels_barrel_side.png"},
tiles = { "mcl_barrels_barrel_top_open.png", "mcl_barrels_barrel_bottom.png", "mcl_barrels_barrel_side.png" },
paramtype = "light",
paramtype2 = "facedir",
drop = "mcl_barrels:barrel_closed",
stack_max = 64,
sounds = mcl_sounds.node_sound_wood_defaults(),
groups = {handy = 1, axey = 1, container = 2, material_wood = 1, flammable = -1, deco_block = 1, not_in_creative_inventory = 1},
groups = {
handy = 1,
axey = 1,
container = 2,
material_wood = 1,
flammable = -1,
deco_block = 1,
not_in_creative_inventory = 1
},
allow_metadata_inventory_move = protection_check_move,
allow_metadata_inventory_take = protection_check_put_take,
allow_metadata_inventory_put = protection_check_put_take,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", player:get_player_name()..
" moves stuff in barrel at "..minetest.pos_to_string(pos))
minetest.log("action", player:get_player_name() ..
" moves stuff in barrel at " .. minetest.pos_to_string(pos))
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name()..
" moves stuff to barrel at "..minetest.pos_to_string(pos))
minetest.log("action", player:get_player_name() ..
" moves stuff to barrel at " .. minetest.pos_to_string(pos))
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name()..
" takes stuff from barrel at "..minetest.pos_to_string(pos))
minetest.log("action", player:get_player_name() ..
" takes stuff from barrel at " .. minetest.pos_to_string(pos))
end,
after_dig_node = drop_content,
on_blast = on_blast,
@ -193,10 +211,10 @@ end)
minetest.register_craft({
output = "mcl_barrels:barrel_closed",
recipe = {
{"group:wood", "group:wood_slab", "group:wood"},
{"group:wood", "", "group:wood"},
{"group:wood", "group:wood_slab", "group:wood"},
}
{ "group:wood", "group:wood_slab", "group:wood" },
{ "group:wood", "", "group:wood" },
{ "group:wood", "group:wood_slab", "group:wood" },
},
})
minetest.register_craft({

@ -1,5 +1,6 @@
local S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
local F = minetest.formspec_escape
local LIGHT_ACTIVE_FURNACE = 13
@ -8,60 +9,82 @@ local LIGHT_ACTIVE_FURNACE = 13
--
local function active_formspec(fuel_percent, item_percent)
return "size[9,8.75]"..
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Blast Furnace"))).."]"..
"list[context;src;2.75,0.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,0.5,1,1)..
"list[context;fuel;2.75,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1)..
"list[context;dst;5.75,1.5;1,1;]"..
mcl_formspec.get_itemslot_bg(5.75,1.5,1,1)..
"image[2.75,1.5;1,1;default_furnace_fire_bg.png^[lowpart:"..
(100-fuel_percent)..":default_furnace_fire_fg.png]"..
"image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[lowpart:"..
(item_percent)..":gui_furnace_arrow_fg.png^[transformR270]"..
return table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Blast Furnace"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
"list[context;src;3.5,0.75;1,1;]",
"image[3.5,2;1,1;default_furnace_fire_bg.png^[lowpart:" ..
(100 - fuel_percent) .. ":default_furnace_fire_fg.png]",
mcl_formspec.get_itemslot_bg_v4(3.5, 3.25, 1, 1),
"list[context;fuel;3.5,3.25;1,1;]",
"image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[lowpart:" ..
(item_percent) .. ":gui_furnace_arrow_fg.png^[transformR270]",
mcl_formspec.get_itemslot_bg_v4(7.875, 2, 1, 1, 0.2),
"list[context;dst;7.875,2;1,1;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
-- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed.
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]"
"listring[context;dst]",
"listring[current_player;main]",
"listring[context;src]",
"listring[current_player;main]",
"listring[context;fuel]",
"listring[current_player;main]",
})
end
local inactive_formspec = "size[9,8.75]"..
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Blast Furnace"))).."]"..
"list[context;src;2.75,0.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,0.5,1,1)..
"list[context;fuel;2.75,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1)..
"list[context;dst;5.75,1.5;1,1;]"..
mcl_formspec.get_itemslot_bg(5.75,1.5,1,1)..
"image[2.75,1.5;1,1;default_furnace_fire_bg.png]"..
"image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[transformR270]"..
local inactive_formspec = table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Blast Furnace"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
"list[context;src;3.5,0.75;1,1;]",
"image[3.5,2;1,1;default_furnace_fire_bg.png]",
mcl_formspec.get_itemslot_bg_v4(3.5, 3.25, 1, 1),
"list[context;fuel;3.5,3.25;1,1;]",
"image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[transformR270]",
mcl_formspec.get_itemslot_bg_v4(7.875, 2, 1, 1, 0.2),
"list[context;dst;7.875,2;1,1;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
-- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed.
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]"
"listring[context;dst]",
"listring[current_player;main]",
"listring[context;src]",
"listring[current_player;main]",
"listring[context;fuel]",
"listring[current_player;main]",
})
local receive_fields = function(pos, formname, fields, sender)
if fields.craftguide then
@ -71,7 +94,7 @@ end
local function give_xp(pos, player)
local meta = minetest.get_meta(pos)
local dir = vector.divide(minetest.facedir_to_dir(minetest.get_node(pos).param2),-1.95)
local dir = vector.divide(minetest.facedir_to_dir(minetest.get_node(pos).param2), -1.95)
local xp = meta:get_int("xp")
if xp > 0 then
if player then
@ -99,7 +122,7 @@ local function allow_metadata_inventory_put(pos, listname, index, stack, player)
-- Test stack with size 1 because we burn one fuel at a time
local teststack = ItemStack(stack)
teststack:set_count(1)
local output, decremented_input = minetest.get_craft_result({method="fuel", width=1, items={teststack}})
local output, decremented_input = minetest.get_craft_result({ method = "fuel", width = 1, items = { teststack } })
if output.time ~= 0 then
-- Only allow to place 1 item if fuel get replaced by recipe.
-- This is the case for lava buckets.
@ -160,17 +183,17 @@ local function spawn_flames(pos, param2)
local minrelpos, maxrelpos
local dir = minetest.facedir_to_dir(param2)
if dir.x > 0 then
minrelpos = { x = -0.6, y = -0.05, z = -0.25 }
maxrelpos = { x = -0.55, y = -0.45, z = 0.25 }
minrelpos = vector.new(-0.6, -0.05, -0.25)
maxrelpos = vector.new(-0.55, -0.45, 0.25)
elseif dir.x < 0 then
minrelpos = { x = 0.55, y = -0.05, z = -0.25 }
maxrelpos = { x = 0.6, y = -0.45, z = 0.25 }
minrelpos = vector.new(0.55, -0.05, -0.25)
maxrelpos = vector.new(0.6, -0.45, 0.25)
elseif dir.z > 0 then
minrelpos = { x = -0.25, y = -0.05, z = -0.6 }
maxrelpos = { x = 0.25, y = -0.45, z = -0.55 }
minrelpos = vector.new(-0.25, -0.05, -0.6)
maxrelpos = vector.new(0.25, 0.45, -0.55)
elseif dir.z < 0 then
minrelpos = { x = -0.25, y = -0.05, z = 0.55 }
maxrelpos = { x = 0.25, y = -0.45, z = 0.6 }
minrelpos = vector.new(-0.25, -0.05, 0.55)
maxrelpos = vector.new(0.25, -0.45, 0.6)
else
return
end
@ -179,8 +202,8 @@ local function spawn_flames(pos, param2)
time = 0,
minpos = vector.add(pos, minrelpos),
maxpos = vector.add(pos, maxrelpos),
minvel = { x = -0.01, y = 0, z = -0.01 },
maxvel = { x = 0.01, y = 0.1, z = 0.01 },
minvel = vector.new(-0.01, 0, -0.01),
maxvel = vector.new(0.01, 0.1, 0.01),
minexptime = 0.3,
maxexptime = 0.6,
minsize = 0.4,
@ -293,7 +316,7 @@ local function blast_furnace_node_timer(pos, elapsed)
-- Check if we have cookable content: cookable
local aftercooked
cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
cooked, aftercooked = minetest.get_craft_result({ method = "cooking", width = 1, items = srclist })
cookable = minetest.get_item_group(inv:get_stack("src", 1):get_name(), "blast_furnace_smeltable") == 1
if cookable then
-- Successful cooking requires space in dst slot and time
@ -311,7 +334,7 @@ local function blast_furnace_node_timer(pos, elapsed)
if cookable and not active then
-- We need to get new fuel
local afterfuel
fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
fuel, afterfuel = minetest.get_craft_result({ method = "fuel", width = 1, items = fuellist })
if fuel.time == 0 then
-- No valid fuel in fuel list -- stop
@ -415,12 +438,13 @@ end
minetest.register_node("mcl_blast_furnace:blast_furnace", {
description = S("Blast Furnace"),
_tt_help = S("Smelts ores faster than furnace"),
_doc_items_longdesc = S("Blast Furnaces smelt several items, mainly ores and armor, using a furnace fuel, but twice as fast as a normal furnace."),
_doc_items_longdesc = S(
"Blast Furnaces smelt several items, mainly ores and armor, using a furnace fuel, but twice as fast as a normal furnace."),
_doc_items_usagehelp =
S("Use the blast furnace to open the furnace menu.").."\n"..
S("Place a furnace fuel in the lower slot and the source material in the upper slot.").."\n"..
S("The blast furnace will slowly use its fuel to smelt the item.").."\n"..
S("The result will be placed into the output slot at the right side.").."\n"..
S("Use the blast furnace to open the furnace menu.") .. "\n" ..
S("Place a furnace fuel in the lower slot and the source material in the upper slot.") .. "\n" ..
S("The blast furnace will slowly use its fuel to smelt the item.") .. "\n" ..
S("The result will be placed into the output slot at the right side.") .. "\n" ..
S("Use the recipe book to see what ores you can smelt, what you can use as fuel and how long it will burn."),
_doc_items_hidden = false,
tiles = {
@ -429,7 +453,7 @@ minetest.register_node("mcl_blast_furnace:blast_furnace", {
"blast_furnace_side.png", "blast_furnace_front.png"
},
paramtype2 = "facedir",
groups = {pickaxey=1, container=4, deco_block=1, material_stone=1},
groups = { pickaxey = 1, container = 4, deco_block = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
@ -439,11 +463,10 @@ minetest.register_node("mcl_blast_furnace:blast_furnace", {
local meta2 = meta:to_table()
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for _, listname in ipairs({"src", "dst", "fuel"}) do
for _, listname in ipairs({ "src", "dst", "fuel" }) do
local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
minetest.add_item(p, stack)
minetest.add_item(vector.offset(pos, math.random(0, 10) / 10 - 0.5, 0, math.random(0, 10) / 10 - 0.5), stack)
end
end
meta:from_table(meta2)
@ -499,14 +522,16 @@ minetest.register_node("mcl_blast_furnace:blast_furnace_active", {
tiles = {
"blast_furnace_top.png", "blast_furnace_top.png",
"blast_furnace_side.png", "blast_furnace_side.png",
"blast_furnace_side.png", {name = "blast_furnace_front_on.png",
animation = {type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 48}},
"blast_furnace_side.png", {
name = "blast_furnace_front_on.png",
animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 48 }
},
},
paramtype2 = "facedir",
paramtype = "light",
light_source = LIGHT_ACTIVE_FURNACE,
drop = "mcl_blast_furnace:blast_furnace",
groups = {pickaxey=1, container=4, deco_block=1, not_in_creative_inventory=1, material_stone=1},
groups = { pickaxey = 1, container = 4, deco_block = 1, not_in_creative_inventory = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
on_timer = blast_furnace_node_timer,
@ -516,10 +541,14 @@ minetest.register_node("mcl_blast_furnace:blast_furnace_active", {
local meta2 = meta
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for _, listname in ipairs({"src", "dst", "fuel"}) do
for _, listname in ipairs({ "src", "dst", "fuel" }) do
local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
local p = vector.new(
pos.x + math.random(0, 10) / 10 - 0.5,
pos.y,
pos.z + math.random(0, 10) / 10 - 0.5
)
minetest.add_item(p, stack)
end
end
@ -564,10 +593,9 @@ end
minetest.register_lbm({
label = "Active blast_furnace flame particles",
name = "mcl_blast_furnace:flames",
nodenames = {"mcl_blast_furnace:blast_furnace_active"},
nodenames = { "mcl_blast_furnace:blast_furnace_active" },
run_at_every_load = true,
action = function(pos, node)
spawn_flames(pos, node.param2)
end,
})

@ -5,7 +5,7 @@ local C = minetest.colorize
local max_text_length = 4500 -- TODO: Increase to 12800 when scroll bar was added to written book
local max_title_length = 64
local bookshelf_inv = minetest.settings:get_bool("mcl_bookshelf_inventories",true)
local bookshelf_inv = minetest.settings:get_bool("mcl_bookshelf_inventories", true)
local header = ""
if minetest.get_modpath("mcl_init") then
@ -19,7 +19,7 @@ minetest.register_craftitem("mcl_books:book", {
_doc_items_longdesc = S("Books are used to make bookshelves and book and quills."),
inventory_image = "default_book.png",
stack_max = 64,
groups = { book=1, craftitem = 1, enchantability = 1 },
groups = { book = 1, craftitem = 1, enchantability = 1 },
_mcl_enchanting_enchanted_tool = "mcl_enchanting:book_enchanted",
})
@ -85,18 +85,19 @@ local function write(itemstack, user, pointed_thing)
local node = minetest.get_node(pointed_thing.under)
if user and not user:get_player_control().sneak then
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or
itemstack
end
end
end
local text = get_text(itemstack)
local formspec = "size[8,9]"..
header..
"background[-0.5,-0.5;9,10;mcl_books_book_bg.png]"..
"textarea[0.75,0.1;7.25,9;text;;"..minetest.formspec_escape(text).."]"..
"button[0.75,7.95;3,1;sign;"..minetest.formspec_escape(S("Sign")).."]"..
"button_exit[4.25,7.95;3,1;ok;"..minetest.formspec_escape(S("Done")).."]"
local formspec = "size[8,9]" ..
header ..
"background[-0.5,-0.5;9,10;mcl_books_book_bg.png]" ..
"textarea[0.75,0.1;7.25,9;text;;" .. minetest.formspec_escape(text) .. "]" ..
"button[0.75,7.95;3,1;sign;" .. minetest.formspec_escape(S("Sign")) .. "]" ..
"button_exit[4.25,7.95;3,1;ok;" .. minetest.formspec_escape(S("Done")) .. "]"
minetest.show_formspec(user:get_player_name(), "mcl_books:writable_book", formspec)
end
@ -106,17 +107,18 @@ local function read(itemstack, user, pointed_thing)
local node = minetest.get_node(pointed_thing.under)
if user and not user:get_player_control().sneak then
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or
itemstack
end
end
end
local text = get_text(itemstack)
local formspec = "size[8,9]"..
header..
"background[-0.5,-0.5;9,10;mcl_books_book_bg.png]"..
"textarea[0.75,0.1;7.25,9;;"..minetest.formspec_escape(text)..";]"..
"button_exit[2.25,7.95;3,1;ok;"..minetest.formspec_escape(S("Done")).."]"
local formspec = "size[8,9]" ..
header ..
"background[-0.5,-0.5;9,10;mcl_books_book_bg.png]" ..
"textarea[0.75,0.1;7.25,9;;" .. minetest.formspec_escape(text) .. ";]" ..
"button_exit[2.25,7.95;3,1;ok;" .. minetest.formspec_escape(S("Done")) .. "]"
minetest.show_formspec(user:get_player_name(), "mcl_books:written_book", formspec)
end
@ -125,16 +127,18 @@ minetest.register_craftitem("mcl_books:writable_book", {
description = S("Book and Quill"),
_tt_help = S("Write down some notes"),
_doc_items_longdesc = S("This item can be used to write down some notes."),
_doc_items_usagehelp = S("Hold it in the hand, then rightclick to read the current notes and edit then. You can edit the text as often as you like. You can also sign the book which turns it into a written book which you can stack, but it can't be edited anymore.").."\n"..
_doc_items_usagehelp = S(
"Hold it in the hand, then rightclick to read the current notes and edit then. You can edit the text as often as you like. You can also sign the book which turns it into a written book which you can stack, but it can't be edited anymore.")
.. "\n" ..
S("A book can hold up to 4500 characters. The title length is limited to 64 characters."),
inventory_image = "mcl_books_book_writable.png",
groups = { book=1 },
groups = { book = 1 },
stack_max = 1,
on_place = write,
on_secondary_use = write,
})
minetest.register_on_player_receive_fields(function ( player, formname, fields )
minetest.register_on_player_receive_fields(function(player, formname, fields)
if ((formname == "mcl_books:writable_book") and fields and fields.text) then
local stack = player:get_wielded_item()
if (stack:get_name() and (stack:get_name() == "mcl_books:writable_book")) then
@ -148,14 +152,17 @@ minetest.register_on_player_receive_fields(function ( player, formname, fields )
player:set_wielded_item(stack)
local name = player:get_player_name()
local formspec = "size[8,9]"..
header..
"background[-0.5,-0.5;9,10;mcl_books_book_bg.png]"..
"field[0.75,1;7.25,1;title;"..minetest.formspec_escape(minetest.colorize("#000000", S("Enter book title:")))..";]"..
"label[0.75,1.5;"..minetest.formspec_escape(minetest.colorize("#404040", S("by @1", name))).."]"..
"button_exit[0.75,7.95;3,1;sign;"..minetest.formspec_escape(S("Sign and Close")).."]"..
"tooltip[sign;"..minetest.formspec_escape(S("Note: The book will no longer be editable after signing")).."]"..
"button[4.25,7.95;3,1;cancel;"..minetest.formspec_escape(S("Cancel")).."]"
local formspec = "size[8,9]" ..
header ..
"background[-0.5,-0.5;9,10;mcl_books_book_bg.png]" ..
"field[0.75,1;7.25,1;title;" ..
minetest.formspec_escape(minetest.colorize("#000000", S("Enter book title:"))) .. ";]" ..
"label[0.75,1.5;" ..
minetest.formspec_escape(minetest.colorize("#404040", S("by @1", name))) .. "]" ..
"button_exit[0.75,7.95;3,1;sign;" .. minetest.formspec_escape(S("Sign and Close")) .. "]" ..
"tooltip[sign;" ..
minetest.formspec_escape(S("Note: The book will no longer be editable after signing")) .. "]" ..
"button[4.25,7.95;3,1;cancel;" .. minetest.formspec_escape(S("Cancel")) .. "]"
minetest.show_formspec(player:get_player_name(), "mcl_books:signing", formspec)
end
end
@ -181,7 +188,7 @@ minetest.register_on_player_receive_fields(function ( player, formname, fields )
player:set_wielded_item(newbook)
else
minetest.log("error", "[mcl_books] "..name.." failed to sign a book!")
minetest.log("error", "[mcl_books] " .. name .. " failed to sign a book!")
end
elseif ((formname == "mcl_books:signing") and fields and fields.cancel) then
local book = player:get_wielded_item()
@ -202,12 +209,16 @@ end
-- Written Book
minetest.register_craftitem("mcl_books:written_book", {
description = S("Written Book"),
_doc_items_longdesc = S("Written books contain some text written by someone. They can be read and copied, but not edited."),
_doc_items_usagehelp = S("Hold it in your hand, then rightclick to read the book.").."\n\n"..
S("To copy the text of the written book, place it into the crafting grid together with a book and quill (or multiple of those) and craft. The written book will not be consumed. Copies of copies can not be copied."),
_doc_items_longdesc = S(
"Written books contain some text written by someone. They can be read and copied, but not edited."
),
_doc_items_usagehelp = S("Hold it in your hand, then rightclick to read the book.") ..
"\n\n" ..
S(
"To copy the text of the written book, place it into the crafting grid together with a book and quill (or multiple of those) and craft. The written book will not be consumed. Copies of copies can not be copied."
),
inventory_image = "mcl_books_book_written.png",
groups = { not_in_creative_inventory=1, book=1, no_rename=1 },
groups = { not_in_creative_inventory = 1, book = 1, no_rename = 1 },
stack_max = 16,
on_place = read,
on_secondary_use = read
@ -219,19 +230,19 @@ S("To copy the text of the written book, place it into the crafting grid togethe
local baq = "mcl_books:writable_book"
local wb = "mcl_books:written_book"
local recipes = {
{wb, baq},
{baq, baq, wb},
{baq, baq, wb, baq},
{baq, baq, baq, baq, wb},
{baq, baq, baq, baq, wb, baq},
{baq, baq, baq, baq, wb, baq, baq},
{baq, baq, baq, baq, wb, baq, baq, baq},
{baq, baq, baq, baq, wb, baq, baq, baq, baq},
{ wb, baq },
{ baq, baq, wb },
{ baq, baq, wb, baq },
{ baq, baq, baq, baq, wb },
{ baq, baq, baq, baq, wb, baq },
{ baq, baq, baq, baq, wb, baq, baq },
{ baq, baq, baq, baq, wb, baq, baq, baq },
{ baq, baq, baq, baq, wb, baq, baq, baq, baq },
}
for r=#recipes, 1, -1 do
for r = #recipes, 1, -1 do
minetest.register_craft({
type = "shapeless",
output = "mcl_books:written_book "..r,
output = "mcl_books:written_book " .. r,
recipe = recipes[r],
})
end
@ -367,6 +378,9 @@ local function protection_check_put_take(pos, listname, index, stack, player)
end
end
---@param pos Vector
---@param node node
---@param clicker ObjectRef
local function bookshelf_gui(pos, node, clicker)
if not bookshelf_inv then return end
local name = minetest.get_meta(pos):get_string("name")
@ -378,18 +392,22 @@ local function bookshelf_gui(pos, node, clicker)
local playername = clicker:get_player_name()
minetest.show_formspec(playername,
"mcl_books:bookshelf_"..pos.x.."_"..pos.y.."_"..pos.z,
"mcl_books:bookshelf_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z,
table.concat({
"size[9,8.75]",
"label[0,0;"..F(C("#313131", name)).."]",
"list[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main;0,0.5;9,3;]",
mcl_formspec.get_itemslot_bg(0, 0.5, 9, 3),
"label[0,4.0;"..F(C("#313131", S("Inventory"))).."]",
"list[current_player;main;0,4.5;9,3;9]",
mcl_formspec.get_itemslot_bg(0, 4.5, 9, 3),
"list[current_player;main;0,7.74;9,1;]",
mcl_formspec.get_itemslot_bg(0, 7.74, 9, 1),
"listring[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main]",
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3),
mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3, 0, "mcl_book_book_empty_slot.png"),
"list[nodemeta:" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ";main;0.375,0.75;9,3;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
"listring[nodemeta:" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ";main]",
"listring[current_player;main]",
})
)
@ -397,7 +415,7 @@ end
local function close_forms(pos)
local players = minetest.get_connected_players()
local formname = "mcl_books:bookshelf_"..pos.x.."_"..pos.y.."_"..pos.z
local formname = "mcl_books:bookshelf_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z
for p = 1, #players do
if vector.distance(players[p]:get_pos(), pos) <= 30 then
minetest.close_formspec(players[p]:get_player_name(), formname)
@ -409,12 +427,18 @@ end
minetest.register_node("mcl_books:bookshelf", {
description = S("Bookshelf"),
_doc_items_longdesc = S("Bookshelves are used for decoration."),
tiles = {"mcl_books_bookshelf_top.png", "mcl_books_bookshelf_top.png", "default_bookshelf.png"},
tiles = { "mcl_books_bookshelf_top.png", "mcl_books_bookshelf_top.png", "default_bookshelf.png" },
stack_max = 64,
is_ground_content = false,
groups = {
handy=1, axey=1, deco_block=1, material_wood=1,
flammable=3, fire_encouragement=30, fire_flammability=20, container=1
handy = 1,
axey = 1,
deco_block = 1,
material_wood = 1,
flammable = 3,
fire_encouragement = 30,
fire_flammability = 20,
container = 1
},
drop = "mcl_books:book 3",
sounds = wood_sound,
@ -424,7 +448,7 @@ minetest.register_node("mcl_books:bookshelf", {
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("main", 9*3)
inv:set_size("main", 9 * 3)
end,
after_place_node = function(pos, placer, itemstack, pointed_thing)
minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name"))
@ -433,16 +457,16 @@ minetest.register_node("mcl_books:bookshelf", {
allow_metadata_inventory_take = protection_check_put_take,
allow_metadata_inventory_put = protection_check_put_take,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", player:get_player_name()..
" moves stuff in bookshelf at "..minetest.pos_to_string(pos))
minetest.log("action", player:get_player_name() ..
" moves stuff in bookshelf at " .. minetest.pos_to_string(pos))
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name()..
" moves stuff to bookshelf at "..minetest.pos_to_string(pos))
minetest.log("action", player:get_player_name() ..
" moves stuff to bookshelf at " .. minetest.pos_to_string(pos))
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name()..
" takes stuff from bookshelf at "..minetest.pos_to_string(pos))
minetest.log("action", player:get_player_name() ..
" takes stuff from bookshelf at " .. minetest.pos_to_string(pos))
end,
after_dig_node = drop_content,
on_blast = on_blast,
@ -453,9 +477,9 @@ minetest.register_node("mcl_books:bookshelf", {
minetest.register_craft({
output = "mcl_books:bookshelf",
recipe = {
{"group:wood", "group:wood", "group:wood"},
{"mcl_books:book", "mcl_books:book", "mcl_books:book"},
{"group:wood", "group:wood", "group:wood"},
{ "group:wood", "group:wood", "group:wood" },
{ "mcl_books:book", "mcl_books:book", "mcl_books:book" },
{ "group:wood", "group:wood", "group:wood" },
}
})
@ -464,4 +488,3 @@ minetest.register_craft({
recipe = "mcl_books:bookshelf",
burntime = 15,
})

File diff suppressed because it is too large Load Diff

@ -1,34 +1,51 @@
---@diagnostic disable lowercase-global
local S = minetest.get_translator(minetest.get_current_modname())
local formspec_escape = minetest.formspec_escape
local show_formspec = minetest.show_formspec
local F = minetest.formspec_escape
local C = minetest.colorize
local text_color = "#313131"
local itemslot_bg = mcl_formspec.get_itemslot_bg
local show_formspec = minetest.show_formspec
mcl_crafting_table = {}
function mcl_crafting_table.show_crafting_form(player)
player:get_inventory():set_width("craft", 3)
player:get_inventory():set_size("craft", 9)
mcl_crafting_table.formspec = table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
show_formspec(player:get_player_name(), "main",
"size[9,8.75]"..
"image[4.7,1.5;1.5,1;gui_crafting_arrow.png]"..
"label[0,4;"..formspec_escape(C(text_color, S("Inventory"))).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
itemslot_bg(0,7.74,9,1)..
"label[1.75,0;"..formspec_escape(C(text_color, S("Crafting"))).."]"..
"list[current_player;craft;1.75,0.5;3,3;]"..
itemslot_bg(1.75,0.5,3,3)..
"list[current_player;craftpreview;6.1,1.5;1,1;]"..
itemslot_bg(6.1,1.5,1,1)..
"image_button[0.75,1.5;1,1;craftguide_book.png;__mcl_craftguide;]"..
"tooltip[__mcl_craftguide;"..formspec_escape(S("Recipe book")).."]"..
"listring[current_player;main]"..
"listring[current_player;craft]"
)
"label[2.25,0.375;" .. F(C(mcl_formspec.label_color, S("Crafting"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(2.25, 0.75, 3, 3),
"list[current_player;craft;2.25,0.75;3,3;]",
"image[6.125,2;1.5,1;gui_crafting_arrow.png]",
mcl_formspec.get_itemslot_bg_v4(8.2, 2, 1, 1, 0.2),
"list[current_player;craftpreview;8.2,2;1,1;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
"listring[current_player;craft]",
"listring[current_player;main]",
--Crafting guide button
"image_button[0.325,1.95;1.1,1.1;craftguide_book.png;__mcl_craftguide;]",
"tooltip[__mcl_craftguide;" .. F(S("Recipe book")) .. "]",
})
---@param player ObjectRef
function mcl_crafting_table.show_crafting_form(player)
local inv = player:get_inventory()
if inv then
inv:set_width("craft", 3)
inv:set_size("craft", 9)
end
show_formspec(player:get_player_name(), "main", mcl_crafting_table.formspec)
end
minetest.register_node("mcl_crafting_table:crafting_table", {
@ -38,10 +55,10 @@ minetest.register_node("mcl_crafting_table:crafting_table", {
_doc_items_usagehelp = S("Rightclick the crafting table to access the 3×3 crafting grid."),
_doc_items_hidden = false,
is_ground_content = false,
tiles = {"crafting_workbench_top.png", "default_wood.png", "crafting_workbench_side.png",
"crafting_workbench_side.png", "crafting_workbench_front.png", "crafting_workbench_front.png"},
tiles = { "crafting_workbench_top.png", "default_wood.png", "crafting_workbench_side.png",
"crafting_workbench_side.png", "crafting_workbench_front.png", "crafting_workbench_front.png" },
paramtype2 = "facedir",
groups = {handy=1,axey=1, deco_block=1, material_wood=1,flammable=-1},
groups = { handy = 1, axey = 1, deco_block = 1, material_wood = 1, flammable = -1 },
on_rightclick = function(pos, node, player, itemstack)
if not player:get_player_control().sneak then
mcl_crafting_table.show_crafting_form(player)
@ -55,9 +72,9 @@ minetest.register_node("mcl_crafting_table:crafting_table", {
minetest.register_craft({
output = "mcl_crafting_table:crafting_table",
recipe = {
{"group:wood", "group:wood"},
{"group:wood", "group:wood"}
}
{ "group:wood", "group:wood" },
{ "group:wood", "group:wood" }
},
})
minetest.register_craft({

@ -2,7 +2,8 @@ local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape
function mcl_enchanting.is_book(itemname)
return itemname == "mcl_books:book" or itemname == "mcl_enchanting:book_enchanted" or itemname == "mcl_books:book_enchanted"
return itemname == "mcl_books:book" or itemname == "mcl_enchanting:book_enchanted" or
itemname == "mcl_books:book_enchanted"
end
function mcl_enchanting.get_enchantments(itemstack)
@ -54,11 +55,13 @@ end
function mcl_enchanting.get_enchantment_description(enchantment, level)
local enchantment_def = mcl_enchanting.enchantments[enchantment]
return enchantment_def.name .. (enchantment_def.max_level == 1 and "" or " " .. mcl_enchanting.roman_numerals.toRoman(level))
return enchantment_def.name ..
(enchantment_def.max_level == 1 and "" or " " .. mcl_enchanting.roman_numerals.toRoman(level))
end
function mcl_enchanting.get_colorized_enchantment_description(enchantment, level)
return minetest.colorize(mcl_enchanting.enchantments[enchantment].curse and mcl_colors.RED or mcl_colors.GRAY, mcl_enchanting.get_enchantment_description(enchantment, level))
return minetest.colorize(mcl_enchanting.enchantments[enchantment].curse and mcl_colors.RED or mcl_colors.GRAY,
mcl_enchanting.get_enchantment_description(enchantment, level))
end
function mcl_enchanting.get_enchanted_itemstring(itemname)
@ -79,7 +82,8 @@ function mcl_enchanting.not_enchantable_on_enchanting_table(itemname)
end
function mcl_enchanting.is_enchantable(itemname)
return mcl_enchanting.get_enchantability(itemname) > 0 or mcl_enchanting.not_enchantable_on_enchanting_table(itemname)
return mcl_enchanting.get_enchantability(itemname) > 0 or
mcl_enchanting.not_enchantable_on_enchanting_table(itemname)
end
function mcl_enchanting.can_enchant_freshly(itemname)
@ -150,7 +154,8 @@ function mcl_enchanting.can_enchant(itemstack, enchantment, level)
for incompatible in pairs(enchantment_def.incompatible) do
local incompatible_level = item_enchantments[incompatible]
if incompatible_level then
return false, "incompatible", mcl_enchanting.get_enchantment_description(incompatible, incompatible_level)
return false, "incompatible",
mcl_enchanting.get_enchantment_description(incompatible, incompatible_level)
end
end
end
@ -169,7 +174,9 @@ function mcl_enchanting.combine(itemstack, combine_with)
local itemname = itemstack:get_name()
local combine_name = combine_with:get_name()
local enchanted_itemname = mcl_enchanting.get_enchanted_itemstring(itemname)
if not enchanted_itemname or enchanted_itemname ~= mcl_enchanting.get_enchanted_itemstring(combine_name) and not mcl_enchanting.is_book(combine_name) then
if not enchanted_itemname or
enchanted_itemname ~= mcl_enchanting.get_enchanted_itemstring(combine_name) and
not mcl_enchanting.is_book(combine_name) then
return false
end
local enchantments = mcl_enchanting.get_enchantments(itemstack)
@ -267,7 +274,7 @@ function mcl_enchanting.initialize()
for itemname, itemdef in pairs(minetest.registered_items) do
if mcl_enchanting.can_enchant_freshly(itemname) and not mcl_enchanting.is_book(itemname) then
local new_name = itemname .. "_enchanted"
minetest.override_item(itemname, {_mcl_enchanting_enchanted_tool = new_name})
minetest.override_item(itemname, { _mcl_enchanting_enchanted_tool = new_name })
local new_def = table.copy(itemdef)
new_def.inventory_image = itemdef.inventory_image .. mcl_enchanting.overlay
if new_def.wield_image then
@ -303,7 +310,7 @@ end
function mcl_enchanting.random(pr, ...)
local r = pr and pr:next(...) or math.random(...)
if pr and not ({...})[1] then
if pr and not ({ ... })[1] then
r = r / 32767
end
@ -328,20 +335,24 @@ function mcl_enchanting.get_random_enchantment(itemstack, treasure, weighted, ex
return #possible > 0 and possible[mcl_enchanting.random(pr, 1, #possible)]
end
function mcl_enchanting.generate_random_enchantments(itemstack, enchantment_level, treasure, no_reduced_bonus_chance, ignore_already_enchanted, pr)
function mcl_enchanting.generate_random_enchantments(itemstack, enchantment_level, treasure, no_reduced_bonus_chance,
ignore_already_enchanted, pr)
local itemname = itemstack:get_name()
if (not mcl_enchanting.can_enchant_freshly(itemname) and not ignore_already_enchanted) or mcl_enchanting.not_enchantable_on_enchanting_table(itemname) then
if (not mcl_enchanting.can_enchant_freshly(itemname) and not ignore_already_enchanted) or
mcl_enchanting.not_enchantable_on_enchanting_table(itemname) then
return
end
itemstack = ItemStack(itemstack)
local enchantability = minetest.get_item_group(itemname, "enchantability")
enchantability = 1 + mcl_enchanting.random(pr, 0, math.floor(enchantability / 4)) + mcl_enchanting.random(pr, 0, math.floor(enchantability / 4))
enchantability = 1 + mcl_enchanting.random(pr, 0, math.floor(enchantability / 4)) +
mcl_enchanting.random(pr, 0, math.floor(enchantability / 4))
enchantment_level = enchantment_level + enchantability
enchantment_level = enchantment_level + enchantment_level * (mcl_enchanting.random(pr) + mcl_enchanting.random(pr) - 1) * 0.15
enchantment_level = enchantment_level +
enchantment_level * (mcl_enchanting.random(pr) + mcl_enchanting.random(pr) - 1) * 0.15
enchantment_level = math.max(math.floor(enchantment_level + 0.5), 1)
local enchantments = {}
@ -387,7 +398,6 @@ function mcl_enchanting.generate_random_enchantments(itemstack, enchantment_leve
enchantments[selected_enchantment] = enchantment_power
mcl_enchanting.enchant(itemstack, selected_enchantment, enchantment_power)
end
until not no_reduced_bonus_chance and mcl_enchanting.random(pr) >= (enchantment_level + 1) / 50
return enchantments, description
@ -397,13 +407,15 @@ function mcl_enchanting.generate_random_enchantments_reliable(itemstack, enchant
local enchantments
repeat
enchantments = mcl_enchanting.generate_random_enchantments(itemstack, enchantment_level, treasure, no_reduced_bonus_chance, ignore_already_enchanted, pr)
enchantments = mcl_enchanting.generate_random_enchantments(itemstack, enchantment_level, treasure,
no_reduced_bonus_chance, ignore_already_enchanted, pr)
until enchantments
return enchantments
end
function mcl_enchanting.enchant_randomly(itemstack, enchantment_level, treasure, no_reduced_bonus_chance, ignore_already_enchanted, pr)
function mcl_enchanting.enchant_randomly(itemstack, enchantment_level, treasure, no_reduced_bonus_chance,
ignore_already_enchanted, pr)
local enchantments = mcl_enchanting.generate_random_enchantments_reliable(itemstack, enchantment_level, treasure, no_reduced_bonus_chance, ignore_already_enchanted, pr)
mcl_enchanting.set_enchanted_itemstring(itemstack)
@ -416,7 +428,8 @@ function mcl_enchanting.enchant_uniform_randomly(stack, exclude, pr)
local enchantment = mcl_enchanting.get_random_enchantment(stack, true, false, exclude, pr)
if enchantment then
mcl_enchanting.enchant(stack, enchantment, mcl_enchanting.random(pr, 1, mcl_enchanting.enchantments[enchantment].max_level))
mcl_enchanting.enchant(stack, enchantment,
mcl_enchanting.random(pr, 1, mcl_enchanting.enchantments[enchantment].max_level))
end
return stack
@ -426,7 +439,8 @@ function mcl_enchanting.get_random_glyph_row()
local glyphs = ""
local x = 1.3
for i = 1, 9 do
glyphs = glyphs .. "image[".. x .. ",0.1;0.5,0.5;mcl_enchanting_glyph_" .. math.random(18) .. ".png^[colorize:#675D49:255]"
glyphs = glyphs ..
"image[" .. x .. ",0.1;0.5,0.5;mcl_enchanting_glyph_" .. math.random(18) .. ".png^[colorize:#675D49:255]"
x = x + 0.6
end
return glyphs
@ -459,7 +473,7 @@ end
function mcl_enchanting.get_table_slots(player, itemstack, num_bookshelves)
local itemname = itemstack:get_name()
if (not mcl_enchanting.can_enchant_freshly(itemname)) or mcl_enchanting.not_enchantable_on_enchanting_table(itemname) then
return {false, false, false}
return { false, false, false }
end
local meta = player:get_meta()
local player_slots = minetest.deserialize(meta:get_string("mcl_enchanting:slots")) or {}
@ -475,7 +489,7 @@ function mcl_enchanting.get_table_slots(player, itemstack, num_bookshelves)
meta:set_string("mcl_enchanting:slots", minetest.serialize(player_slots))
return player_bookshelves_item_slots
else
return {false, false, false}
return { false, false, false }
end
end
end
@ -491,28 +505,33 @@ function mcl_enchanting.show_enchanting_formspec(player)
local inv = player:get_inventory()
local num_bookshelves = meta:get_int("mcl_enchanting:num_bookshelves")
local table_name = meta:get_string("mcl_enchanting:table_name")
local formspec = ""
.. "size[9.07,8.6;]"
.. "formspec_version[3]"
.. "label[0,0;" .. C("#313131") .. F(table_name) .. "]"
.. mcl_formspec.get_itemslot_bg(0.2, 2.4, 1, 1)
.. "list[current_player;enchanting_item;0.2,2.4;1,1]"
.. mcl_formspec.get_itemslot_bg(1.1, 2.4, 1, 1)
.. "image[1.1,2.4;1,1;mcl_enchanting_lapis_background.png]"
.. "list[current_player;enchanting_lapis;1.1,2.4;1,1]"
.. "label[0,4;" .. C("#313131") .. F(S("Inventory")).."]"
.. mcl_formspec.get_itemslot_bg(0, 4.5, 9, 3)
.. mcl_formspec.get_itemslot_bg(0, 7.74, 9, 1)
.. "list[current_player;main;0,4.5;9,3;9]"
.. "listring[current_player;enchanting_item]"
.. "listring[current_player;main]"
.. "listring[current_player;enchanting]"
.. "listring[current_player;main]"
.. "listring[current_player;enchanting_lapis]"
.. "listring[current_player;main]"
.. "list[current_player;main;0,7.74;9,1;]"
.. "real_coordinates[true]"
.. "image[3.15,0.6;7.6,4.1;mcl_enchanting_button_background.png]"
local formspec = table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color) .. table_name) .. "]",
mcl_formspec.get_itemslot_bg_v4(1, 3.25, 1, 1),
"list[current_player;enchanting_item;1,3.25;1,1]",
mcl_formspec.get_itemslot_bg_v4(2.25, 3.25, 1, 1),
"image[2.25,3.25;1,1;mcl_enchanting_lapis_background.png]",
"list[current_player;enchanting_lapis;2.25,3.25;1,1]",
"image[4.125,0.56;7.25,4.1;mcl_enchanting_button_background.png]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color) .. S("Inventory")) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
"listring[current_player;enchanting_item]",
"listring[current_player;main]",
"listring[current_player;enchanting]",
"listring[current_player;main]",
"listring[current_player;enchanting_lapis]",
"listring[current_player;main]",
})
local itemstack = inv:get_stack("enchanting_item", 1)
local player_levels = mcl_experience.get_level(player)
local y = 0.65
@ -520,24 +539,50 @@ function mcl_enchanting.show_enchanting_formspec(player)
local table_slots = mcl_enchanting.get_table_slots(player, itemstack, num_bookshelves)
for i, slot in ipairs(table_slots) do
any_enchantment = any_enchantment or slot
local enough_lapis = inv:contains_item("enchanting_lapis", ItemStack({name = "mcl_core:lapis", count = i}))
local enough_lapis = inv:contains_item("enchanting_lapis", ItemStack({ name = "mcl_core:lapis", count = i }))
local enough_levels = slot and slot.level_requirement <= player_levels
local can_enchant = (slot and enough_lapis and enough_levels)
local ending = (can_enchant and "" or "_off")
local hover_ending = (can_enchant and "_hovered" or "_off")
formspec = formspec
.. "container[3.2," .. y .. "]"
.. (slot and "tooltip[button_" .. i .. ";" .. C("#818181") .. ((slot.description and F(slot.description)) or "") .. " " .. C("#FFFFFF") .. " . . . ?\n\n" .. (enough_levels and C(enough_lapis and "#818181" or "#FC5454") .. F(S("@1 Lapis Lazuli", i)) .. "\n" .. C("#818181") .. F(S("@1 Enchantment Levels", i)) or C("#FC5454") .. F(S("Level requirement: @1", slot.level_requirement))) .. "]" or "")
.. "style[button_" .. i .. ";bgimg=mcl_enchanting_button" .. ending .. ".png;bgimg_hovered=mcl_enchanting_button" .. hover_ending .. ".png;bgimg_pressed=mcl_enchanting_button" .. hover_ending .. ".png]"
.. "button[0,0;7.5,1.3;button_" .. i .. ";]"
.. "container[4.125," .. y .. "]"
..
(
slot and
"tooltip[button_" ..
i ..
";" ..
C("#818181") ..
((slot.description and F(slot.description)) or "") ..
" " ..
C("#FFFFFF") ..
" . . . ?\n\n" ..
(
enough_levels and
C(enough_lapis and "#818181" or "#FC5454") ..
F(S("@1 Lapis Lazuli", i)) .. "\n" .. C("#818181") .. F(S("@1 Enchantment Levels", i)) or
C("#FC5454") .. F(S("Level requirement: @1", slot.level_requirement))) .. "]" or "")
..
"style[button_" ..
i ..
";bgimg=mcl_enchanting_button" ..
ending ..
".png;bgimg_hovered=mcl_enchanting_button" ..
hover_ending .. ".png;bgimg_pressed=mcl_enchanting_button" .. hover_ending .. ".png]"
.. "button[0,0;7.25,1.3;button_" .. i .. ";]"
.. (slot and "image[0,0;1.3,1.3;mcl_enchanting_number_" .. i .. ending .. ".png]" or "")
.. (slot and "label[7.2,1.1;" .. C(can_enchant and "#80FF20" or "#407F10") .. slot.level_requirement .. "]" or "")
.. (slot and "label[6.8,1;" .. C(can_enchant and "#80FF20" or "#407F10") .. slot.level_requirement .. "]" or "")
.. (slot and slot.glyphs or "")
.. "container_end[]"
y = y + 1.35
y = y + 1.3
end
formspec = formspec
.. "image[" .. (any_enchantment and 0.58 or 1.15) .. ",1.2;" .. (any_enchantment and 2 or 0.87) .. ",1.43;mcl_enchanting_book_" .. (any_enchantment and "open" or "closed") .. ".png]"
..
"image[" ..
(any_enchantment and 1.1 or 1.67) ..
",1.2;" ..
(any_enchantment and 2 or 0.87) ..
",1.43;mcl_enchanting_book_" .. (any_enchantment and "open" or "closed") .. ".png]"
minetest.show_formspec(name, "mcl_enchanting:table", formspec)
end
@ -555,7 +600,7 @@ function mcl_enchanting.handle_formspec_fields(player, formname, fields)
local meta = player:get_meta()
local num_bookshelfes = meta:get_int("mcl_enchanting:num_bookshelves")
local itemstack = inv:get_stack("enchanting_item", 1)
local cost = ItemStack({name = "mcl_core:lapis", count = button_pressed})
local cost = ItemStack({ name = "mcl_core:lapis", count = button_pressed })
if not inv:contains_item("enchanting_lapis", cost) then
return
end
@ -573,7 +618,7 @@ function mcl_enchanting.handle_formspec_fields(player, formname, fields)
mcl_enchanting.set_enchanted_itemstring(itemstack)
mcl_enchanting.set_enchantments(itemstack, slot.enchantments)
inv:set_stack("enchanting_item", 1, itemstack)
minetest.sound_play("mcl_enchanting_enchant", {to_player = name, gain = 5.0})
minetest.sound_play("mcl_enchanting_enchant", { to_player = name, gain = 5.0 })
mcl_enchanting.reset_table_slots(player)
mcl_enchanting.show_enchanting_formspec(player)
awards.unlock(player:get_player_name(), "mcl:enchanter")
@ -603,7 +648,8 @@ function mcl_enchanting.is_enchanting_inventory_action(action, inventory, invent
end
function mcl_enchanting.allow_inventory_action(player, action, inventory, inventory_info)
local is_enchanting_action, do_limit = mcl_enchanting.is_enchanting_inventory_action(action, inventory, inventory_info)
local is_enchanting_action, do_limit = mcl_enchanting.is_enchanting_inventory_action(action, inventory,
inventory_info)
if is_enchanting_action and do_limit then
if action == "move" then
local listname = inventory_info.to_list
@ -645,13 +691,15 @@ function mcl_enchanting.on_inventory_action(player, action, inventory, inventory
end
function mcl_enchanting.schedule_book_animation(self, anim)
self.scheduled_anim = {timer = self.anim_length, anim = anim}
self.scheduled_anim = { timer = self.anim_length, anim = anim }
end
function mcl_enchanting.set_book_animation(self, anim)
local anim_index = mcl_enchanting.book_animations[anim]
local start, stop = mcl_enchanting.book_animation_steps[anim_index], mcl_enchanting.book_animation_steps[anim_index + 1]
self.object:set_animation({x = start, y = stop}, mcl_enchanting.book_animation_speed, 0, mcl_enchanting.book_animation_loop[anim] or false)
local start, stop = mcl_enchanting.book_animation_steps[anim_index],
mcl_enchanting.book_animation_steps[anim_index + 1]
self.object:set_animation({ x = start, y = stop }, mcl_enchanting.book_animation_speed, 0,
mcl_enchanting.book_animation_loop[anim] or false)
self.scheduled_anim = nil
self.anim_length = (stop - start) / 40
end
@ -669,7 +717,7 @@ end
function mcl_enchanting.look_at(self, pos2)
local pos1 = self.object:get_pos()
local vec = vector.subtract(pos1, pos2)
local yaw = math.atan(vec.z / vec.x) - math.pi/2
local yaw = math.atan(vec.z / vec.x) - math.pi / 2
yaw = yaw + (pos1.x >= pos2.x and math.pi or 0)
self.object:set_yaw(yaw + math.pi)
end

@ -1,5 +1,6 @@
local S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
local F = minetest.formspec_escape
local LIGHT_ACTIVE_FURNACE = 13
@ -8,60 +9,82 @@ local LIGHT_ACTIVE_FURNACE = 13
--
local function active_formspec(fuel_percent, item_percent)
return "size[9,8.75]"..
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Furnace"))).."]"..
"list[context;src;2.75,0.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,0.5,1,1)..
"list[context;fuel;2.75,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1)..
"list[context;dst;5.75,1.5;1,1;]"..
mcl_formspec.get_itemslot_bg(5.75,1.5,1,1)..
"image[2.75,1.5;1,1;default_furnace_fire_bg.png^[lowpart:"..
(100-fuel_percent)..":default_furnace_fire_fg.png]"..
"image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[lowpart:"..
(item_percent)..":gui_furnace_arrow_fg.png^[transformR270]"..
return table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Furnace"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
"list[context;src;3.5,0.75;1,1;]",
"image[3.5,2;1,1;default_furnace_fire_bg.png^[lowpart:" ..
(100 - fuel_percent) .. ":default_furnace_fire_fg.png]",
mcl_formspec.get_itemslot_bg_v4(3.5, 3.25, 1, 1),
"list[context;fuel;3.5,3.25;1,1;]",
"image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[lowpart:" ..
(item_percent) .. ":gui_furnace_arrow_fg.png^[transformR270]",
mcl_formspec.get_itemslot_bg_v4(7.875, 2, 1, 1, 0.2),
"list[context;dst;7.875,2;1,1;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
-- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed.
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]"
"listring[context;dst]",
"listring[current_player;main]",
"listring[context;src]",
"listring[current_player;main]",
"listring[context;fuel]",
"listring[current_player;main]",
})
end
local inactive_formspec = "size[9,8.75]"..
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Furnace"))).."]"..
"list[context;src;2.75,0.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,0.5,1,1)..
"list[context;fuel;2.75,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1)..
"list[context;dst;5.75,1.5;1,1;]"..
mcl_formspec.get_itemslot_bg(5.75,1.5,1,1)..
"image[2.75,1.5;1,1;default_furnace_fire_bg.png]"..
"image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[transformR270]"..
local inactive_formspec = table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Furnace"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
"list[context;src;3.5,0.75;1,1;]",
"image[3.5,2;1,1;default_furnace_fire_bg.png]",
mcl_formspec.get_itemslot_bg_v4(3.5, 3.25, 1, 1),
"list[context;fuel;3.5,3.25;1,1;]",
"image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[transformR270]",
mcl_formspec.get_itemslot_bg_v4(7.875, 2, 1, 1, 0.2),
"list[context;dst;7.875,2;1,1;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
-- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed.
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]"
"listring[context;dst]",
"listring[current_player;main]",
"listring[context;src]",
"listring[current_player;main]",
"listring[context;fuel]",
"listring[current_player;main]",
})
local receive_fields = function(pos, formname, fields, sender)
if fields.craftguide then
@ -71,7 +94,7 @@ end
local function give_xp(pos, player)
local meta = minetest.get_meta(pos)
local dir = vector.divide(minetest.facedir_to_dir(minetest.get_node(pos).param2),-1.95)
local dir = vector.divide(minetest.facedir_to_dir(minetest.get_node(pos).param2), -1.95)
local xp = meta:get_int("xp")
if xp > 0 then
if player then
@ -108,7 +131,7 @@ local function allow_metadata_inventory_put(pos, listname, index, stack, player)
-- Test stack with size 1 because we burn one fuel at a time
local teststack = ItemStack(stack)
teststack:set_count(1)
local output, decremented_input = minetest.get_craft_result({method="fuel", width=1, items={teststack}})
local output, decremented_input = minetest.get_craft_result({ method = "fuel", width = 1, items = { teststack } })
if output.time ~= 0 then
-- Only allow to place 1 item if fuel get replaced by recipe.
-- This is the case for lava buckets.
@ -303,7 +326,7 @@ local function furnace_node_timer(pos, elapsed)
-- Check if we have cookable content: cookable
local aftercooked
cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
cooked, aftercooked = minetest.get_craft_result({ method = "cooking", width = 1, items = srclist })
cookable = cooked.time ~= 0
if cookable then
-- Successful cooking requires space in dst slot and time
@ -321,7 +344,7 @@ local function furnace_node_timer(pos, elapsed)
if cookable and not active then
-- We need to get new fuel
local afterfuel
fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
fuel, afterfuel = minetest.get_craft_result({ method = "fuel", width = 1, items = fuellist })
if fuel.time == 0 then
-- No valid fuel in fuel list -- stop
@ -440,10 +463,10 @@ minetest.register_node("mcl_furnaces:furnace", {
_tt_help = S("Uses fuel to smelt or cook items"),
_doc_items_longdesc = S("Furnaces cook or smelt several items, using a furnace fuel, into something else."),
_doc_items_usagehelp =
S("Use the furnace to open the furnace menu.").."\n"..
S("Place a furnace fuel in the lower slot and the source material in the upper slot.").."\n"..
S("The furnace will slowly use its fuel to smelt the item.").."\n"..
S("The result will be placed into the output slot at the right side.").."\n"..
S("Use the furnace to open the furnace menu.") .. "\n" ..
S("Place a furnace fuel in the lower slot and the source material in the upper slot.") .. "\n" ..
S("The furnace will slowly use its fuel to smelt the item.") .. "\n" ..
S("The result will be placed into the output slot at the right side.") .. "\n" ..
S("Use the recipe book to see what you can smelt, what you can use as fuel and how long it will burn."),
_doc_items_hidden = false,
tiles = {
@ -452,7 +475,7 @@ minetest.register_node("mcl_furnaces:furnace", {
"default_furnace_side.png", "default_furnace_front.png"
},
paramtype2 = "facedir",
groups = {pickaxey=1, container=4, deco_block=1, material_stone=1},
groups = { pickaxey = 1, container = 4, deco_block = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
@ -462,10 +485,11 @@ minetest.register_node("mcl_furnaces:furnace", {
local meta2 = meta:to_table()
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for _, listname in ipairs({"src", "dst", "fuel"}) do
for _, listname in ipairs({ "src", "dst", "fuel" }) do
local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
local p = { x = pos.x + math.random(0, 10) / 10 - 0.5, y = pos.y,
z = pos.z + math.random(0, 10) / 10 - 0.5 }
minetest.add_item(p, stack)
end
end
@ -528,7 +552,7 @@ minetest.register_node("mcl_furnaces:furnace_active", {
paramtype = "light",
light_source = LIGHT_ACTIVE_FURNACE,
drop = "mcl_furnaces:furnace",
groups = {pickaxey=1, container=4, deco_block=1, not_in_creative_inventory=1, material_stone=1},
groups = { pickaxey = 1, container = 4, deco_block = 1, not_in_creative_inventory = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
on_timer = furnace_node_timer,
@ -538,10 +562,11 @@ minetest.register_node("mcl_furnaces:furnace_active", {
local meta2 = meta
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for _, listname in ipairs({"src", "dst", "fuel"}) do
for _, listname in ipairs({ "src", "dst", "fuel" }) do
local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
local p = { x = pos.x + math.random(0, 10) / 10 - 0.5, y = pos.y,
z = pos.z + math.random(0, 10) / 10 - 0.5 }
minetest.add_item(p, stack)
end
end
@ -586,7 +611,7 @@ end
minetest.register_lbm({
label = "Active furnace flame particles",
name = "mcl_furnaces:flames",
nodenames = {"mcl_furnaces:furnace_active"},
nodenames = { "mcl_furnaces:furnace_active" },
run_at_every_load = true,
action = function(pos, node)
spawn_flames(pos, node.param2)

@ -1,31 +1,46 @@
-- Code based from mcl_anvils
local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape
local C = minetest.colorize
local MAX_WEAR = 65535
-- formspecs
local function get_grindstone_formspec()
return "size[9,8.75]"..
"image[3,1.5;1.5,1;gui_crafting_arrow.png]"..
"label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
"label[1,0.1;"..minetest.formspec_escape(minetest.colorize("#313131", S("Repair & Disenchant"))).."]"..
"list[context;main;0,0;8,4;]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"list[context;input;1,1;1,1;]"..
mcl_formspec.get_itemslot_bg(1,1,1,1)..
"list[context;input;1,2;1,1;1]"..
mcl_formspec.get_itemslot_bg(1,2,1,1)..
"list[context;output;6,1.5;1,1;]"..
mcl_formspec.get_itemslot_bg(6,1.5,1,1)..
"listring[context;output]"..
"listring[current_player;main]"..
"listring[context;input]"..
"listring[current_player;main]"
end
local grindstone_formspec = table.concat({
"formspec_version[6]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Repair & Disenchant"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(2.875, 1.25, 1, 1),
"list[context;input;2.875,1.25;1,1;]",
mcl_formspec.get_itemslot_bg_v4(2.875, 2.625, 1, 1),
"list[context;input;2.875,2.625;1,1;1]",
"image[2.375,1;2,2.875;grindstone_gui_9.png;2]",
"image[1.875,1.5;0.5,2.875;grindstone_gui_9.png;2]",
"image[4.375,1.5;0.5,2.875;grindstone_gui_9.png;2]",
"image[5.5,1.95;1.5,1;gui_crafting_arrow.png]",
mcl_formspec.get_itemslot_bg_v4(7.875, 1.9375, 1, 1),
"list[context;output;7.875,1.9375;1,1;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
"listring[context;output]",
"listring[current_player;main]",
"listring[context;input]",
"listring[current_player;main]",
})
-- Creates a new item with the wear of the items and custom name
local function create_new_item(name_item, meta, wear)
@ -86,7 +101,6 @@ local function fix_stack_size(stack)
return count
end
-- Update the inventory slots of an grindstone node.
-- meta: Metadata of grindstone node
local function update_grindstone_slots(meta)
@ -168,10 +182,10 @@ end
-- Drop any items inside the grindstone if destroyed
local function drop_grindstone_items(pos, meta)
local inv = meta:get_inventory()
for i=1, inv:get_size("input") do
for i = 1, inv:get_size("input") do
local stack = inv:get_stack("input", i)
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
local p = { x = pos.x + math.random(0, 10) / 10 - 0.5, y = pos.y, z = pos.z + math.random(0, 10) / 10 - 0.5 }
minetest.add_item(p, stack)
end
end
@ -181,11 +195,11 @@ local node_box = {
type = "fixed",
-- created with nodebox editor
fixed = {
{-0.25, -0.25, -0.375, 0.25, 0.5, 0.375},
{-0.375, -0.0625, -0.1875, -0.25, 0.3125, 0.1875},
{0.25, -0.0625, -0.1875, 0.375, 0.3125, 0.1875},
{0.25, -0.5, -0.125, 0.375, -0.0625, 0.125},
{-0.375, -0.5, -0.125, -0.25, -0.0625, 0.125},
{ -0.25, -0.25, -0.375, 0.25, 0.5, 0.375 },
{ -0.375, -0.0625, -0.1875, -0.25, 0.3125, 0.1875 },
{ 0.25, -0.0625, -0.1875, 0.375, 0.3125, 0.1875 },
{ 0.25, -0.5, -0.125, 0.375, -0.0625, 0.125 },
{ -0.375, -0.5, -0.125, -0.25, -0.0625, 0.125 },
}
}
@ -193,10 +207,10 @@ minetest.register_node("mcl_grindstone:grindstone", {
description = S("Grindstone"),
_tt_help = S("Used to disenchant/fix tools"),
_doc_items_longdesc = S("Grindstone disenchants tools and armour except for curses, and repairs two items of the same type it is also the weapon smith's work station."),
_doc_items_usagehelp = S("To use the grindstone, rightclick it, Two input slots (on the left) and a single output slot.").."\n"..
S("To disenchant an item place enchanted item in one of the input slots and take the disenchanted item from the output.").."\n"..
S("To repair a tool you need a tool of the same type and material, put both items in the input slot and the output slot will combine two items durabilities with 5% bonus.").."\n"..
S("If both items have enchantments the player will get xp from both items from the disenchant.").."\n"..
_doc_items_usagehelp = S("To use the grindstone, rightclick it, Two input slots (on the left) and a single output slot.") .. "\n" ..
S("To disenchant an item place enchanted item in one of the input slots and take the disenchanted item from the output.") .. "\n" ..
S("To repair a tool you need a tool of the same type and material, put both items in the input slot and the output slot will combine two items durabilities with 5% bonus.") .. "\n" ..
S("If both items have enchantments the player will get xp from both items from the disenchant.") .. "\n" ..
S("Curses cannot be removed and will be transfered to the new repaired item, if both items have a different curse the curses will be combined."),
tiles = {
"grindstone_top.png",
@ -212,7 +226,7 @@ minetest.register_node("mcl_grindstone:grindstone", {
selection_box = node_box,
collision_box = node_box,
sounds = mcl_sounds.node_sound_stone_defaults(),
groups = {pickaxey = 1, deco_block = 1},
groups = { pickaxey = 1, deco_block = 1 },
after_dig_node = function(pos, oldnode, oldmetadata, digger)
local meta = minetest.get_meta(pos)
@ -268,7 +282,7 @@ minetest.register_node("mcl_grindstone:grindstone", {
local meta = minetest.get_meta(pos)
if from_list == "output" and to_list == "input" then
local inv = meta:get_inventory()
for i=1, inv:get_size("input") do
for i = 1, inv:get_size("input") do
if i ~= to_index then
local istack = inv:get_stack("input", i)
istack:set_count(math.max(0, istack:get_count() - count))
@ -289,8 +303,8 @@ minetest.register_node("mcl_grindstone:grindstone", {
if not input1:is_empty() and not input2:is_empty() then
-- Get xp earnt from the enchanted items
xp_earnt = calculate_xp(input1) + calculate_xp(input1)
input1:take_item()
input2:take_item()
input1:take_item(1)
input2:take_item(1)
inv:set_stack("input", 1, input1)
inv:set_stack("input", 2, input2)
else
@ -320,14 +334,13 @@ minetest.register_node("mcl_grindstone:grindstone", {
local inv = meta:get_inventory()
inv:set_size("input", 2)
inv:set_size("output", 1)
local form = get_grindstone_formspec()
meta:set_string("formspec", form)
meta:set_string("formspec", grindstone_formspec)
end,
on_rightclick = function(pos, node, player, itemstack)
if not player:get_player_control().sneak then
local meta = minetest.get_meta(pos)
update_grindstone_slots(meta)
meta:set_string("formspec", get_grindstone_formspec())
meta:set_string("formspec", grindstone_formspec)
end
end,
_mcl_blast_resistance = 6,
@ -337,7 +350,7 @@ minetest.register_node("mcl_grindstone:grindstone", {
minetest.register_craft({
output = "mcl_grindstone:grindstone",
recipe = {
{ "mcl_core:stick", "mcl_stairs:slab_stone_rough", "mcl_core:stick"},
{ "group:wood", "", "group:wood"},
{ "mcl_core:stick", "mcl_stairs:slab_stone_rough", "mcl_core:stick" },
{ "group:wood", "", "group:wood" },
}
})

@ -12,15 +12,22 @@ end
--[[ BEGIN OF NODE DEFINITIONS ]]
local mcl_hoppers_formspec = table.concat({
"size[9,7]",
"label[2,0;" .. F(C("#313131", S("Hopper"))) .. "]",
"list[context;main;2,0.5;5,1;]",
mcl_formspec.get_itemslot_bg(2, 0.5, 5, 1),
"label[0,2;" .. F(C("#313131", S("Inventory"))) .. "]",
"list[current_player;main;0,2.5;9,3;9]",
mcl_formspec.get_itemslot_bg(0, 2.5, 9, 3),
"list[current_player;main;0,5.74;9,1;]",
mcl_formspec.get_itemslot_bg(0, 5.74, 9, 1),
"formspec_version[4]",
"size[11.75,8.175]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Hopper"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(2.875, 0.75, 5, 1),
"list[context;main;2.875,0.75;5,1;]",
"label[0.375,2.45;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 2.85, 9, 3),
"list[current_player;main;0.375,2.85;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 6.8, 9, 1),
"list[current_player;main;0.375,6.8;9,1;]",
"listring[context;main]",
"listring[current_player;main]",
})
@ -31,7 +38,7 @@ local mcl_hoppers_formspec = table.concat({
local def_hopper = {
inventory_image = "mcl_hoppers_item.png",
wield_image = "mcl_hoppers_item.png",
groups = {pickaxey = 1, container = 2, deco_block = 1, hopper = 1},
groups = { pickaxey = 1, container = 2, deco_block = 1, hopper = 1 },
drawtype = "nodebox",
paramtype = "light",
-- FIXME: mcl_hoppers_hopper_inside.png is unused by hoppers.
@ -47,25 +54,25 @@ local def_hopper = {
type = "fixed",
fixed = {
--funnel walls
{-0.5, 0.0, 0.4, 0.5, 0.5, 0.5},
{0.4, 0.0, -0.5, 0.5, 0.5, 0.5},
{-0.5, 0.0, -0.5, -0.4, 0.5, 0.5},
{-0.5, 0.0, -0.5, 0.5, 0.5, -0.4},
{ -0.5, 0.0, 0.4, 0.5, 0.5, 0.5 },
{ 0.4, 0.0, -0.5, 0.5, 0.5, 0.5 },
{ -0.5, 0.0, -0.5, -0.4, 0.5, 0.5 },
{ -0.5, 0.0, -0.5, 0.5, 0.5, -0.4 },
--funnel base
{-0.5, 0.0, -0.5, 0.5, 0.1, 0.5},
{ -0.5, 0.0, -0.5, 0.5, 0.1, 0.5 },
--spout
{-0.3, -0.3, -0.3, 0.3, 0.0, 0.3},
{-0.1, -0.3, -0.1, 0.1, -0.5, 0.1},
{ -0.3, -0.3, -0.3, 0.3, 0.0, 0.3 },
{ -0.1, -0.3, -0.1, 0.1, -0.5, 0.1 },
},
},
selection_box = {
type = "fixed",
fixed = {
--funnel
{-0.5, 0.0, -0.5, 0.5, 0.5, 0.5},
{ -0.5, 0.0, -0.5, 0.5, 0.5, 0.5 },
--spout
{-0.3, -0.3, -0.3, 0.3, 0.0, 0.3},
{-0.1, -0.3, -0.1, 0.1, -0.5, 0.1},
{ -0.3, -0.3, -0.3, 0.3, 0.0, 0.3 },
{ -0.1, -0.3, -0.1, 0.1, -0.5, 0.1 },
},
},
is_ground_content = false,
@ -248,25 +255,25 @@ local def_hopper_side = {
type = "fixed",
fixed = {
--funnel walls
{-0.5, 0.0, 0.4, 0.5, 0.5, 0.5},
{0.4, 0.0, -0.5, 0.5, 0.5, 0.5},
{-0.5, 0.0, -0.5, -0.4, 0.5, 0.5},
{-0.5, 0.0, -0.5, 0.5, 0.5, -0.4},
{ -0.5, 0.0, 0.4, 0.5, 0.5, 0.5 },
{ 0.4, 0.0, -0.5, 0.5, 0.5, 0.5 },
{ -0.5, 0.0, -0.5, -0.4, 0.5, 0.5 },
{ -0.5, 0.0, -0.5, 0.5, 0.5, -0.4 },
--funnel base
{-0.5, 0.0, -0.5, 0.5, 0.1, 0.5},
{ -0.5, 0.0, -0.5, 0.5, 0.1, 0.5 },
--spout
{-0.3, -0.3, -0.3, 0.3, 0.0, 0.3},
{-0.5, -0.3, -0.1, 0.1, -0.1, 0.1},
{ -0.3, -0.3, -0.3, 0.3, 0.0, 0.3 },
{ -0.5, -0.3, -0.1, 0.1, -0.1, 0.1 },
},
},
selection_box = {
type = "fixed",
fixed = {
--funnel
{-0.5, 0.0, -0.5, 0.5, 0.5, 0.5},
{ -0.5, 0.0, -0.5, 0.5, 0.5, 0.5 },
--spout
{-0.3, -0.3, -0.3, 0.3, 0.0, 0.3},
{-0.5, -0.3, -0.1, 0.1, -0.1, 0.1},
{ -0.3, -0.3, -0.3, 0.3, 0.0, 0.3 },
{ -0.5, -0.3, -0.1, 0.1, -0.1, 0.1 },
},
},
is_ground_content = false,
@ -559,8 +566,8 @@ end
minetest.register_abm({
label = "Hopper/container item exchange",
nodenames = {"mcl_hoppers:hopper"},
neighbors = {"group:container"},
nodenames = { "mcl_hoppers:hopper" },
neighbors = { "group:container" },
interval = 1.0,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
@ -591,8 +598,8 @@ minetest.register_abm({
minetest.register_abm({
label = "Side-hopper/container item exchange",
nodenames = {"mcl_hoppers:hopper_side"},
neighbors = {"group:container"},
nodenames = { "mcl_hoppers:hopper_side" },
neighbors = { "group:container" },
interval = 1.0,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)

@ -1,11 +1,13 @@
--[[
By EliasFleckenstein03 and Code-Sploit
]]
-- By EliasFleckenstein03 and Code-Sploit
local S = minetest.get_translator("mcl_smithing_table")
local F = minetest.formspec_escape
local C = minetest.colorize
mcl_smithing_table = {}
-- Function to upgrade diamond tool/armor to netherite tool/armor
---Function to upgrade diamond tool/armor to netherite tool/armor
---@param itemstack ItemStack
function mcl_smithing_table.upgrade_item(itemstack)
local def = itemstack:get_definition()
@ -29,28 +31,48 @@ function mcl_smithing_table.upgrade_item(itemstack)
return itemstack
end
-- Badly copied over from mcl_anvils
-- ToDo: Make better formspec
local formspec = table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
local formspec = "size[9,9]" ..
"background[-0.19,-0.25;9.41,9.49;mcl_smithing_table_inventory.png]"..
"label[0,4.0;" .. minetest.formspec_escape(minetest.colorize(mcl_colors.DARK_GRAY, S("Inventory"))) .. "]" ..
"list[current_player;main;0,4.5;9,3;9]" ..
mcl_formspec.get_itemslot_bg(0,4.5,9,3) ..
"list[current_player;main;0,7.74;9,1;]" ..
mcl_formspec.get_itemslot_bg(0,7.74,9,1) ..
"list[context;diamond_item;1,2.5;1,1;]" ..
mcl_formspec.get_itemslot_bg(1,2.5,1,1) ..
"list[context;netherite;4,2.5;1,1;]" ..
mcl_formspec.get_itemslot_bg(4,2.5,1,1) ..
"list[context;upgraded_item;8,2.5;1,1;]" ..
mcl_formspec.get_itemslot_bg(8,2.5,1,1) ..
"label[3,0.1;" .. minetest.formspec_escape(minetest.colorize(mcl_colors.DARK_GRAY, S("Upgrade Gear"))) .. "]" ..
"listring[context;output]"..
"listring[current_player;main]"..
"listring[context;input]"..
"listring[current_player;main]"
"label[4.125,0.375;" .. F(C(mcl_formspec.label_color, S("Upgrade Gear"))) .. "]",
"image[0.875,0.375;1.75,1.75;mcl_smithing_table_inventory_hammer.png]",
mcl_formspec.get_itemslot_bg_v4(1.625, 2.6, 1, 1),
"list[context;diamond_item;1.625,2.6;1,1;]",
"image[3.5,2.6;1,1;mcl_anvils_inventory_cross.png]",
mcl_formspec.get_itemslot_bg_v4(5.375, 2.6, 1, 1),
"list[context;netherite;5.375,2.6;1,1;]",
"image[6.75,2.6;2,1;mcl_anvils_inventory_arrow.png]",
mcl_formspec.get_itemslot_bg_v4(9.125, 2.6, 1, 1),
"list[context;upgraded_item;9.125,2.6;1,1;]",
-- Player Inventory
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
-- Listrings
"listring[context;diamond_item]",
"listring[current_player;main]",
"listring[context;netherite]",
"listring[current_player;main]",
"listring[context;upgraded_item]",
"listring[current_player;main]",
"listring[current_player;main]",
"listring[context;diamond_item]",
})
---@param pos Vector
local function reset_upgraded_item(pos)
local inv = minetest.get_meta(pos):get_inventory()
local upgraded_item
@ -66,8 +88,7 @@ minetest.register_node("mcl_smithing_table:table", {
description = S("Smithing table"),
-- ToDo: Add _doc_items_longdesc and _doc_items_usagehelp
stack_max = 64,
groups = {pickaxey = 2, deco_block = 1},
groups = { pickaxey = 2, deco_block = 1 },
tiles = {
"mcl_smithing_table_top.png",
@ -92,7 +113,8 @@ minetest.register_node("mcl_smithing_table:table", {
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if listname == "diamond_item" and mcl_smithing_table.upgrade_item(stack) or listname == "netherite" and stack:get_name() == "mcl_nether:netherite_ingot" then
if listname == "diamond_item" and mcl_smithing_table.upgrade_item(stack) or
listname == "netherite" and stack:get_name() == "mcl_nether:netherite_ingot" then
return stack:get_count()
end
@ -119,7 +141,7 @@ minetest.register_node("mcl_smithing_table:table", {
take_item("netherite")
-- ToDo: make epic sound
minetest.sound_play("mcl_smithing_table_upgrade", {pos = pos, max_hear_distance = 16})
minetest.sound_play("mcl_smithing_table_upgrade", { pos = pos, max_hear_distance = 16 })
end
if listname == "upgraded_item" then
if stack:get_name() == "mcl_farming:hoe_netherite" then
@ -138,8 +160,8 @@ minetest.register_node("mcl_smithing_table:table", {
minetest.register_craft({
output = "mcl_smithing_table:table",
recipe = {
{"mcl_core:iron_ingot", "mcl_core:iron_ingot", ""},
{"group:wood", "group:wood", ""},
{"group:wood", "group:wood", ""}
}
{ "mcl_core:iron_ingot", "mcl_core:iron_ingot", "" },
{ "group:wood", "group:wood", "" },
{ "group:wood", "group:wood", "" }
},
})

@ -1,2 +1,2 @@
name = mcl_smithing_table
depends = mcl_colors, mcl_formspec
depends = mcl_colors, mcl_formspec, mcl_anvils

@ -1,5 +1,6 @@
local S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
local F = minetest.formspec_escape
local LIGHT_ACTIVE_FURNACE = 13
@ -8,60 +9,82 @@ local LIGHT_ACTIVE_FURNACE = 13
--
local function active_formspec(fuel_percent, item_percent)
return "size[9,8.75]"..
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Smoker"))).."]"..
"list[context;src;2.75,0.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,0.5,1,1)..
"list[context;fuel;2.75,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1)..
"list[context;dst;5.75,1.5;1,1;]"..
mcl_formspec.get_itemslot_bg(5.75,1.5,1,1)..
"image[2.75,1.5;1,1;default_furnace_fire_bg.png^[lowpart:"..
(100-fuel_percent)..":default_furnace_fire_fg.png]"..
"image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[lowpart:"..
(item_percent)..":gui_furnace_arrow_fg.png^[transformR270]"..
return table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Smoker"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
"list[context;src;3.5,0.75;1,1;]",
"image[3.5,2;1,1;default_furnace_fire_bg.png^[lowpart:" ..
(100 - fuel_percent) .. ":default_furnace_fire_fg.png]",
mcl_formspec.get_itemslot_bg_v4(3.5, 3.25, 1, 1),
"list[context;fuel;3.5,3.25;1,1;]",
"image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[lowpart:" ..
(item_percent) .. ":gui_furnace_arrow_fg.png^[transformR270]",
mcl_formspec.get_itemslot_bg_v4(7.875, 2, 1, 1, 0.2),
"list[context;dst;7.875,2;1,1;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
-- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed.
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]"
"listring[context;dst]",
"listring[current_player;main]",
"listring[context;src]",
"listring[current_player;main]",
"listring[context;fuel]",
"listring[current_player;main]",
})
end
local inactive_formspec = "size[9,8.75]"..
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]"..
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Smoker"))).."]"..
"list[context;src;2.75,0.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,0.5,1,1)..
"list[context;fuel;2.75,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1)..
"list[context;dst;5.75,1.5;1,1;]"..
mcl_formspec.get_itemslot_bg(5.75,1.5,1,1)..
"image[2.75,1.5;1,1;default_furnace_fire_bg.png]"..
"image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[transformR270]"..
local inactive_formspec = table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Smoker"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
"list[context;src;3.5,0.75;1,1;]",
"image[3.5,2;1,1;default_furnace_fire_bg.png]",
mcl_formspec.get_itemslot_bg_v4(3.5, 3.25, 1, 1),
"list[context;fuel;3.5,3.25;1,1;]",
"image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[transformR270]",
mcl_formspec.get_itemslot_bg_v4(7.875, 2, 1, 1, 0.2),
"list[context;dst;7.875,2;1,1;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
-- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed.
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]"
"listring[context;dst]",
"listring[current_player;main]",
"listring[context;src]",
"listring[current_player;main]",
"listring[context;fuel]",
"listring[current_player;main]",
})
local receive_fields = function(pos, formname, fields, sender)
if fields.craftguide then
@ -71,7 +94,7 @@ end
local function give_xp(pos, player)
local meta = minetest.get_meta(pos)
local dir = vector.divide(minetest.facedir_to_dir(minetest.get_node(pos).param2),-1.95)
local dir = vector.divide(minetest.facedir_to_dir(minetest.get_node(pos).param2), -1.95)
local xp = meta:get_int("xp")
if xp > 0 then
if player then
@ -98,7 +121,7 @@ local function allow_metadata_inventory_put(pos, listname, index, stack, player)
-- Test stack with size 1 because we burn one fuel at a time
local teststack = ItemStack(stack)
teststack:set_count(1)
local output, decremented_input = minetest.get_craft_result({method="fuel", width=1, items={teststack}})
local output, decremented_input = minetest.get_craft_result({ method = "fuel", width = 1, items = { teststack } })
if output.time ~= 0 then
-- Only allow to place 1 item if fuel get replaced by recipe.
-- This is the case for lava buckets.
@ -292,7 +315,7 @@ local function smoker_node_timer(pos, elapsed)
-- Check if we have cookable content: cookable
local aftercooked
cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
cooked, aftercooked = minetest.get_craft_result({ method = "cooking", width = 1, items = srclist })
cookable = minetest.get_item_group(inv:get_stack("src", 1):get_name(), "smoker_cookable") == 1
if cookable then
-- Successful cooking requires space in dst slot and time
@ -310,7 +333,7 @@ local function smoker_node_timer(pos, elapsed)
if cookable and not active then
-- We need to get new fuel
local afterfuel
fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
fuel, afterfuel = minetest.get_craft_result({ method = "fuel", width = 1, items = fuellist })
if fuel.time == 0 then
-- No valid fuel in fuel list -- stop
@ -415,12 +438,13 @@ end
minetest.register_node("mcl_smoker:smoker", {
description = S("Smoker"),
_tt_help = S("Cooks food faster than furnace"),
_doc_items_longdesc = S("Smokers cook several items, mainly raw foods, into cooked foods, but twice as fast as a normal furnace."),
_doc_items_longdesc = S(
"Smokers cook several items, mainly raw foods, into cooked foods, but twice as fast as a normal furnace."),
_doc_items_usagehelp =
S("Use the smoker to open the furnace menu.").."\n"..
S("Place a furnace fuel in the lower slot and the source material in the upper slot.").."\n"..
S("The smoker will slowly use its fuel to smelt the item.").."\n"..
S("The result will be placed into the output slot at the right side.").."\n"..
S("Use the smoker to open the furnace menu.") .. "\n" ..
S("Place a furnace fuel in the lower slot and the source material in the upper slot.") .. "\n" ..
S("The smoker will slowly use its fuel to smelt the item.") .. "\n" ..
S("The result will be placed into the output slot at the right side.") .. "\n" ..
S("Use the recipe book to see what foods you can smelt, what you can use as fuel and how long it will burn."),
_doc_items_hidden = false,
tiles = {
@ -429,7 +453,7 @@ minetest.register_node("mcl_smoker:smoker", {
"smoker_side.png", "smoker_front.png"
},
paramtype2 = "facedir",
groups = {pickaxey=1, container=4, deco_block=1, material_stone=1},
groups = { pickaxey = 1, container = 4, deco_block = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
@ -439,10 +463,14 @@ minetest.register_node("mcl_smoker:smoker", {
local meta2 = meta:to_table()
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for _, listname in ipairs({"src", "dst", "fuel"}) do
for _, listname in ipairs({ "src", "dst", "fuel" }) do
local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
local p = {
x = pos.x + math.random(0, 10) / 10 - 0.5,
y = pos.y,
z = pos.z + math.random(0, 10) / 10 - 0.5
}
minetest.add_item(p, stack)
end
end
@ -499,14 +527,16 @@ minetest.register_node("mcl_smoker:smoker_active", {
tiles = {
"smoker_top.png", "smoker_bottom.png",
"smoker_side.png", "smoker_side.png",
"smoker_side.png", {name = "smoker_front_on.png",
animation = {type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 48}},
"smoker_side.png", {
name = "smoker_front_on.png",
animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 48 }
},
},
paramtype2 = "facedir",
paramtype = "light",
light_source = LIGHT_ACTIVE_FURNACE,
drop = "mcl_smoker:smoker",
groups = {pickaxey=1, container=4, deco_block=1, not_in_creative_inventory=1, material_stone=1},
groups = { pickaxey = 1, container = 4, deco_block = 1, not_in_creative_inventory = 1, material_stone = 1 },
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
on_timer = smoker_node_timer,
@ -516,10 +546,14 @@ minetest.register_node("mcl_smoker:smoker_active", {
local meta2 = meta
meta:from_table(oldmetadata)
local inv = meta:get_inventory()
for _, listname in ipairs({"src", "dst", "fuel"}) do
for _, listname in ipairs({ "src", "dst", "fuel" }) do
local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then
local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
local p = {
x = pos.x + math.random(0, 10) / 10 - 0.5,
y = pos.y,
z = pos.z + math.random(0, 10) / 10 - 0.5
}
minetest.add_item(p, stack)
end
end
@ -564,7 +598,7 @@ end
minetest.register_lbm({
label = "Active smoker flame particles",
name = "mcl_smoker:flames",
nodenames = {"mcl_smoker:smoker_active"},
nodenames = { "mcl_smoker:smoker_active" },
run_at_every_load = true,
action = function(pos, node)
spawn_flames(pos, node.param2)

@ -0,0 +1,27 @@
# `mcl_gamemode`
## `mcl_gamemode.gamemodes`
List of availlable gamemodes.
Currently `{"survival", "creative"}`
## `mcl_gamemode.get_gamemode(player)`
Get the player's gamemode.
Returns "survival" or "creative".
## `mcl_gamemode.set_gamemode(player, gamemode)`
Set the player's gamemode.
gamemode: "survival" or "creative"
## `mcl_gamemode.register_on_gamemode_change(function(player, old_gamemode, new_gamemode))`
Register a function that will be called when `mcl_gamemode.set_gamemode` is called.
## `mcl_gamemode.registered_on_gamemode_change`
Map of registered on_gamemode_change.

@ -0,0 +1,84 @@
local S = minetest.get_translator("mcl_gamemode")
mcl_gamemode = {}
mcl_gamemode.gamemodes = {
"survival",
"creative",
}
---@param n any
---@param h table
---@return boolean
local function in_table(n, h)
for k, v in pairs(h) do
if v == n then return true end
end
return false
end
---@type fun(player: mt.PlayerObjectRef, old_gamemode: '"survival"'|'"creative"', new_gamemode: '"survival"'|'"creative"')[]
mcl_gamemode.registered_on_gamemode_change = {}
---@param func fun(player: mt.PlayerObjectRef, old_gamemode: '"survival"'|'"creative"', new_gamemode: '"survival"'|'"creative"')
function mcl_gamemode.register_on_gamemode_change(func)
table.insert(mcl_gamemode.registered_on_gamemode_change, func)
end
---@param player mt.PlayerObjectRef
---@param gamemode '"survival"'|'"creative"'
function mcl_gamemode.set_gamemode(player, gamemode)
local meta = player:get_meta()
local old_gamemode = meta:get_string("gamemode")
meta:set_string("gamemode", gamemode)
for _, f in ipairs(mcl_gamemode.registered_on_gamemode_change) do
f(player, old_gamemode, gamemode)
end
end
local mt_is_creative_enabled = minetest.is_creative_enabled
---@param player mt.PlayerObjectRef
---@return '"survival"'|'"creative"'
function mcl_gamemode.get_gamemode(player)
if mt_is_creative_enabled(player:get_player_name()) then
return "creative"
end
return player:get_meta():get_string("gamemode")
end
function minetest.is_creative_enabled(name)
if mt_is_creative_enabled(name) then return true end
if not name then return false end
local p = minetest.get_player_by_name(name)
if p then
return p:get_meta():get_string("gamemode") == "creative"
end
return false
end
minetest.register_chatcommand("gamemode", {
params = S("[<gamemode>] [<player>]"),
description = S("Change gamemode (survival/creative) for yourself or player"),
privs = { server = true },
func = function(n, param)
-- Full input validation ( just for @erlehmann <3 )
local p = minetest.get_player_by_name(n)
local args = param:split(" ")
if args[2] ~= nil then
p = minetest.get_player_by_name(args[2])
end
if not p then
return false, S("Player not online")
end
if args[1] ~= nil and not in_table(args[1], mcl_gamemode.gamemodes) then
return false, S("Gamemode " .. args[1] .. " does not exist.")
elseif args[1] ~= nil then
mcl_gamemode.set_gamemode(p, args[1])
end
--Result message - show effective game mode
local gm = p:get_meta():get_string("gamemode")
if gm == "" then gm = mcl_gamemode.gamemodes[1] end
return true, S("Gamemode for player ") .. n .. S(": " .. gm)
end
})

@ -0,0 +1 @@
name = mcl_gamemode

@ -89,6 +89,10 @@ minetest.register_on_joinplayer(function(player)
player:get_inventory():set_size("hand", 1)
end)
mcl_gamemode.register_on_gamemode_change(function(player)
mcl_meshhand.update_player(player)
end)
if mcl_skins_enabled then
mcl_player.register_on_visual_change(mcl_meshhand.update_player)
else

@ -1,5 +1,5 @@
name = mcl_meshhand
author = jordan4ibanez
description = Applies the player skin texture to the hand.
depends = mcl_tools, mcl_player
depends = mcl_tools, mcl_player, mcl_gamemode
optional_depends = mcl_skins, mcl_custom_skins

@ -1,3 +1,4 @@
local string = string
local sf = string.format
-- Minetest 0.4 mod: player
@ -11,7 +12,8 @@ local animation_blend = 0
local function get_mouse_button(player)
local controls = player:get_player_control()
local get_wielded_item_name = player:get_wielded_item():get_name()
if controls.RMB and not string.find(get_wielded_item_name, "mcl_bows:bow") and not string.find(get_wielded_item_name, "mcl_bows:crossbow") and
if controls.RMB and not string.find(get_wielded_item_name, "mcl_bows:bow") and
not string.find(get_wielded_item_name, "mcl_bows:crossbow") and
not mcl_shields.wielding_shield(player, 1) and not mcl_shields.wielding_shield(player, 2) or controls.LMB then
return true
else
@ -19,7 +21,7 @@ local function get_mouse_button(player)
end
end
mcl_player.registered_player_models = { }
mcl_player.registered_player_models = {}
-- Local for speed.
local models = mcl_player.registered_player_models
@ -49,7 +51,7 @@ function mcl_player.player_get_animation(player)
model = player_model[name],
textures = textures,
animation = player_anim[name],
visibility = player_visibility[name]
visibility = player_visible[name]
}
end
@ -94,7 +96,7 @@ function mcl_player.player_set_model(player, model_name)
player:set_properties({
mesh = model_name,
visual = "mesh",
visual_size = model.visual_size or {x=1, y=1},
visual_size = model.visual_size or { x = 1, y = 1 },
damage_texture_modifier = "^[colorize:red:130",
})
update_player_textures(player)
@ -133,6 +135,13 @@ function mcl_player.player_set_armor(player, texture)
update_player_textures(player)
end
---@param player mt.PlayerObjectRef
---@param x number
---@param y number
---@param w number
---@param h number
---@param fsname string
---@return string
function mcl_player.get_player_formspec_model(player, x, y, w, h, fsname)
local name = player:get_player_name()
local model = player_model[name]
@ -165,7 +174,8 @@ minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
mcl_player.player_attached[name] = false
player_visible[name] = true
player_textures[name] = {"character.png", "blank.png", "blank.png"}
player_textures[name] = { "character.png", "blank.png", "blank.png" }
--player:set_local_animation({x=0, y=79}, {x=168, y=187}, {x=189, y=198}, {x=200, y=219}, 30)
player:set_fov(86.1) -- see <https://minecraft.gamepedia.com/Options#Video_settings>>>>
end)
@ -225,7 +235,9 @@ minetest.register_globalstep(function(dtime)
or walking and velocity.z > 0.35
or walking and velocity.z < -0.35 then
local wielded_itemname = player:get_wielded_item():get_name()
local no_arm_moving = string.find(wielded_itemname, "mcl_bows:bow") or mcl_shields.wielding_shield(player, 1) or mcl_shields.wielding_shield(player, 2)
local no_arm_moving = string.find(wielded_itemname, "mcl_bows:bow") or
mcl_shields.wielding_shield(player, 1) or
mcl_shields.wielding_shield(player, 2)
if player_sneak[name] ~= controls.sneak then
player_anim[name] = nil
player_sneak[name] = controls.sneak
@ -234,7 +246,8 @@ minetest.register_globalstep(function(dtime)
player_set_animation(player, "swim_walk_mine", animation_speed_mod)
elseif not controls.sneak and head_in_water and is_sprinting == true then
player_set_animation(player, "swim_walk", animation_speed_mod)
elseif no_arm_moving and controls.RMB and controls.sneak or string.find(wielded_itemname, "mcl_bows:crossbow_") and controls.sneak then
elseif no_arm_moving and controls.RMB and controls.sneak or
string.find(wielded_itemname, "mcl_bows:crossbow_") and controls.sneak then
player_set_animation(player, "bow_sneak", animation_speed_mod)
elseif no_arm_moving and controls.RMB or string.find(wielded_itemname, "mcl_bows:crossbow_") then
player_set_animation(player, "bow_walk", animation_speed_mod)

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 B

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 B

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 B

After

Width:  |  Height:  |  Size: 320 B