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,10 +1,12 @@
---@diagnostic disable
unused_args = false unused_args = false
allow_defined_top = true allow_defined_top = true
max_line_length = false max_line_length = false
redefined = false redefined = false
globals = { globals = {
"minetest", "core", "minetest", "core",
} }
read_globals = { read_globals = {
@ -40,16 +42,16 @@ read_globals = {
"factorial" "factorial"
} }
}, },
------ ------
--MODS --MODS
------ ------
--GENERAL --GENERAL
"default", "default",
--ENTITIES --ENTITIES
"cmi", "cmi",
--HUD --HUD
"sfinv", "sfinv_buttons", "unified_inventory", "cmsg", "inventory_plus", "sfinv", "sfinv_buttons", "unified_inventory", "cmsg", "inventory_plus",
} }

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( obj:set_velocity(vector.new(
math.random(-2, 2) * math.random(), math.random(-2, 2) * math.random(),
math.random( 2, 5), math.random(2, 5),
math.random(-2, 2) * math.random() math.random(-2, 2) * math.random()
)) ))
i = i + xp i = i + xp
j = j + 1 j = j + 1
table.insert(obs,obj) table.insert(obs, obj)
end end
return obs return obs
end end
@ -179,18 +179,18 @@ function mcl_experience.setup_hud(player)
if not minetest.is_creative_enabled(player:get_player_name()) then if not minetest.is_creative_enabled(player:get_player_name()) then
hud_bars[player] = player:hud_add({ hud_bars[player] = player:hud_add({
hud_elem_type = "image", hud_elem_type = "image",
position = {x = 0.5, y = 1}, position = { x = 0.5, y = 1 },
offset = {x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5)}, offset = { x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5) },
scale = {x = 0.35, y = 0.375}, scale = { x = 0.35, y = 0.375 },
alignment = {x = 1, y = 1}, alignment = { x = 1, y = 1 },
z_index = 11, z_index = 11,
}) })
hud_levels[player] = player:hud_add({ hud_levels[player] = player:hud_add({
hud_elem_type = "text", hud_elem_type = "text",
position = {x = 0.5, y = 1}, position = { x = 0.5, y = 1 },
number = 0x80FF20, number = 0x80FF20,
offset = {x = 0, y = -(48 + 24 + 24)}, offset = { x = 0, y = -(48 + 24 + 24) },
z_index = 12, z_index = 12,
}) })
end end
@ -221,7 +221,7 @@ function mcl_experience.update(player)
end end
function mcl_experience.register_on_add_xp(func, priority) 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 end
-- callbacks -- callbacks
@ -232,9 +232,9 @@ minetest.register_on_joinplayer(function(player)
end) end)
minetest.register_on_leaveplayer(function(player) minetest.register_on_leaveplayer(function(player)
hud_bars[player] = nil hud_bars[player] = nil
hud_levels[player] = nil hud_levels[player] = nil
caches[player] = nil caches[player] = nil
end) end)
minetest.register_on_dieplayer(function(player) minetest.register_on_dieplayer(function(player)
@ -247,3 +247,12 @@ end)
minetest.register_on_mods_loaded(function() minetest.register_on_mods_loaded(function()
table.sort(mcl_experience.on_add_xp, function(a, b) return a.priority < b.priority end) table.sort(mcl_experience.on_add_xp, function(a, b) return a.priority < b.priority end)
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 name = mcl_experience
author = oilboi author = oilboi
description = eXPerience mod 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 = {}
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) function mcl_formspec.get_itemslot_bg(x, y, w, h)
local out = "" local out = ""
for i = 0, w - 1, 1 do for i = 0, w - 1, 1 do
for j = 0, h - 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
end end
return out 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 local C = minetest.colorize
-- Prepare player info table -- Prepare player info table
---@type table<string, {page: string, filter: string, start_i: integer, inv_size: integer}>
local players = {} local players = {}
-- Containing all the items for each Creative Mode tab -- Containing all the items for each Creative Mode tab
---@type table<string, string[]>
local inventory_lists = {} local inventory_lists = {}
--local mod_player = minetest.get_modpath("mcl_player")
-- Create tables -- 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 for _, f in pairs(builtin_filter_ids) do
inventory_lists[f] = {} inventory_lists[f] = {}
end end
---@param tbl string[]
local function replace_enchanted_books(tbl) local function replace_enchanted_books(tbl)
for k, item in ipairs(tbl) do for k, item in ipairs(tbl) do
if item:find("mcl_enchanting:book_enchanted") == 1 then if item:find("mcl_enchanting:book_enchanted") == 1 then
@ -28,20 +44,32 @@ local function replace_enchanted_books(tbl)
end end
end end
--[[ Populate all the item tables. We only do this once. Note this code must be -- Populate all the item tables. We only do this once.
executed after loading all the other mods in order to work. ]] -- Note this code must be executed after loading all the other mods in order to work.
minetest.register_on_mods_loaded(function() minetest.register_on_mods_loaded(function()
for name,def in pairs(minetest.registered_items) do 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 (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) 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 end
---@param def mt.ItemDef|mt.NodeDef
local function is_tool(def) local function is_tool(def)
return def.groups.tool or (def.tool_capabilities and def.tool_capabilities.damage_groups == nil) return def.groups.tool or (def.tool_capabilities and def.tool_capabilities.damage_groups == nil)
end end
---@param def mt.ItemDef|mt.NodeDef
local function is_weapon_or_armor(def) 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 end
-- Is set to true if it was added in any category besides misc -- Is set to true if it was added in any category besides misc
local nonmisc = false local nonmisc = false
if def.groups.building_block then if def.groups.building_block then
@ -110,6 +138,11 @@ minetest.register_on_mods_loaded(function()
end end
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 function filter_item(name, description, lang, filter)
local desc local desc
if not lang then 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) return string.find(name, filter, nil, true) or string.find(desc, filter, nil, true)
end end
---@param filter string
---@param player mt.PlayerObjectRef
local function set_inv_search(filter, player) local function set_inv_search(filter, player)
local playername = player:get_player_name() 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 creative_list = {}
local lang = minetest.get_player_information(playername).lang_code local lang = minetest.get_player_information(playername).lang_code
for name,def in pairs(minetest.registered_items) do 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 (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 if filter_item(string.lower(def.name), def.description, lang, filter) then
table.insert(creative_list, name) table.insert(creative_list, name)
end end
@ -147,9 +183,11 @@ local function set_inv_search(filter, player)
inv:set_list("main", creative_list) inv:set_list("main", creative_list)
end end
---@param page string
---@param player mt.PlayerObjectRef
local function set_inv_page(page, player) local function set_inv_page(page, player)
local playername = player:get_player_name() 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) inv:set_size("main", 0)
local creative_list = {} local creative_list = {}
if inventory_lists[page] then -- Standard filter if inventory_lists[page] then -- Standard filter
@ -160,9 +198,11 @@ local function set_inv_page(page, player)
inv:set_list("main", creative_list) inv:set_list("main", creative_list)
end end
---@param player mt.PlayerObjectRef
local function init(player) local function init(player)
local playername = player:get_player_name() 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) allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
if minetest.is_creative_enabled(playername) then if minetest.is_creative_enabled(playername) then
return count return count
@ -197,24 +237,45 @@ local trash = minetest.create_detached_inventory("trash", {
inv:set_stack(listname, index, "") inv:set_stack(listname, index, "")
end, end,
}) })
trash:set_size("main", 1) trash:set_size("main", 1)
local noffset = {} -- numeric tab offset ------------------------------
local offset = {} -- string offset: -- Formspec Precalculations --
local boffset = {} -- ------------------------------
local hoch = {}
local filtername = {}
--local bg = {}
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_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) local function next_noffset(id, right)
if right then if right then
noffset[id] = { 8.94, noffset_y } noffset[id] = { 11.3, noffset_y }
else else
noffset[id] = { noffset_x, noffset_y } noffset[id] = { noffset_x, noffset_y }
noffset_x = noffset_x + 1.25 noffset_x = noffset_x + 1.6
end end
end end
@ -228,7 +289,7 @@ next_noffset("misc")
next_noffset("nix", true) next_noffset("nix", true)
noffset_x = noffset_x_start noffset_x = noffset_x_start
noffset_y = 8.12 noffset_y = 8.64
-- Lower row -- Lower row
next_noffset("food") next_noffset("food")
@ -238,25 +299,25 @@ next_noffset("mobs")
next_noffset("matr") next_noffset("matr")
next_noffset("inv", true) 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]) 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 end
hoch["blocks"] = "" button_bg_postfix["blocks"] = ""
hoch["deco"] = "" button_bg_postfix["deco"] = ""
hoch["redstone"] = "" button_bg_postfix["redstone"] = ""
hoch["rail"] = "" button_bg_postfix["rail"] = ""
hoch["brew"] = "" button_bg_postfix["brew"] = ""
hoch["misc"] = "" button_bg_postfix["misc"] = ""
hoch["nix"] = "" button_bg_postfix["nix"] = ""
hoch["default"] = "" button_bg_postfix["default"] = ""
hoch["food"] = "_down" button_bg_postfix["food"] = "_down"
hoch["tools"] = "_down" button_bg_postfix["tools"] = "_down"
hoch["combat"] = "_down" button_bg_postfix["combat"] = "_down"
hoch["mobs"] = "_down" button_bg_postfix["mobs"] = "_down"
hoch["matr"] = "_down" button_bg_postfix["matr"] = "_down"
hoch["inv"] = "_down" button_bg_postfix["inv"] = "_down"
filtername["blocks"] = S("Building Blocks") filtername["blocks"] = S("Building Blocks")
filtername["deco"] = S("Decoration Blocks") filtername["deco"] = S("Decoration Blocks")
@ -291,197 +352,252 @@ filtername["inv"] = S("Survival Inventory")
bg["default"] = dark_bg bg["default"] = dark_bg
end]] end]]
-- 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",
rail = "mcl_minecarts:golden_rail",
misc = "mcl_buckets:bucket_lava",
nix = "mcl_compass:compass",
food = "mcl_core:apple",
tools = "mcl_core:axe_iron",
combat = "mcl_core:sword_gold",
mobs = "mobs_mc:cow",
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) local function get_stack_size(player)
return player:get_meta():get_int("mcl_inventory:switch_stack") return player:get_meta():get_int("mcl_inventory:switch_stack")
end 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) local function set_stack_size(player, n)
player:get_meta():set_int("mcl_inventory:switch_stack", n) player:get_meta():set_int("mcl_inventory:switch_stack", n)
end end
minetest.register_on_joinplayer(function (player) minetest.register_on_joinplayer(function(player)
if get_stack_size(player) == 0 then if get_stack_size(player) == 0 then
set_stack_size(player, 64) set_stack_size(player, 64)
end end
end) end)
---@param player mt.PlayerObjectRef
function mcl_inventory.set_creative_formspec(player) function mcl_inventory.set_creative_formspec(player)
local playername = player:get_player_name() local playername = player:get_player_name()
if not players[playername] then return end if not players[playername] then return end
local start_i = players[playername].start_i local start_i = players[playername].start_i
local pagenum = start_i / (9*5) + 1 local pagenum = start_i / (9 * 5) + 1
local name = players[playername].page local page = players[playername].page
local inv_size = players[playername].inv_size local inv_size = players[playername].inv_size
local filter = players[playername].filter local filter = players[playername].filter
local pagemax = math.max(1, math.floor((inv_size-1) / (9*5) + 1))
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 main_list
local listrings = "listring[detached:creative_"..playername..";main]".. local listrings = table.concat({
"listring[current_player;main]".. "listring[detached:creative_" .. playername .. ";main]",
"listring[detached:trash;main]" "listring[current_player;main]",
"listring[detached:trash;main]",
})
if page then
name = page
if players[playername] then
players[playername].page = page
end
end
local inv_bg = "crafting_inventory_creative.png"
if name == "inv" then if name == "inv" then
inv_bg = "crafting_inventory_creative_survival.png"
-- Background images for armor slots (hide if occupied) -- Background images for armor slots (hide if occupied)
local armor_slot_imgs = "" local armor_slot_imgs = ""
local inv = player:get_inventory() local inv = player:get_inventory()
if inv:get_stack("armor", 2):is_empty() then 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]" armor_slot_imgs = armor_slot_imgs .. "image[3.5,0.375;1,1;mcl_inventory_empty_armor_slot_helmet.png]"
end end
if inv:get_stack("armor", 3):is_empty() then 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]" armor_slot_imgs = armor_slot_imgs .. "image[3.5,2.125;1,1;mcl_inventory_empty_armor_slot_chestplate.png]"
end end
if inv:get_stack("armor", 4):is_empty() then 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]" armor_slot_imgs = armor_slot_imgs .. "image[7.25,0.375;1,1;mcl_inventory_empty_armor_slot_leggings.png]"
end end
if inv:get_stack("armor", 5):is_empty() then 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]" armor_slot_imgs = armor_slot_imgs .. "image[7.25,2.125;1,1;mcl_inventory_empty_armor_slot_boots.png]"
end end
if inv:get_stack("offhand", 1):is_empty() then 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]" armor_slot_imgs = armor_slot_imgs .. "image[2.25,1.25;1,1;mcl_inventory_empty_armor_slot_shield.png]"
end end
local stack_size = get_stack_size(player) local stack_size = get_stack_size(player)
-- Survival inventory slots -- Survival inventory slots
main_list = "list[current_player;main;0,3.75;9,3;9]" .. main_list = table.concat({
mcl_formspec.get_itemslot_bg(0, 3.75, 9, 3) .. mcl_formspec.get_itemslot_bg_v4(0.375, 3.375, 9, 3),
"list[current_player;main;0.375,3.375;9,3;9]",
-- Armor -- Armor
"list[current_player;armor;2.5,1.3;1,1;1]" .. mcl_formspec.get_itemslot_bg_v4(3.5, 0.375, 1, 1),
"list[current_player;armor;2.5,2.75;1,1;2]" .. mcl_formspec.get_itemslot_bg_v4(3.5, 2.125, 1, 1),
"list[current_player;armor;5.5,1.3;1,1;3]" .. mcl_formspec.get_itemslot_bg_v4(7.25, 0.375, 1, 1),
"list[current_player;armor;5.5,2.75;1,1;4]" .. mcl_formspec.get_itemslot_bg_v4(7.25, 2.125, 1, 1),
mcl_formspec.get_itemslot_bg(2.5, 1.3, 1, 1) .. "list[current_player;armor;3.5,0.375;1,1;1]",
mcl_formspec.get_itemslot_bg(2.5, 2.75, 1, 1) .. "list[current_player;armor;3.5,2.125;1,1;2]",
mcl_formspec.get_itemslot_bg(5.5, 1.3, 1, 1) .. "list[current_player;armor;7.25,0.375;1,1;3]",
mcl_formspec.get_itemslot_bg(5.5, 2.75, 1, 1) .. "list[current_player;armor;7.25,2.125;1,1;4]",
"list[current_player;offhand;1.5,2.025;1,1]" ..
mcl_formspec.get_itemslot_bg(1.5, 2.025, 1, 1) .. -- Offhand
armor_slot_imgs .. 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 -- Player preview
mcl_player.get_player_formspec_model(player, 3.9, 1.4, 1.2333, 2.4666, "") .. "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 -- Crafting guide button
"image_button[9,1;1,1;craftguide_book.png;__mcl_craftguide;]" .. "image_button[11.575,0.825;1.1,1.1;craftguide_book.png;__mcl_craftguide;]",
"tooltip[__mcl_craftguide;"..F(S("Recipe book")) .. "]" .. "tooltip[__mcl_craftguide;" .. F(S("Recipe book")) .. "]",
-- Help button -- Help button
"image_button[9,2;1,1;doc_button_icon_lores.png;__mcl_doc;]" .. "image_button[11.575,2.075;1.1,1.1;doc_button_icon_lores.png;__mcl_doc;]",
"tooltip[__mcl_doc;" .. F(S("Help")) .. "]" .. "tooltip[__mcl_doc;" .. F(S("Help")) .. "]",
-- Achievements button -- Advancements button
"image_button[9,3;1,1;mcl_achievements_button.png;__mcl_achievements;]" .. "image_button[11.575,3.325;1.1,1.1;mcl_achievements_button.png;__mcl_achievements;]",
--"style_type[image_button;border=;bgimg=;bgimg_pressed=]" .. --"style_type[image_button;border=;bgimg=;bgimg_pressed=]",
"tooltip[__mcl_achievements;"..F(S("Advancements")) .. "]" .. "tooltip[__mcl_achievements;" .. F(S("Advancements")) .. "]",
-- Switch stack size button -- Switch stack size button
"image_button[9,4;1,1;default_apple.png;__switch_stack;]" .. "image_button[11.575,4.575;1.1,1.1;default_apple.png;__switch_stack;]",
"label[9.4,4.4;" .. F(C("#FFFFFF", stack_size ~= 1 and stack_size or "")) .. "]" .. "label[12.275,5.35;" .. F(C("#FFFFFF", tostring(stack_size ~= 1 and stack_size or ""))) .. "]",
"tooltip[__switch_stack;" .. F(S("Switch stack size")) .. "]" "tooltip[__switch_stack;" .. F(S("Switch stack size")) .. "]",
-- Skins button -- Skins button
if minetest.global_exists("mcl_skins") then "image_button[11.575,5.825;1.1,1.1;mcl_skins_button.png;__mcl_skins;]",
main_list = main_list .. "tooltip[__mcl_skins;" .. F(S("Select player skin")) .. "]",
"image_button[9,5;1,1;mcl_skins_button.png;__mcl_skins;]" .. })
"tooltip[__mcl_skins;"..F(S("Select player skin")) .. "]"
end
-- For shortcuts -- For shortcuts
listrings = listrings .. listrings = listrings ..
"listring[detached:"..playername.."_armor;armor]".. "listring[detached:" .. playername .. "_armor;armor]" ..
"listring[current_player;main]" "listring[current_player;main]"
else else
--local nb_lines = math.ceil(inv_size / 9)
-- Creative inventory slots -- Creative inventory slots
main_list = "list[detached:creative_"..playername..";main;0,1.75;9,5;"..tostring(start_i).."]".. main_list = table.concat({
mcl_formspec.get_itemslot_bg(0,1.75,9,5).. mcl_formspec.get_itemslot_bg_v4(0.375, 0.875, 9, 5),
-- Page buttons
"label[9.0,5.5;"..F(S("@1/@2", pagenum, pagemax)).."]".. -- Basic code to replace buttons by scrollbar
"image_button[9.0,6.0;0.7,0.7;crafting_creative_prev.png;creative_prev;]".. -- Require Minetest 5.8
"image_button[9.5,6.0;0.7,0.7;crafting_creative_next.png;creative_next;]" --
--"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 end
local tab_icon = { ---@param current_tab string
blocks = "mcl_core:brick_block", ---@param this_tab string
deco = "mcl_flowers:peony", ---@return string
redstone = "mesecons:redstone",
rail = "mcl_minecarts:golden_rail",
misc = "mcl_buckets:bucket_lava",
nix = "mcl_compass:compass",
food = "mcl_core:apple",
tools = "mcl_core:axe_iron",
combat = "mcl_core:sword_gold",
mobs = "mobs_mc:cow",
brew = "mcl_potions:dragon_breath",
matr = "mcl_core:stick",
inv = "mcl_chests:chest",
}
local function tab(current_tab, this_tab) local function tab(current_tab, this_tab)
local bg_img local bg_img
if current_tab == this_tab then 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 else
bg_img = "crafting_creative_inactive"..hoch[this_tab]..".png" bg_img = "crafting_creative_inactive" .. button_bg_postfix[this_tab] .. ".png"
end end
return return table.concat({
"style["..this_tab..";border=false;bgimg=;bgimg_pressed=]".. "style[" .. this_tab .. ";border=false;bgimg=;bgimg_pressed=;noclip=true]",
"item_image_button[" .. boffset[this_tab] ..";1,1;"..tab_icon[this_tab]..";"..this_tab..";]".. "image[" .. offset[this_tab] .. ";1.5,1.44;" .. bg_img .. "]",
"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 end
local caption = "" local caption = ""
if name ~= "inv" and filtername[name] then 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 end
local formspec = "size[10,9.3]".. local formspec = table.concat({
"no_prepend[]".. "formspec_version[6]",
mcl_vars.gui_nonbg..mcl_vars.gui_bg_color.. "size[13,8.75]",
"background[-0.19,-0.25;10.5,9.87;"..inv_bg.."]"..
"label[-5,-5;"..name.."]".. "style_type[image;noclip=true]",
tab(name, "blocks") ..
"tooltip[blocks;"..F(filtername["blocks"]).."]".. -- Hotbar
tab(name, "deco") .. mcl_formspec.get_itemslot_bg_v4(0.375, 7.375, 9, 1),
"tooltip[deco;"..F(filtername["deco"]).."]".. "list[current_player;main;0.375,7.375;9,1;]",
tab(name, "redstone") ..
"tooltip[redstone;"..F(filtername["redstone"]).."]".. -- Trash
tab(name, "rail") .. mcl_formspec.get_itemslot_bg_v4(11.625, 7.375, 1, 1, nil, "crafting_creative_trash.png"),
"tooltip[rail;"..F(filtername["rail"]).."]".. "list[detached:trash;main;11.625,7.375;1,1;]",
tab(name, "misc") ..
"tooltip[misc;"..F(filtername["misc"]).."]".. main_list,
tab(name, "nix") ..
"tooltip[nix;"..F(filtername["nix"]).."]".. caption,
caption..
"list[current_player;main;0,7;9,1;]".. listrings,
mcl_formspec.get_itemslot_bg(0,7,9,1)..
main_list.. tab(name, "blocks"),
tab(name, "food") .. tab(name, "deco"),
"tooltip[food;"..F(filtername["food"]).."]".. tab(name, "redstone"),
tab(name, "tools") .. tab(name, "rail"),
"tooltip[tools;"..F(filtername["tools"]).."]".. tab(name, "misc"),
tab(name, "combat") .. tab(name, "nix"),
"tooltip[combat;"..F(filtername["combat"]).."]"..
tab(name, "mobs") .. tab(name, "food"),
"tooltip[mobs;"..F(filtername["mobs"]).."]".. tab(name, "tools"),
tab(name, "brew") .. tab(name, "combat"),
"tooltip[brew;"..F(filtername["brew"]).."]".. tab(name, "mobs"),
tab(name, "matr") .. tab(name, "brew"),
"tooltip[matr;"..F(filtername["matr"]).."]".. tab(name, "matr"),
tab(name, "inv") .. 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
if name == "nix" then if name == "nix" then
formspec = formspec .. "field[5.3,1.34;4,0.75;search;;"..minetest.formspec_escape(filter).."]" if filter == nil then
formspec = formspec .. "field_close_on_enter[search;false]" filter = ""
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 end
if pagenum then formspec = formspec .. "p"..tostring(pagenum) end if pagenum then formspec = formspec .. "p" .. tostring(pagenum) end
player:set_inventory_formspec(formspec) player:set_inventory_formspec(formspec)
end end
@ -500,50 +616,50 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.blocks then if fields.blocks then
if players[name].page == "blocks" then return end if players[name].page == "blocks" then return end
set_inv_page("blocks",player) set_inv_page("blocks", player)
page = "blocks" page = "blocks"
elseif fields.deco then elseif fields.deco then
if players[name].page == "deco" then return end if players[name].page == "deco" then return end
set_inv_page("deco",player) set_inv_page("deco", player)
page = "deco" page = "deco"
elseif fields.redstone then elseif fields.redstone then
if players[name].page == "redstone" then return end if players[name].page == "redstone" then return end
set_inv_page("redstone",player) set_inv_page("redstone", player)
page = "redstone" page = "redstone"
elseif fields.rail then elseif fields.rail then
if players[name].page == "rail" then return end if players[name].page == "rail" then return end
set_inv_page("rail",player) set_inv_page("rail", player)
page = "rail" page = "rail"
elseif fields.misc then elseif fields.misc then
if players[name].page == "misc" then return end if players[name].page == "misc" then return end
set_inv_page("misc",player) set_inv_page("misc", player)
page = "misc" page = "misc"
elseif fields.nix then elseif fields.nix then
set_inv_page("all",player) set_inv_page("all", player)
page = "nix" page = "nix"
elseif fields.food then elseif fields.food then
if players[name].page == "food" then return end if players[name].page == "food" then return end
set_inv_page("food",player) set_inv_page("food", player)
page = "food" page = "food"
elseif fields.tools then elseif fields.tools then
if players[name].page == "tools" then return end if players[name].page == "tools" then return end
set_inv_page("tools",player) set_inv_page("tools", player)
page = "tools" page = "tools"
elseif fields.combat then elseif fields.combat then
if players[name].page == "combat" then return end if players[name].page == "combat" then return end
set_inv_page("combat",player) set_inv_page("combat", player)
page = "combat" page = "combat"
elseif fields.mobs then elseif fields.mobs then
if players[name].page == "mobs" then return end if players[name].page == "mobs" then return end
set_inv_page("mobs",player) set_inv_page("mobs", player)
page = "mobs" page = "mobs"
elseif fields.brew then elseif fields.brew then
if players[name].page == "brew" then return end if players[name].page == "brew" then return end
set_inv_page("brew",player) set_inv_page("brew", player)
page = "brew" page = "brew"
elseif fields.matr then elseif fields.matr then
if players[name].page == "matr" then return end if players[name].page == "matr" then return end
set_inv_page("matr",player) set_inv_page("matr", player)
page = "matr" page = "matr"
elseif fields.inv then elseif fields.inv then
if players[name].page == "inv" then return end 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) set_inv_page("all", player)
page = "nix" page = "nix"
elseif fields.search and not fields.creative_next and not fields.creative_prev then 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" page = "nix"
elseif fields.__switch_stack then elseif fields.__switch_stack then
local switch = 1 local switch = 1
@ -570,20 +686,20 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local start_i = players[name].start_i local start_i = players[name].start_i
if fields.creative_prev then if fields.creative_prev then
start_i = start_i - 9*5 start_i = start_i - 9 * 5
elseif fields.creative_next then elseif fields.creative_next then
start_i = start_i + 9*5 start_i = start_i + 9 * 5
else else
-- Reset scroll bar if not scrolled -- Reset scroll bar if not scrolled
start_i = 0 start_i = 0
end end
if start_i < 0 then if start_i < 0 then
start_i = start_i + 9*5 start_i = start_i + 9 * 5
end end
local inv_size local inv_size
if page == "nix" then 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") inv_size = inv:get_size("main")
elseif page and page ~= "inv" then elseif page and page ~= "inv" then
inv_size = #(inventory_lists[page]) 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 players[name].inv_size = inv_size
if start_i >= inv_size then if start_i >= inv_size then
start_i = start_i - 9*5 start_i = start_i - 9 * 5
end end
if start_i < 0 or start_i >= inv_size then if start_i < 0 or start_i >= inv_size then
start_i = 0 start_i = 0
@ -609,11 +725,34 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
mcl_inventory.set_creative_formspec(player) mcl_inventory.set_creative_formspec(player)
end) end)
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack) 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()) return placer and placer:is_player() and minetest.is_creative_enabled(placer:get_player_name())
end) 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) minetest.register_on_joinplayer(function(player)
-- Initialize variables and inventory -- Initialize variables and inventory
local name = player:get_player_name() local name = player:get_player_name()
@ -629,7 +768,8 @@ minetest.register_on_joinplayer(function(player)
end) end)
minetest.register_on_player_inventory_action(function(player, action, inventory, inventory_info) 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 local stack = inventory_info.stack
stack:set_count(stack:get_stack_max()) stack:set_count(stack:get_stack_max())
player:get_inventory():set_stack("main", inventory_info.index, stack) 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 = {} 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_player = minetest.get_modpath("mcl_player")
--local mod_craftguide = minetest.get_modpath("mcl_craftguide") --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 ---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) ---@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 if dropper:is_player() then
-- Return to main inventory -- Return to main inventory
if inv:room_for_item("main", itemstack) then if inv:room_for_item("main", itemstack) then
@ -15,14 +19,14 @@ function return_item(itemstack, dropper, pos, inv)
else else
-- Drop item on the ground -- Drop item on the ground
local v = dropper:get_look_dir() local v = dropper:get_look_dir()
local p = {x=pos.x, y=pos.y+1.2, z=pos.z} local p = vector.offset(pos, 0, 1.2, 0)
p.x = p.x+(math.random(1,3)*0.2) p.x = p.x + (math.random(1, 3) * 0.2)
p.z = p.z+(math.random(1,3)*0.2) p.z = p.z + (math.random(1, 3) * 0.2)
local obj = minetest.add_item(p, itemstack) local obj = minetest.add_item(p, itemstack)
if obj then if obj then
v.x = v.x*4 v.x = v.x * 4
v.y = v.y*4 + 2 v.y = v.y * 4 + 2
v.z = v.z*4 v.z = v.z * 4
obj:set_velocity(v) obj:set_velocity(v)
obj:get_luaentity()._insta_collect = false obj:get_luaentity()._insta_collect = false
end end
@ -34,111 +38,53 @@ function return_item(itemstack, dropper, pos, inv)
return itemstack return itemstack
end end
-- Return items in the given inventory list (name) to the main inventory, or drop them if there is no space left ---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) ---@param player mt.PlayerObjectRef
---@param name string
local function return_fields(player, name)
local inv = player:get_inventory() local inv = player:get_inventory()
local list = inv:get_list(name) local list = inv:get_list(name)
if not list then return end 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) return_item(stack, player, player:get_pos(), inv)
stack:clear() stack:clear()
inv:set_stack(name, i, stack) inv:set_stack(name, i, stack)
end end
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 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 return
end end
local inv = player:get_inventory()
inv:set_width("craft", 2)
inv:set_size("craft", 4)
local armor_slots = {"helmet", "chestplate", "leggings", "boots"} player:set_inventory_formspec(mcl_inventory.build_survival_formspec(player))
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)
end end
-- Drop items in craft grid and reset inventory on closing -- Drop items in craft grid and reset inventory on closing
minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.quit then if fields.quit then
return_fields(player,"craft") return_fields(player, "craft")
return_fields(player,"enchanting_lapis") return_fields(player, "enchanting_lapis")
return_fields(player,"enchanting_item") return_fields(player, "enchanting_item")
if not minetest.is_creative_enabled(player:get_player_name()) and (formname == "" or formname == "main") then if not minetest.is_creative_enabled(player:get_player_name()) and (formname == "" or formname == "main") then
set_inventory(player) set_inventory(player)
end end
end 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 -- Drop crafting grid items on leaving
minetest.register_on_leaveplayer(function(player) minetest.register_on_leaveplayer(function(player)
@ -150,6 +96,7 @@ end)
minetest.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
--init inventory --init inventory
local inv = player:get_inventory() local inv = player:get_inventory()
inv:set_width("main", 9) inv:set_width("main", 9)
inv:set_size("main", 36) inv:set_size("main", 36)
inv:set_size("offhand", 1) inv:set_size("offhand", 1)
@ -174,73 +121,18 @@ minetest.register_on_joinplayer(function(player)
return_fields(player, "enchanting_lapis") return_fields(player, "enchanting_lapis")
end) 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) 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 name = mcl_inventory
author = BlockMen author = BlockMen
description = Adds the player inventory and creative inventory. description = Adds the player inventory and creative inventory.
depends = mcl_init, mcl_formspec, mcl_enchanting, mcl_player depends = mcl_init, mcl_formspec, mcl_enchanting, mcl_gamemode
optional_depends = mcl_armor, mcl_brewing, mcl_potions, mcl_enchanting, mcl_craftguide 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. are so many weird tables below.
]] ]]
local S = minetest.get_translator(minetest.get_current_modname()) 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) local function setup_dispenser(pos)
-- Set formspec and inventory -- 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) local meta = minetest.get_meta(pos)
meta:set_string("formspec", form) meta:set_string("formspec", dispenser_formspec)
local inv = meta:get_inventory() local inv = meta:get_inventory()
inv:set_size("main", 9) inv:set_size("main", 9)
end end
@ -38,9 +51,9 @@ local function orientate_dispenser(pos, placer)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
if pitch > 55 then 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 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
end end
@ -85,11 +98,10 @@ local dispenserdef = {
local meta2 = meta:to_table() local meta2 = meta:to_table()
meta:from_table(oldmetadata) meta:from_table(oldmetadata)
local inv = meta:get_inventory() 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) local stack = inv:get_stack("main", i)
if not stack:is_empty() then 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(vector.offset(pos, math.random(0, 10) / 10 - 0.5, 0, math.random(0, 10) / 10 - 0.5), stack)
minetest.add_item(p, stack)
end end
end end
meta:from_table(meta2) meta:from_table(meta2)
@ -107,19 +119,19 @@ local dispenserdef = {
dropdir = vector.multiply(minetest.facedir_to_dir(node.param2), -1) dropdir = vector.multiply(minetest.facedir_to_dir(node.param2), -1)
droppos = vector.add(pos, dropdir) droppos = vector.add(pos, dropdir)
elseif node.name == "mcl_dispensers:dispenser_up" then elseif node.name == "mcl_dispensers:dispenser_up" then
dropdir = {x=0, y=1, z=0} dropdir = vector.new(0, 1, 0)
droppos = {x=pos.x, y=pos.y+1, z=pos.z} droppos = vector.offset(pos, 0, 1, 0)
elseif node.name == "mcl_dispensers:dispenser_down" then elseif node.name == "mcl_dispensers:dispenser_down" then
dropdir = {x=0, y=-1, z=0} dropdir = vector.new(0, -1, 0)
droppos = {x=pos.x, y=pos.y-1, z=pos.z} droppos = vector.offset(pos, 0, -1, 0)
end end
local dropnode = minetest.get_node(droppos) local dropnode = minetest.get_node(droppos)
local dropnodedef = minetest.registered_nodes[dropnode.name] local dropnodedef = minetest.registered_nodes[dropnode.name]
local stacks = {} 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) local stack = inv:get_stack("main", i)
if not stack:is_empty() then if not stack:is_empty() then
table.insert(stacks, {stack = stack, stackpos = i}) table.insert(stacks, { stack = stack, stackpos = i })
end end
end end
if #stacks >= 1 then if #stacks >= 1 then
@ -143,9 +155,10 @@ local dispenserdef = {
-- Armor, mob heads and pumpkins -- Armor, mob heads and pumpkins
if igroups.armor then 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 for _, obj in ipairs(objs) do
stack = mcl_armor.equip(stack, obj) stack = mcl_armor.equip(stack, obj)
if stack:is_empty() then if stack:is_empty() then
@ -157,11 +170,11 @@ local dispenserdef = {
end end
end end
-- Place head or pumpkin as node, if equipping it as armor has failed -- Place head or pumpkin as node, if equipping it as armor has failed
if not stack:is_empty() then if not stack:is_empty() then
if igroups.head or iname == "mcl_farming:pumpkin_face" then if igroups.head or iname == "mcl_farming:pumpkin_face" then
if dropnodedef.buildable_to 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() stack:take_item()
end end
end end
@ -169,7 +182,7 @@ local dispenserdef = {
inv:set_stack("main", stack_id, stack) inv:set_stack("main", stack_id, stack)
-- Use shears on sheeps -- Use shears on sheeps
elseif igroups.shears then elseif igroups.shears then
for _, obj in pairs(minetest.get_objects_inside_radius(droppos, 1)) do for _, obj in pairs(minetest.get_objects_inside_radius(droppos, 1)) do
local entity = obj:get_luaentity() local entity = obj:get_luaentity()
@ -220,7 +233,7 @@ local dispenserdef = {
end end
end end
-- Spawn Egg -- Spawn Egg
elseif igroups.spawn_egg then elseif igroups.spawn_egg then
-- Spawn mob -- Spawn mob
if not dropnodedef.walkable then if not dropnodedef.walkable then
@ -231,7 +244,7 @@ local dispenserdef = {
inv:set_stack("main", stack_id, stack) inv:set_stack("main", stack_id, stack)
end end
-- Generalized dispension -- Generalized dispension
elseif (not dropnodedef.walkable or stackdef._dispense_into_walkable) then elseif (not dropnodedef.walkable or stackdef._dispense_into_walkable) then
--[[ _on_dispense(stack, pos, droppos, dropnode, dropdir) --[[ _on_dispense(stack, pos, droppos, dropnode, dropdir)
* stack: Itemstack which is dispense * stack: Itemstack which is dispense
@ -263,7 +276,7 @@ local dispenserdef = {
local item_entity = minetest.add_item(droppos, dropitem) local item_entity = minetest.add_item(droppos, dropitem)
local drop_vel = vector.subtract(droppos, pos) local drop_vel = vector.subtract(droppos, pos)
local speed = 3 local speed = 3
item_entity:set_velocity(vector.multiply(drop_vel,speed)) item_entity:set_velocity(vector.multiply(drop_vel, speed))
end end
else else
stack:take_item() stack:take_item()
@ -280,7 +293,7 @@ local dispenserdef = {
local item_entity = minetest.add_item(droppos, dropitem) local item_entity = minetest.add_item(droppos, dropitem)
local drop_vel = vector.subtract(droppos, pos) local drop_vel = vector.subtract(droppos, pos)
local speed = 3 local speed = 3
item_entity:set_velocity(vector.multiply(drop_vel,speed)) item_entity:set_velocity(vector.multiply(drop_vel, speed))
stack:take_item() stack:take_item()
inv:set_stack("main", stack_id, stack) inv:set_stack("main", stack_id, stack)
end end
@ -299,27 +312,28 @@ local dispenserdef = {
local horizontal_def = table.copy(dispenserdef) local horizontal_def = table.copy(dispenserdef)
horizontal_def.description = S("Dispenser") 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_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("• Arrows: Are launched") .. "\n" ..
S("• Eggs and snowballs: Are thrown").."\n".. S("• Eggs and snowballs: Are thrown") .. "\n" ..
S("• Fire charges: Are fired in a straight line").."\n".. S("• Fire charges: Are fired in a straight line") .. "\n" ..
S("• Armor: Will be equipped to players and armor stands").."\n".. S("• Armor: Will be equipped to players and armor stands") .. "\n" ..
S("• Boats: Are placed on water or are dropped").."\n".. S("• Boats: Are placed on water or are dropped") .. "\n" ..
S("• Minecart: Are placed on rails 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("• Bone meal: Is applied on the block it is facing") .. "\n" ..
S("• Empty buckets: Are used to collect a liquid source").."\n".. S("• Empty buckets: Are used to collect a liquid source") .. "\n" ..
S("• Filled buckets: Are used to place 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("• Heads, pumpkins: Equipped to players and armor stands, or placed as a block") .. "\n" ..
S("• Shulker boxes: Are placed as a block").."\n".. S("• Shulker boxes: Are placed as a block") .. "\n" ..
S("• TNT: Is placed and ignited").."\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("• 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("• Spawn eggs: Will summon the mob they contain") .. "\n" ..
S("• Other items: Are simply dropped") S("• Other items: Are simply dropped")
function horizontal_def.after_place_node(pos, placer, itemstack, pointed_thing) function horizontal_def.after_place_node(pos, placer, itemstack, pointed_thing)
setup_dispenser(pos) setup_dispenser(pos)
@ -332,7 +346,7 @@ horizontal_def.tiles = {
"default_furnace_side.png", "mcl_dispensers_dispenser_front_horizontal.png" "default_furnace_side.png", "mcl_dispensers_dispenser_front_horizontal.png"
} }
horizontal_def.paramtype2 = "facedir" 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) 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",
"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._doc_items_create_entry = false
down_def.drop = "mcl_dispensers:dispenser" down_def.drop = "mcl_dispensers:dispenser"
minetest.register_node("mcl_dispensers:dispenser_down", down_def) 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({ minetest.register_craft({
output = "mcl_dispensers:dispenser", output = "mcl_dispensers:dispenser",
recipe = { recipe = {
{"mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble",}, { "mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble", },
{"mcl_core:cobble", "mcl_bows:bow", "mcl_core:cobble",}, { "mcl_core:cobble", "mcl_bows:bow", "mcl_core:cobble", },
{"mcl_core:cobble", "mesecons:redstone", "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" }, nodenames = { "mcl_dispensers:dispenser", "mcl_dispensers:dispenser_down", "mcl_dispensers:dispenser_up" },
action = function(pos, node) action = function(pos, node)
setup_dispenser(pos) 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, end,
}) })

@ -9,23 +9,36 @@ are so many weird tables below.
]] ]]
local S = minetest.get_translator(minetest.get_current_modname()) 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) local function setup_dropper(pos)
-- Set formspec and inventory -- 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) local meta = minetest.get_meta(pos)
meta:set_string("formspec", form) meta:set_string("formspec", dropper_formspec)
local inv = meta:get_inventory() local inv = meta:get_inventory()
inv:set_size("main", 9) inv:set_size("main", 9)
end end
@ -38,9 +51,9 @@ local function orientate_dropper(pos, placer)
local pitch = placer:get_look_vertical() * (180 / math.pi) local pitch = placer:get_look_vertical() * (180 / math.pi)
if pitch > 55 then 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 elseif pitch < -55 then
minetest.swap_node(pos, {name="mcl_droppers:dropper_down"}) minetest.swap_node(pos, { name = "mcl_droppers:dropper_down" })
end end
end end
@ -58,11 +71,10 @@ local dropperdef = {
local meta2 = meta:to_table() local meta2 = meta:to_table()
meta:from_table(oldmetadata) meta:from_table(oldmetadata)
local inv = meta:get_inventory() 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) local stack = inv:get_stack("main", i)
if not stack:is_empty() then 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(vector.offset(pos, math.random(0, 10) / 10 - 0.5, 0, math.random(0, 10) / 10 - 0.5), stack)
minetest.add_item(p, stack)
end end
end end
meta:from_table(meta2) meta:from_table(meta2)
@ -96,7 +108,7 @@ local dropperdef = {
end, end,
_mcl_blast_resistance = 3.5, _mcl_blast_resistance = 3.5,
_mcl_hardness = 3.5, _mcl_hardness = 3.5,
mesecons = {effector = { mesecons = { effector = {
-- Drop random item when triggered -- Drop random item when triggered
action_on = function(pos, node) action_on = function(pos, node)
if not pos then return end if not pos then return end
@ -104,11 +116,11 @@ local dropperdef = {
local inv = meta:get_inventory() local inv = meta:get_inventory()
local droppos local droppos
if node.name == "mcl_droppers:dropper" then if node.name == "mcl_droppers:dropper" then
droppos = vector.subtract(pos, minetest.facedir_to_dir(node.param2)) droppos = vector.subtract(pos, minetest.facedir_to_dir(node.param2))
elseif node.name == "mcl_droppers:dropper_up" then 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 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 end
local dropnode = minetest.get_node(droppos) local dropnode = minetest.get_node(droppos)
-- Do not drop into solid nodes, unless they are containers -- Do not drop into solid nodes, unless they are containers
@ -117,10 +129,10 @@ local dropperdef = {
return return
end end
local stacks = {} 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) local stack = inv:get_stack("main", i)
if not stack:is_empty() then if not stack:is_empty() then
table.insert(stacks, {stack = stack, stackpos = i}) table.insert(stacks, { stack = stack, stackpos = i })
end end
end end
if #stacks >= 1 then if #stacks >= 1 then
@ -136,22 +148,22 @@ local dropperdef = {
if not dropped and not dropnodedef.groups.container then if not dropped and not dropnodedef.groups.container then
-- Drop item normally -- Drop item normally
local pos_variation = 100 local pos_variation = 100
droppos = { droppos = vector.offset(droppos,
x = droppos.x + math.random(-pos_variation, pos_variation) / 1000, math.random(-pos_variation, pos_variation) / 1000,
y = droppos.y + math.random(-pos_variation, pos_variation) / 1000, math.random(-pos_variation, pos_variation) / 1000,
z = droppos.z + math.random(-pos_variation, pos_variation) / 1000, math.random(-pos_variation, pos_variation) / 1000
} )
local item_entity = minetest.add_item(droppos, dropitem) local item_entity = minetest.add_item(droppos, dropitem)
local drop_vel = vector.subtract(droppos, pos) local drop_vel = vector.subtract(droppos, pos)
local speed = 3 local speed = 3
item_entity:set_velocity(vector.multiply(drop_vel,speed)) item_entity:set_velocity(vector.multiply(drop_vel, speed))
stack:take_item() stack:take_item()
inv:set_stack("main", stack_id, stack) inv:set_stack("main", stack_id, stack)
end end
end end
end, end,
rules = mesecon.rules.alldirs, rules = mesecon.rules.alldirs,
}}, } },
on_rotate = on_rotate, on_rotate = on_rotate,
} }
@ -159,20 +171,21 @@ local dropperdef = {
local horizontal_def = table.copy(dropperdef) local horizontal_def = table.copy(dropperdef)
horizontal_def.description = S("Dropper") 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_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.") 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) function horizontal_def.after_place_node(pos, placer, itemstack, pointed_thing)
setup_dropper(pos) setup_dropper(pos)
orientate_dropper(pos, placer) orientate_dropper(pos, placer)
end end
horizontal_def.tiles = { horizontal_def.tiles = {
"default_furnace_top.png", "default_furnace_bottom.png", "default_furnace_top.png", "default_furnace_bottom.png",
"default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png",
"default_furnace_side.png", "mcl_droppers_dropper_front_horizontal.png" "default_furnace_side.png", "mcl_droppers_dropper_front_horizontal.png"
} }
horizontal_def.paramtype2 = "facedir" 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) 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",
"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._doc_items_create_entry = false
down_def.drop = "mcl_droppers:dropper" down_def.drop = "mcl_droppers:dropper"
minetest.register_node("mcl_droppers:dropper_down", down_def) 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({ minetest.register_craft({
output = "mcl_droppers:dropper", output = "mcl_droppers:dropper",
recipe = { recipe = {
{"mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble",}, { "mcl_core:cobble", "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",}, { "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" }, nodenames = { "mcl_droppers:dropper", "mcl_droppers:dropper_down", "mcl_droppers:dropper_up" },
action = function(pos, node) action = function(pos, node)
setup_dropper(pos) 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, end,
}) })

@ -1,4 +1,6 @@
local S = minetest.get_translator(minetest.get_current_modname()) 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_NAME_LENGTH = 35
local MAX_WEAR = 65535 local MAX_WEAR = 65535
@ -10,41 +12,65 @@ local MATERIAL_TOOL_REPAIR_BOOST = {
MAX_WEAR, -- 100% MAX_WEAR, -- 100%
} }
---@param set_name? string
local function get_anvil_formspec(set_name) local function get_anvil_formspec(set_name)
if not set_name then if not set_name then
set_name = "" set_name = ""
end end
return "size[9,8.75]"..
"background[-0.19,-0.25;9.41,9.49;mcl_anvils_inventory.png]".. return table.concat({
"label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]".. "formspec_version[4]",
"list[current_player;main;0,4.5;9,3;9]".. "size[11.75,10.425]",
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]".. "label[4.125,0.375;" .. F(C(mcl_formspec.label_color, S("Repair and Name"))) .. "]",
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"list[context;input;1,2.5;1,1;]".. "image[0.875,0.375;1.75,1.75;mcl_anvils_inventory_hammer.png]",
mcl_formspec.get_itemslot_bg(1,2.5,1,1)..
"list[context;input;4,2.5;1,1;1]".. "field[4.125,0.75;7.25,1;name;;" .. F(set_name) .. "]",
mcl_formspec.get_itemslot_bg(4,2.5,1,1).. "field_close_on_enter[name;false]",
"list[context;output;8,2.5;1,1;]".. "set_focus[name;true]",
mcl_formspec.get_itemslot_bg(8,2.5,1,1)..
"label[3,0.1;"..minetest.formspec_escape(minetest.colorize("#313131", S("Repair and Name"))).."]".. mcl_formspec.get_itemslot_bg_v4(1.625, 2.6, 1, 1),
"field[3.25,1;4,1;name;;"..minetest.formspec_escape(set_name).."]".. "list[context;input;1.625,2.6;1,1;]",
"field_close_on_enter[name;false]"..
"button[7,0.7;2,1;name_button;"..minetest.formspec_escape(S("Set Name")).."]".. "image[3.5,2.6;1,1;mcl_anvils_inventory_cross.png]",
"listring[context;output]"..
"listring[current_player;main]".. mcl_formspec.get_itemslot_bg_v4(5.375, 2.6, 1, 1),
"listring[context;input]".. "list[context;input;5.375,2.6;1,1;1]",
"listring[current_player;main]"
"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 end
-- Given a tool and material stack, returns how many items of the material stack -- Given a tool and material stack, returns how many items of the material stack
-- needs to be used up to repair the tool. -- 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 function get_consumed_materials(tool, material)
local wear = tool:get_wear() local wear = tool:get_wear()
--local health = (MAX_WEAR - wear) --local health = (MAX_WEAR - wear)
local matsize = material:get_count() local matsize = material:get_count()
local materials_used = 0 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 materials_used = materials_used + 1
if (wear - MATERIAL_TOOL_REPAIR_BOOST[m]) <= 0 then if (wear - MATERIAL_TOOL_REPAIR_BOOST[m]) <= 0 then
break break
@ -53,27 +79,20 @@ local function get_consumed_materials(tool, material)
return materials_used return materials_used
end 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. -- 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 ("tool", input1, input2) if input1 is tool and input2 is material.
-- Returns ("material", input2, input1) if input1 is material and input2 is tool. -- Returns ("material", input2, input1) if input1 is material and input2 is tool.
-- Returns nil otherwise. -- Returns nil otherwise.
---@param input1 ItemStack
---@param input2 ItemStack
local function distinguish_tool_and_material(input1, input2) local function distinguish_tool_and_material(input1, input2)
local def1 = input1:get_definition() local def1 = input1:get_definition()
local def2 = input2:get_definition() local def2 = input2:get_definition()
local r1 = def1._repair_material local r1 = def1._repair_material
local r2 = def2._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 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 return "material", input2, input1
elseif def1.type == "tool" and r1 then elseif def1.type == "tool" and r1 then
return "tool", input1, input2 return "tool", input1, input2
@ -84,7 +103,8 @@ local function distinguish_tool_and_material(input1, input2)
end end
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) local function fix_stack_size(stack)
if not stack or stack == "" then return "" end if not stack or stack == "" then return "" end
local count = stack:get_count() local count = stack:get_count()
@ -99,6 +119,7 @@ end
-- Update the inventory slots of an anvil node. -- Update the inventory slots of an anvil node.
-- meta: Metadata of anvil node -- meta: Metadata of anvil node
---@param meta NodeMetaRef
local function update_anvil_slots(meta) local function update_anvil_slots(meta)
local inv = meta:get_inventory() local inv = meta:get_inventory()
local new_name = meta:get_string("set_name") local new_name = meta:get_string("set_name")
@ -137,7 +158,7 @@ local function update_anvil_slots(meta)
name_item = input1 name_item = input1
new_output = name_item new_output = name_item
-- Tool + repair item -- Tool + repair item
else else
-- Any tool can have a repair item. This may be defined in the tool's item definition -- Any tool can have a repair item. This may be defined in the tool's item definition
-- as an itemstring in the field `_repair_material`. Only if this field is set, the -- as an itemstring in the field `_repair_material`. Only if this field is set, the
@ -159,7 +180,7 @@ local function update_anvil_slots(meta)
has_correct_material = true has_correct_material = true
end end
else else
if contains(repair, material_name) then if table.indexof(repair, material_name) ~= -1 then
has_correct_material = true has_correct_material = true
else else
for _, r in pairs(repair) do for _, r in pairs(repair) do
@ -185,7 +206,7 @@ local function update_anvil_slots(meta)
new_output = "" new_output = ""
end end
end end
-- Exactly 1 input slot occupied -- Exactly 1 input slot occupied
elseif (not input1:is_empty() and input2:is_empty()) or (input1:is_empty() and not input2:is_empty()) then elseif (not input1:is_empty() and input2:is_empty()) or (input1:is_empty() and not input2:is_empty()) then
-- Just rename item -- Just rename item
if input1:is_empty() then if input1:is_empty() then
@ -231,28 +252,32 @@ local function update_anvil_slots(meta)
end end
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 function drop_anvil_items(pos, meta)
local inv = meta:get_inventory() 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) local stack = inv:get_stack("input", i)
if not stack:is_empty() then 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) minetest.add_item(p, stack)
end end
end end
end end
---@param pos Vector
---@param node node
local function damage_particles(pos, node) local function damage_particles(pos, node)
minetest.add_particlespawner({ minetest.add_particlespawner({
amount = 30, amount = 30,
time = 0.1, time = 0.1,
minpos = vector.add(pos, {x=-0.5, y=-0.5, z=-0.5}), minpos = vector.offset(pos, -0.5, -0.5, -0.5),
maxpos = vector.add(pos, {x=0.5, y=-0.25, z=0.5}), maxpos = vector.offset(pos, 0.5, -0.25, 0.5),
minvel = {x=-0.5, y=0.05, z=-0.5}, minvel = vector.new(-0.5, 0.05, -0.5),
maxvel = {x=0.5, y=0.3, z=0.5}, maxvel = vector.new(0.5, 0.3, 0.5),
minacc = {x=0, y=-9.81, z=0}, minacc = vector.new(0, -9.81, 0),
maxacc = {x=0, y=-9.81, z=0}, maxacc = vector.new(0, -9.81, 0),
minexptime = 0.1, minexptime = 0.1,
maxexptime = 0.5, maxexptime = 0.5,
minsize = 0.4, minsize = 0.4,
@ -267,12 +292,12 @@ local function destroy_particles(pos, node)
minetest.add_particlespawner({ minetest.add_particlespawner({
amount = math.random(20, 30), amount = math.random(20, 30),
time = 0.1, time = 0.1,
minpos = vector.add(pos, {x=-0.4, y=-0.4, z=-0.4}), minpos = vector.offset(pos, -0.4, -0.4, -0.4),
maxpos = vector.add(pos, {x=0.4, y=0.4, z=0.4}), maxpos = vector.offset(pos, 0.4, 0.4, 0.4),
minvel = {x=-0.5, y=-0.1, z=-0.5}, minvel = vector.new(-0.5, -0.1, -0.5),
maxvel = {x=0.5, y=0.2, z=0.5}, maxvel = vector.new(0.5, 0.2, 0.5),
minacc = {x=0, y=-9.81, z=0}, minacc = vector.new(0, -9.81, 0),
maxacc = {x=0, y=-9.81, z=0}, maxacc = vector.new(0, -9.81, 0),
minexptime = 0.2, minexptime = 0.2,
maxexptime = 0.65, maxexptime = 0.65,
minsize = 0.8, minsize = 0.8,
@ -289,28 +314,29 @@ end
local function damage_anvil(pos) local function damage_anvil(pos)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
if node.name == "mcl_anvils:anvil" then 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) 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 return false
elseif node.name == "mcl_anvils:anvil_damage_1" then 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) 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 return false
elseif node.name == "mcl_anvils:anvil_damage_2" then elseif node.name == "mcl_anvils:anvil_damage_2" then
-- Destroy anvil -- Destroy anvil
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
drop_anvil_items(pos, meta) 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) minetest.remove_node(pos)
destroy_particles(pos, node) 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 return true
end end
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 function damage_anvil_by_using(pos)
local r = math.random(1, 100) local r = math.random(1, 100)
-- 12% chance -- 12% chance
@ -321,25 +347,30 @@ local function damage_anvil_by_using(pos)
end end
end end
---@param pos Vector
---@param distance number
local function damage_anvil_by_falling(pos, distance) local function damage_anvil_by_falling(pos, distance)
local r = math.random(1, 100) local r = math.random(1, 100)
if distance > 1 then if distance > 1 then
if r <= (5*distance) then if r <= (5 * distance) then
damage_anvil(pos) damage_anvil(pos)
end end
end end
end end
---@type nodebox
local anvilbox = { local anvilbox = {
type = "fixed", type = "fixed",
fixed = { fixed = {
{ -8 / 16, -8 / 16, -6 / 16, 8 / 16, 8 / 16, 6 / 16 }, { -8 / 16, -8 / 16, -6 / 16, 8 / 16, 8 / 16, 6 / 16 },
}, },
} }
---@type node_definition
local anvildef = { local anvildef = {
groups = {pickaxey=1, falling_node=1, falling_node_damage=1, crush_after_fall=1, deco_block=1, anvil=1}, 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"}, 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, use_texture_alpha = "opaque",
_tt_help = S("Repair and rename items"), _tt_help = S("Repair and rename items"),
paramtype = "light", paramtype = "light",
sunlight_propagates = true, sunlight_propagates = true,
@ -353,7 +384,7 @@ local anvildef = {
{ -5 / 16, -4 / 16, -4 / 16, 5 / 16, -3 / 16, 4 / 16 }, { -5 / 16, -4 / 16, -4 / 16, 5 / 16, -3 / 16, 4 / 16 },
{ -4 / 16, -3 / 16, -2 / 16, 4 / 16, 2 / 16, 2 / 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 }, { -8 / 16, 2 / 16, -5 / 16, 8 / 16, 8 / 16, 5 / 16 },
} },
}, },
selection_box = anvilbox, selection_box = anvilbox,
collision_box = anvilbox, collision_box = anvilbox,
@ -416,7 +447,7 @@ local anvildef = {
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
if from_list == "output" and to_list == "input" then if from_list == "output" and to_list == "input" then
local inv = meta:get_inventory() 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 if i ~= to_index then
local istack = inv:get_stack("input", i) local istack = inv:get_stack("input", i)
istack:set_count(math.max(0, istack:get_count() - count)) istack:set_count(math.max(0, istack:get_count() - count))
@ -504,22 +535,20 @@ local anvildef = {
minetest.record_protection_violation(pos, sender_name) minetest.record_protection_violation(pos, sender_name)
return return
end end
if fields.name_button or fields.name then
local set_name if fields.name then
if fields.name == nil then
set_name = ""
else
set_name = fields.name
end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
-- Limit name length -- 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) meta:set_string("set_name", set_name)
update_anvil_slots(meta) update_anvil_slots(meta)
meta:set_string("formspec", get_anvil_formspec(set_name)) meta:set_string("formspec", get_anvil_formspec(set_name))
end end
end, end,
} }
if minetest.get_modpath("screwdriver") then if minetest.get_modpath("screwdriver") then
anvildef.on_rotate = screwdriver.rotate_simple anvildef.on_rotate = screwdriver.rotate_simple
end end
@ -527,29 +556,34 @@ end
local anvildef0 = table.copy(anvildef) local anvildef0 = table.copy(anvildef)
anvildef0.description = S("Anvil") anvildef0.description = S("Anvil")
anvildef0._doc_items_longdesc = 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 = 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 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("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.")
S("There are two possibilities to repair tools (and armor):").."\n".. .. "\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("There are two possibilities to repair tools (and armor):") .. "\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("• 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.")
S("Armor counts as a tool. It is possible to repair and rename a tool in a single step.").."\n\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("• 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) local anvildef1 = table.copy(anvildef)
anvildef1.description = S("Slightly Damaged Anvil") anvildef1.description = S("Slightly Damaged Anvil")
anvildef1._doc_items_create_entry = false anvildef1._doc_items_create_entry = false
anvildef1.groups.anvil = 2 anvildef1.groups.anvil = 2
anvildef1._doc_items_create_entry = false 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) local anvildef2 = table.copy(anvildef)
anvildef2.description = S("Very Damaged Anvil") anvildef2.description = S("Very Damaged Anvil")
anvildef2._doc_items_create_entry = false anvildef2._doc_items_create_entry = false
anvildef2.groups.anvil = 3 anvildef2.groups.anvil = 3
anvildef2._doc_items_create_entry = false 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", anvildef0)
minetest.register_node("mcl_anvils:anvil_damage_1", anvildef1) 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:ironblock", "mcl_core:ironblock", "mcl_core:ironblock" },
{ "", "mcl_core:iron_ingot", "" }, { "", "mcl_core:iron_ingot", "" },
{ "mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot" }, { "mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot" },
} },
}) })
end end

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

@ -1,5 +1,6 @@
local S = minetest.get_translator(minetest.get_current_modname()) local S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
local F = minetest.formspec_escape
local LIGHT_ACTIVE_FURNACE = 13 local LIGHT_ACTIVE_FURNACE = 13
@ -8,60 +9,82 @@ local LIGHT_ACTIVE_FURNACE = 13
-- --
local function active_formspec(fuel_percent, item_percent) local function active_formspec(fuel_percent, item_percent)
return "size[9,8.75]".. return table.concat({
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]".. "formspec_version[4]",
"list[current_player;main;0,4.5;9,3;9]".. "size[11.75,10.425]",
mcl_formspec.get_itemslot_bg(0,4.5,9,3).. "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Blast Furnace"))) .. "]",
"list[current_player;main;0,7.74;9,1;]".. mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
mcl_formspec.get_itemslot_bg(0,7.74,9,1).. "list[context;src;3.5,0.75;1,1;]",
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Blast Furnace"))).."]"..
"list[context;src;2.75,0.5;1,1;]".. "image[3.5,2;1,1;default_furnace_fire_bg.png^[lowpart:" ..
mcl_formspec.get_itemslot_bg(2.75,0.5,1,1).. (100 - fuel_percent) .. ":default_furnace_fire_fg.png]",
"list[context;fuel;2.75,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1).. mcl_formspec.get_itemslot_bg_v4(3.5, 3.25, 1, 1),
"list[context;dst;5.75,1.5;1,1;]".. "list[context;fuel;3.5,3.25;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:".. "image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[lowpart:" ..
(100-fuel_percent)..":default_furnace_fire_fg.png]".. (item_percent) .. ":gui_furnace_arrow_fg.png^[transformR270]",
"image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[lowpart:".. mcl_formspec.get_itemslot_bg_v4(7.875, 2, 1, 1, 0.2),
(item_percent)..":gui_furnace_arrow_fg.png^[transformR270]".. "list[context;dst;7.875,2;1,1;]",
-- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed. "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]".. mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]".. "list[current_player;main;0.375,5.1;9,3;9]",
"listring[context;dst]"..
"listring[current_player;main]".. mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"listring[context;src]".. "list[current_player;main;0.375,9.05;9,1;]",
"listring[current_player;main]"..
"listring[context;fuel]".. -- Craft guide button temporarily removed due to Minetest bug.
"listring[current_player;main]" -- 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]",
})
end end
local inactive_formspec = "size[9,8.75]".. local inactive_formspec = table.concat({
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]".. "formspec_version[4]",
"list[current_player;main;0,4.5;9,3;9]".. "size[11.75,10.425]",
mcl_formspec.get_itemslot_bg(0,4.5,9,3).. "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Blast Furnace"))) .. "]",
"list[current_player;main;0,7.74;9,1;]".. mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
mcl_formspec.get_itemslot_bg(0,7.74,9,1).. "list[context;src;3.5,0.75;1,1;]",
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Blast Furnace"))).."]"..
"list[context;src;2.75,0.5;1,1;]".. "image[3.5,2;1,1;default_furnace_fire_bg.png]",
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_v4(3.5, 3.25, 1, 1),
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1).. "list[context;fuel;3.5,3.25;1,1;]",
"list[context;dst;5.75,1.5;1,1;]"..
mcl_formspec.get_itemslot_bg(5.75,1.5,1,1).. "image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[transformR270]",
"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]".. 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. -- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed. -- TODO: Add it back when the Minetest bug is fixed.
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]".. --"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]".. --"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
"listring[context;dst]"..
"listring[current_player;main]".. "listring[context;dst]",
"listring[context;src]".. "listring[current_player;main]",
"listring[current_player;main]".. "listring[context;src]",
"listring[context;fuel]".. "listring[current_player;main]",
"listring[current_player;main]" "listring[context;fuel]",
"listring[current_player;main]",
})
local receive_fields = function(pos, formname, fields, sender) local receive_fields = function(pos, formname, fields, sender)
if fields.craftguide then if fields.craftguide then
@ -71,7 +94,7 @@ end
local function give_xp(pos, player) local function give_xp(pos, player)
local meta = minetest.get_meta(pos) 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") local xp = meta:get_int("xp")
if xp > 0 then if xp > 0 then
if player 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 -- Test stack with size 1 because we burn one fuel at a time
local teststack = ItemStack(stack) local teststack = ItemStack(stack)
teststack:set_count(1) 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 if output.time ~= 0 then
-- Only allow to place 1 item if fuel get replaced by recipe. -- Only allow to place 1 item if fuel get replaced by recipe.
-- This is the case for lava buckets. -- This is the case for lava buckets.
@ -160,17 +183,17 @@ local function spawn_flames(pos, param2)
local minrelpos, maxrelpos local minrelpos, maxrelpos
local dir = minetest.facedir_to_dir(param2) local dir = minetest.facedir_to_dir(param2)
if dir.x > 0 then if dir.x > 0 then
minrelpos = { x = -0.6, y = -0.05, z = -0.25 } minrelpos = vector.new(-0.6, -0.05, -0.25)
maxrelpos = { x = -0.55, y = -0.45, z = 0.25 } maxrelpos = vector.new(-0.55, -0.45, 0.25)
elseif dir.x < 0 then elseif dir.x < 0 then
minrelpos = { x = 0.55, y = -0.05, z = -0.25 } minrelpos = vector.new(0.55, -0.05, -0.25)
maxrelpos = { x = 0.6, y = -0.45, z = 0.25 } maxrelpos = vector.new(0.6, -0.45, 0.25)
elseif dir.z > 0 then elseif dir.z > 0 then
minrelpos = { x = -0.25, y = -0.05, z = -0.6 } minrelpos = vector.new(-0.25, -0.05, -0.6)
maxrelpos = { x = 0.25, y = -0.45, z = -0.55 } maxrelpos = vector.new(0.25, 0.45, -0.55)
elseif dir.z < 0 then elseif dir.z < 0 then
minrelpos = { x = -0.25, y = -0.05, z = 0.55 } minrelpos = vector.new(-0.25, -0.05, 0.55)
maxrelpos = { x = 0.25, y = -0.45, z = 0.6 } maxrelpos = vector.new(0.25, -0.45, 0.6)
else else
return return
end end
@ -179,8 +202,8 @@ local function spawn_flames(pos, param2)
time = 0, time = 0,
minpos = vector.add(pos, minrelpos), minpos = vector.add(pos, minrelpos),
maxpos = vector.add(pos, maxrelpos), maxpos = vector.add(pos, maxrelpos),
minvel = { x = -0.01, y = 0, z = -0.01 }, minvel = vector.new(-0.01, 0, -0.01),
maxvel = { x = 0.01, y = 0.1, z = 0.01 }, maxvel = vector.new(0.01, 0.1, 0.01),
minexptime = 0.3, minexptime = 0.3,
maxexptime = 0.6, maxexptime = 0.6,
minsize = 0.4, minsize = 0.4,
@ -293,7 +316,7 @@ local function blast_furnace_node_timer(pos, elapsed)
-- Check if we have cookable content: cookable -- Check if we have cookable content: cookable
local aftercooked 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 cookable = minetest.get_item_group(inv:get_stack("src", 1):get_name(), "blast_furnace_smeltable") == 1
if cookable then if cookable then
-- Successful cooking requires space in dst slot and time -- 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 if cookable and not active then
-- We need to get new fuel -- We need to get new fuel
local afterfuel 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 if fuel.time == 0 then
-- No valid fuel in fuel list -- stop -- No valid fuel in fuel list -- stop
@ -343,7 +366,7 @@ local function blast_furnace_node_timer(pos, elapsed)
srclist = inv:get_list("src") srclist = inv:get_list("src")
src_time = 0 src_time = 0
meta:set_int("xp", meta:get_int("xp") + 1) -- ToDo give each recipe an idividial XP count meta:set_int("xp", meta:get_int("xp") + 1) -- ToDo give each recipe an idividial XP count
end end
end end
@ -390,9 +413,9 @@ local function blast_furnace_node_timer(pos, elapsed)
meta:set_float("fuel_time", fuel_time) meta:set_float("fuel_time", fuel_time)
meta:set_float("src_time", src_time) meta:set_float("src_time", src_time)
if srclist then if srclist then
meta:set_string("src_item", src_item) meta:set_string("src_item", src_item)
else else
meta:set_string("src_item", "") meta:set_string("src_item", "")
end end
meta:set_string("formspec", formspec) meta:set_string("formspec", formspec)
@ -415,13 +438,14 @@ end
minetest.register_node("mcl_blast_furnace:blast_furnace", { minetest.register_node("mcl_blast_furnace:blast_furnace", {
description = S("Blast Furnace"), description = S("Blast Furnace"),
_tt_help = S("Smelts ores faster than 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 = _doc_items_usagehelp =
S("Use the blast furnace to open the furnace menu.").."\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("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 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("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."), 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, _doc_items_hidden = false,
tiles = { tiles = {
"blast_furnace_top.png", "blast_furnace_top.png", "blast_furnace_top.png", "blast_furnace_top.png",
@ -429,7 +453,7 @@ minetest.register_node("mcl_blast_furnace:blast_furnace", {
"blast_furnace_side.png", "blast_furnace_front.png" "blast_furnace_side.png", "blast_furnace_front.png"
}, },
paramtype2 = "facedir", 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, is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(), sounds = mcl_sounds.node_sound_stone_defaults(),
@ -439,11 +463,10 @@ minetest.register_node("mcl_blast_furnace:blast_furnace", {
local meta2 = meta:to_table() local meta2 = meta:to_table()
meta:from_table(oldmetadata) meta:from_table(oldmetadata)
local inv = meta:get_inventory() 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) local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then 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(vector.offset(pos, math.random(0, 10) / 10 - 0.5, 0, math.random(0, 10) / 10 - 0.5), stack)
minetest.add_item(p, stack)
end end
end end
meta:from_table(meta2) meta:from_table(meta2)
@ -499,14 +522,16 @@ minetest.register_node("mcl_blast_furnace:blast_furnace_active", {
tiles = { tiles = {
"blast_furnace_top.png", "blast_furnace_top.png", "blast_furnace_top.png", "blast_furnace_top.png",
"blast_furnace_side.png", "blast_furnace_side.png", "blast_furnace_side.png", "blast_furnace_side.png",
"blast_furnace_side.png", {name = "blast_furnace_front_on.png", "blast_furnace_side.png", {
animation = {type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 48}}, name = "blast_furnace_front_on.png",
animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 48 }
},
}, },
paramtype2 = "facedir", paramtype2 = "facedir",
paramtype = "light", paramtype = "light",
light_source = LIGHT_ACTIVE_FURNACE, light_source = LIGHT_ACTIVE_FURNACE,
drop = "mcl_blast_furnace:blast_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, is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(), sounds = mcl_sounds.node_sound_stone_defaults(),
on_timer = blast_furnace_node_timer, on_timer = blast_furnace_node_timer,
@ -516,10 +541,14 @@ minetest.register_node("mcl_blast_furnace:blast_furnace_active", {
local meta2 = meta local meta2 = meta
meta:from_table(oldmetadata) meta:from_table(oldmetadata)
local inv = meta:get_inventory() 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) local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then 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) minetest.add_item(p, stack)
end end
end end
@ -550,8 +579,8 @@ minetest.register_node("mcl_blast_furnace:blast_furnace_active", {
minetest.register_craft({ minetest.register_craft({
output = "mcl_blast_furnace:blast_furnace", output = "mcl_blast_furnace:blast_furnace",
recipe = { recipe = {
{ "mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot" }, { "mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot" },
{ "mcl_core:iron_ingot", "mcl_furnaces:furnace", "mcl_core:iron_ingot" }, { "mcl_core:iron_ingot", "mcl_furnaces:furnace", "mcl_core:iron_ingot" },
{ "mcl_core:stone_smooth", "mcl_core:stone_smooth", "mcl_core:stone_smooth" }, { "mcl_core:stone_smooth", "mcl_core:stone_smooth", "mcl_core:stone_smooth" },
} }
}) })
@ -564,10 +593,9 @@ end
minetest.register_lbm({ minetest.register_lbm({
label = "Active blast_furnace flame particles", label = "Active blast_furnace flame particles",
name = "mcl_blast_furnace:flames", name = "mcl_blast_furnace:flames",
nodenames = {"mcl_blast_furnace:blast_furnace_active"}, nodenames = { "mcl_blast_furnace:blast_furnace_active" },
run_at_every_load = true, run_at_every_load = true,
action = function(pos, node) action = function(pos, node)
spawn_flames(pos, node.param2) spawn_flames(pos, node.param2)
end, 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_text_length = 4500 -- TODO: Increase to 12800 when scroll bar was added to written book
local max_title_length = 64 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 = "" local header = ""
if minetest.get_modpath("mcl_init") then 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."), _doc_items_longdesc = S("Books are used to make bookshelves and book and quills."),
inventory_image = "default_book.png", inventory_image = "default_book.png",
stack_max = 64, 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", _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) local node = minetest.get_node(pointed_thing.under)
if user and not user:get_player_control().sneak then 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 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 end
end end
local text = get_text(itemstack) local text = get_text(itemstack)
local formspec = "size[8,9]".. local formspec = "size[8,9]" ..
header.. header ..
"background[-0.5,-0.5;9,10;mcl_books_book_bg.png]".. "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).."]".. "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[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")).."]" "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) minetest.show_formspec(user:get_player_name(), "mcl_books:writable_book", formspec)
end end
@ -106,17 +107,18 @@ local function read(itemstack, user, pointed_thing)
local node = minetest.get_node(pointed_thing.under) local node = minetest.get_node(pointed_thing.under)
if user and not user:get_player_control().sneak then 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 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 end
end end
local text = get_text(itemstack) local text = get_text(itemstack)
local formspec = "size[8,9]".. local formspec = "size[8,9]" ..
header.. header ..
"background[-0.5,-0.5;9,10;mcl_books_book_bg.png]".. "background[-0.5,-0.5;9,10;mcl_books_book_bg.png]" ..
"textarea[0.75,0.1;7.25,9;;"..minetest.formspec_escape(text)..";]".. "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")).."]" "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) minetest.show_formspec(user:get_player_name(), "mcl_books:written_book", formspec)
end end
@ -125,16 +127,18 @@ minetest.register_craftitem("mcl_books:writable_book", {
description = S("Book and Quill"), description = S("Book and Quill"),
_tt_help = S("Write down some notes"), _tt_help = S("Write down some notes"),
_doc_items_longdesc = S("This item can be used to 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(
S("A book can hold up to 4500 characters. The title length is limited to 64 characters."), "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", inventory_image = "mcl_books_book_writable.png",
groups = { book=1 }, groups = { book = 1 },
stack_max = 1, stack_max = 1,
on_place = write, on_place = write,
on_secondary_use = 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 if ((formname == "mcl_books:writable_book") and fields and fields.text) then
local stack = player:get_wielded_item() local stack = player:get_wielded_item()
if (stack:get_name() and (stack:get_name() == "mcl_books:writable_book")) then 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) player:set_wielded_item(stack)
local name = player:get_player_name() local name = player:get_player_name()
local formspec = "size[8,9]".. local formspec = "size[8,9]" ..
header.. header ..
"background[-0.5,-0.5;9,10;mcl_books_book_bg.png]".. "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:")))..";]".. "field[0.75,1;7.25,1;title;" ..
"label[0.75,1.5;"..minetest.formspec_escape(minetest.colorize("#404040", S("by @1", name))).."]".. minetest.formspec_escape(minetest.colorize("#000000", S("Enter book title:"))) .. ";]" ..
"button_exit[0.75,7.95;3,1;sign;"..minetest.formspec_escape(S("Sign and Close")).."]".. "label[0.75,1.5;" ..
"tooltip[sign;"..minetest.formspec_escape(S("Note: The book will no longer be editable after signing")).."]".. minetest.formspec_escape(minetest.colorize("#404040", S("by @1", name))) .. "]" ..
"button[4.25,7.95;3,1;cancel;"..minetest.formspec_escape(S("Cancel")).."]" "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) minetest.show_formspec(player:get_player_name(), "mcl_books:signing", formspec)
end end
end end
@ -181,7 +188,7 @@ minetest.register_on_player_receive_fields(function ( player, formname, fields )
player:set_wielded_item(newbook) player:set_wielded_item(newbook)
else else
minetest.log("error", "[mcl_books] "..name.." failed to sign a book!") minetest.log("error", "[mcl_books] " .. name .. " failed to sign a book!")
end end
elseif ((formname == "mcl_books:signing") and fields and fields.cancel) then elseif ((formname == "mcl_books:signing") and fields and fields.cancel) then
local book = player:get_wielded_item() local book = player:get_wielded_item()
@ -202,12 +209,16 @@ end
-- Written Book -- Written Book
minetest.register_craftitem("mcl_books:written_book", { minetest.register_craftitem("mcl_books:written_book", {
description = S("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_longdesc = S(
_doc_items_usagehelp = S("Hold it in your hand, then rightclick to read the book.").."\n\n".. "Written books contain some text written by someone. They can be read and copied, but not edited."
),
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_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", 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, stack_max = 16,
on_place = read, on_place = read,
on_secondary_use = 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 baq = "mcl_books:writable_book"
local wb = "mcl_books:written_book" local wb = "mcl_books:written_book"
local recipes = { local recipes = {
{wb, baq}, { wb, baq },
{baq, baq, wb}, { baq, baq, wb },
{baq, baq, wb, baq}, { baq, baq, wb, baq },
{baq, baq, baq, baq, wb}, { baq, baq, baq, baq, wb },
{baq, baq, baq, baq, wb, baq}, { baq, baq, baq, baq, wb, baq },
{baq, baq, baq, baq, wb, baq, 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, baq, baq, baq, wb, baq, 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({ minetest.register_craft({
type = "shapeless", type = "shapeless",
output = "mcl_books:written_book "..r, output = "mcl_books:written_book " .. r,
recipe = recipes[r], recipe = recipes[r],
}) })
end end
@ -367,6 +378,9 @@ local function protection_check_put_take(pos, listname, index, stack, player)
end end
end end
---@param pos Vector
---@param node node
---@param clicker ObjectRef
local function bookshelf_gui(pos, node, clicker) local function bookshelf_gui(pos, node, clicker)
if not bookshelf_inv then return end if not bookshelf_inv then return end
local name = minetest.get_meta(pos):get_string("name") 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() local playername = clicker:get_player_name()
minetest.show_formspec(playername, minetest.show_formspec(playername,
"mcl_books:bookshelf_"..pos.x.."_"..pos.y.."_"..pos.z, "mcl_books:bookshelf_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z,
table.concat({ table.concat({
"size[9,8.75]", "formspec_version[4]",
"label[0,0;"..F(C("#313131", name)).."]", "size[11.75,10.425]",
"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.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]",
"label[0,4.0;"..F(C("#313131", S("Inventory"))).."]", mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3),
"list[current_player;main;0,4.5;9,3;9]", mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3, 0, "mcl_book_book_empty_slot.png"),
mcl_formspec.get_itemslot_bg(0, 4.5, 9, 3), "list[nodemeta:" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ";main;0.375,0.75;9,3;]",
"list[current_player;main;0,7.74;9,1;]", "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg(0, 7.74, 9, 1), mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"listring[nodemeta:"..pos.x..","..pos.y..","..pos.z..";main]", "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]", "listring[current_player;main]",
}) })
) )
@ -397,7 +415,7 @@ end
local function close_forms(pos) local function close_forms(pos)
local players = minetest.get_connected_players() 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 for p = 1, #players do
if vector.distance(players[p]:get_pos(), pos) <= 30 then if vector.distance(players[p]:get_pos(), pos) <= 30 then
minetest.close_formspec(players[p]:get_player_name(), formname) minetest.close_formspec(players[p]:get_player_name(), formname)
@ -409,12 +427,18 @@ end
minetest.register_node("mcl_books:bookshelf", { minetest.register_node("mcl_books:bookshelf", {
description = S("Bookshelf"), description = S("Bookshelf"),
_doc_items_longdesc = S("Bookshelves are used for decoration."), _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, stack_max = 64,
is_ground_content = false, is_ground_content = false,
groups = { groups = {
handy=1, axey=1, deco_block=1, material_wood=1, handy = 1,
flammable=3, fire_encouragement=30, fire_flammability=20, container=1 axey = 1,
deco_block = 1,
material_wood = 1,
flammable = 3,
fire_encouragement = 30,
fire_flammability = 20,
container = 1
}, },
drop = "mcl_books:book 3", drop = "mcl_books:book 3",
sounds = wood_sound, sounds = wood_sound,
@ -424,7 +448,7 @@ minetest.register_node("mcl_books:bookshelf", {
on_construct = function(pos) on_construct = function(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
inv:set_size("main", 9*3) inv:set_size("main", 9 * 3)
end, end,
after_place_node = function(pos, placer, itemstack, pointed_thing) after_place_node = function(pos, placer, itemstack, pointed_thing)
minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name")) 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_take = protection_check_put_take,
allow_metadata_inventory_put = 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) on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", player:get_player_name().. minetest.log("action", player:get_player_name() ..
" moves stuff in bookshelf at "..minetest.pos_to_string(pos)) " moves stuff in bookshelf at " .. minetest.pos_to_string(pos))
end, end,
on_metadata_inventory_put = function(pos, listname, index, stack, player) on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name().. minetest.log("action", player:get_player_name() ..
" moves stuff to bookshelf at "..minetest.pos_to_string(pos)) " moves stuff to bookshelf at " .. minetest.pos_to_string(pos))
end, end,
on_metadata_inventory_take = function(pos, listname, index, stack, player) on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name().. minetest.log("action", player:get_player_name() ..
" takes stuff from bookshelf at "..minetest.pos_to_string(pos)) " takes stuff from bookshelf at " .. minetest.pos_to_string(pos))
end, end,
after_dig_node = drop_content, after_dig_node = drop_content,
on_blast = on_blast, on_blast = on_blast,
@ -453,9 +477,9 @@ minetest.register_node("mcl_books:bookshelf", {
minetest.register_craft({ minetest.register_craft({
output = "mcl_books:bookshelf", output = "mcl_books:bookshelf",
recipe = { recipe = {
{"group:wood", "group:wood", "group:wood"}, { "group:wood", "group:wood", "group:wood" },
{"mcl_books:book", "mcl_books:book", "mcl_books:book"}, { "mcl_books:book", "mcl_books:book", "mcl_books:book" },
{"group:wood", "group:wood", "group:wood"}, { "group:wood", "group:wood", "group:wood" },
} }
}) })
@ -464,4 +488,3 @@ minetest.register_craft({
recipe = "mcl_books:bookshelf", recipe = "mcl_books:bookshelf",
burntime = 15, 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 S = minetest.get_translator(minetest.get_current_modname())
local formspec_escape = minetest.formspec_escape local F = minetest.formspec_escape
local show_formspec = minetest.show_formspec
local C = minetest.colorize local C = minetest.colorize
local text_color = "#313131" local show_formspec = minetest.show_formspec
local itemslot_bg = mcl_formspec.get_itemslot_bg
mcl_crafting_table = {} mcl_crafting_table = {}
function mcl_crafting_table.show_crafting_form(player) mcl_crafting_table.formspec = table.concat({
player:get_inventory():set_width("craft", 3) "formspec_version[4]",
player:get_inventory():set_size("craft", 9) "size[11.75,10.425]",
show_formspec(player:get_player_name(), "main", "label[2.25,0.375;" .. F(C(mcl_formspec.label_color, S("Crafting"))) .. "]",
"size[9,8.75]"..
"image[4.7,1.5;1.5,1;gui_crafting_arrow.png]".. mcl_formspec.get_itemslot_bg_v4(2.25, 0.75, 3, 3),
"label[0,4;"..formspec_escape(C(text_color, S("Inventory"))).."]".. "list[current_player;craft;2.25,0.75;3,3;]",
"list[current_player;main;0,4.5;9,3;9]"..
itemslot_bg(0,4.5,9,3).. "image[6.125,2;1.5,1;gui_crafting_arrow.png]",
"list[current_player;main;0,7.74;9,1;]"..
itemslot_bg(0,7.74,9,1).. mcl_formspec.get_itemslot_bg_v4(8.2, 2, 1, 1, 0.2),
"label[1.75,0;"..formspec_escape(C(text_color, S("Crafting"))).."]".. "list[current_player;craftpreview;8.2,2;1,1;]",
"list[current_player;craft;1.75,0.5;3,3;]"..
itemslot_bg(1.75,0.5,3,3).. "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
"list[current_player;craftpreview;6.1,1.5;1,1;]"..
itemslot_bg(6.1,1.5,1,1).. mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"image_button[0.75,1.5;1,1;craftguide_book.png;__mcl_craftguide;]".. "list[current_player;main;0.375,5.1;9,3;9]",
"tooltip[__mcl_craftguide;"..formspec_escape(S("Recipe book")).."]"..
"listring[current_player;main]".. mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"listring[current_player;craft]" "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 end
minetest.register_node("mcl_crafting_table:crafting_table", { 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_usagehelp = S("Rightclick the crafting table to access the 3×3 crafting grid."),
_doc_items_hidden = false, _doc_items_hidden = false,
is_ground_content = false, is_ground_content = false,
tiles = {"crafting_workbench_top.png", "default_wood.png", "crafting_workbench_side.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"}, "crafting_workbench_side.png", "crafting_workbench_front.png", "crafting_workbench_front.png" },
paramtype2 = "facedir", 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) on_rightclick = function(pos, node, player, itemstack)
if not player:get_player_control().sneak then if not player:get_player_control().sneak then
mcl_crafting_table.show_crafting_form(player) mcl_crafting_table.show_crafting_form(player)
@ -55,9 +72,9 @@ minetest.register_node("mcl_crafting_table:crafting_table", {
minetest.register_craft({ minetest.register_craft({
output = "mcl_crafting_table:crafting_table", output = "mcl_crafting_table:crafting_table",
recipe = { recipe = {
{"group:wood", "group:wood"}, { "group:wood", "group:wood" },
{"group:wood", "group:wood"} { "group:wood", "group:wood" }
} },
}) })
minetest.register_craft({ minetest.register_craft({

@ -2,7 +2,8 @@ local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape local F = minetest.formspec_escape
function mcl_enchanting.is_book(itemname) 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 end
function mcl_enchanting.get_enchantments(itemstack) function mcl_enchanting.get_enchantments(itemstack)
@ -54,11 +55,13 @@ end
function mcl_enchanting.get_enchantment_description(enchantment, level) function mcl_enchanting.get_enchantment_description(enchantment, level)
local enchantment_def = mcl_enchanting.enchantments[enchantment] 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 end
function mcl_enchanting.get_colorized_enchantment_description(enchantment, level) 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 end
function mcl_enchanting.get_enchanted_itemstring(itemname) function mcl_enchanting.get_enchanted_itemstring(itemname)
@ -79,7 +82,8 @@ function mcl_enchanting.not_enchantable_on_enchanting_table(itemname)
end end
function mcl_enchanting.is_enchantable(itemname) 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 end
function mcl_enchanting.can_enchant_freshly(itemname) 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 for incompatible in pairs(enchantment_def.incompatible) do
local incompatible_level = item_enchantments[incompatible] local incompatible_level = item_enchantments[incompatible]
if incompatible_level then 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 end
end end
@ -169,7 +174,9 @@ function mcl_enchanting.combine(itemstack, combine_with)
local itemname = itemstack:get_name() local itemname = itemstack:get_name()
local combine_name = combine_with:get_name() local combine_name = combine_with:get_name()
local enchanted_itemname = mcl_enchanting.get_enchanted_itemstring(itemname) 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 return false
end end
local enchantments = mcl_enchanting.get_enchantments(itemstack) local enchantments = mcl_enchanting.get_enchantments(itemstack)
@ -219,7 +226,7 @@ function mcl_enchanting.enchantments_snippet(_, _, itemstack)
local enchantments = mcl_enchanting.get_enchantments(itemstack) local enchantments = mcl_enchanting.get_enchantments(itemstack)
local text = "" local text = ""
for enchantment, level in pairs(enchantments) do for enchantment, level in pairs(enchantments) do
text = text .. mcl_enchanting.get_colorized_enchantment_description(enchantment, level) .. "\n" text = text .. mcl_enchanting.get_colorized_enchantment_description(enchantment, level) .. "\n"
end end
if text ~= "" then if text ~= "" then
if not itemstack:get_definition()._tt_original_description then if not itemstack:get_definition()._tt_original_description then
@ -267,7 +274,7 @@ function mcl_enchanting.initialize()
for itemname, itemdef in pairs(minetest.registered_items) do for itemname, itemdef in pairs(minetest.registered_items) do
if mcl_enchanting.can_enchant_freshly(itemname) and not mcl_enchanting.is_book(itemname) then if mcl_enchanting.can_enchant_freshly(itemname) and not mcl_enchanting.is_book(itemname) then
local new_name = itemname .. "_enchanted" 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) local new_def = table.copy(itemdef)
new_def.inventory_image = itemdef.inventory_image .. mcl_enchanting.overlay new_def.inventory_image = itemdef.inventory_image .. mcl_enchanting.overlay
if new_def.wield_image then if new_def.wield_image then
@ -303,7 +310,7 @@ end
function mcl_enchanting.random(pr, ...) function mcl_enchanting.random(pr, ...)
local r = pr and pr:next(...) or math.random(...) local r = pr and pr:next(...) or math.random(...)
if pr and not ({...})[1] then if pr and not ({ ... })[1] then
r = r / 32767 r = r / 32767
end 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)] return #possible > 0 and possible[mcl_enchanting.random(pr, 1, #possible)]
end 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() 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 return
end end
itemstack = ItemStack(itemstack) itemstack = ItemStack(itemstack)
local enchantability = minetest.get_item_group(itemname, "enchantability") 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 + 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) enchantment_level = math.max(math.floor(enchantment_level + 0.5), 1)
local enchantments = {} local enchantments = {}
@ -387,7 +398,6 @@ function mcl_enchanting.generate_random_enchantments(itemstack, enchantment_leve
enchantments[selected_enchantment] = enchantment_power enchantments[selected_enchantment] = enchantment_power
mcl_enchanting.enchant(itemstack, selected_enchantment, enchantment_power) mcl_enchanting.enchant(itemstack, selected_enchantment, enchantment_power)
end end
until not no_reduced_bonus_chance and mcl_enchanting.random(pr) >= (enchantment_level + 1) / 50 until not no_reduced_bonus_chance and mcl_enchanting.random(pr) >= (enchantment_level + 1) / 50
return enchantments, description return enchantments, description
@ -397,13 +407,15 @@ function mcl_enchanting.generate_random_enchantments_reliable(itemstack, enchant
local enchantments local enchantments
repeat 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 until enchantments
return enchantments return enchantments
end 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) 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) 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) local enchantment = mcl_enchanting.get_random_enchantment(stack, true, false, exclude, pr)
if enchantment then 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 end
return stack return stack
@ -426,7 +439,8 @@ function mcl_enchanting.get_random_glyph_row()
local glyphs = "" local glyphs = ""
local x = 1.3 local x = 1.3
for i = 1, 9 do 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 x = x + 0.6
end end
return glyphs return glyphs
@ -459,7 +473,7 @@ end
function mcl_enchanting.get_table_slots(player, itemstack, num_bookshelves) function mcl_enchanting.get_table_slots(player, itemstack, num_bookshelves)
local itemname = itemstack:get_name() local itemname = itemstack:get_name()
if (not mcl_enchanting.can_enchant_freshly(itemname)) or mcl_enchanting.not_enchantable_on_enchanting_table(itemname) then 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 end
local meta = player:get_meta() local meta = player:get_meta()
local player_slots = minetest.deserialize(meta:get_string("mcl_enchanting:slots")) or {} 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)) meta:set_string("mcl_enchanting:slots", minetest.serialize(player_slots))
return player_bookshelves_item_slots return player_bookshelves_item_slots
else else
return {false, false, false} return { false, false, false }
end end
end end
end end
@ -491,28 +505,33 @@ function mcl_enchanting.show_enchanting_formspec(player)
local inv = player:get_inventory() local inv = player:get_inventory()
local num_bookshelves = meta:get_int("mcl_enchanting:num_bookshelves") local num_bookshelves = meta:get_int("mcl_enchanting:num_bookshelves")
local table_name = meta:get_string("mcl_enchanting:table_name") local table_name = meta:get_string("mcl_enchanting:table_name")
local formspec = ""
.. "size[9.07,8.6;]" local formspec = table.concat({
.. "formspec_version[3]" "formspec_version[4]",
.. "label[0,0;" .. C("#313131") .. F(table_name) .. "]" "size[11.75,10.425]",
.. mcl_formspec.get_itemslot_bg(0.2, 2.4, 1, 1)
.. "list[current_player;enchanting_item;0.2,2.4;1,1]" "label[0.375,0.375;" .. F(C(mcl_formspec.label_color) .. table_name) .. "]",
.. mcl_formspec.get_itemslot_bg(1.1, 2.4, 1, 1) mcl_formspec.get_itemslot_bg_v4(1, 3.25, 1, 1),
.. "image[1.1,2.4;1,1;mcl_enchanting_lapis_background.png]" "list[current_player;enchanting_item;1,3.25;1,1]",
.. "list[current_player;enchanting_lapis;1.1,2.4;1,1]" mcl_formspec.get_itemslot_bg_v4(2.25, 3.25, 1, 1),
.. "label[0,4;" .. C("#313131") .. F(S("Inventory")).."]" "image[2.25,3.25;1,1;mcl_enchanting_lapis_background.png]",
.. mcl_formspec.get_itemslot_bg(0, 4.5, 9, 3) "list[current_player;enchanting_lapis;2.25,3.25;1,1]",
.. mcl_formspec.get_itemslot_bg(0, 7.74, 9, 1) "image[4.125,0.56;7.25,4.1;mcl_enchanting_button_background.png]",
.. "list[current_player;main;0,4.5;9,3;9]" "label[0.375,4.7;" .. F(C(mcl_formspec.label_color) .. S("Inventory")) .. "]",
.. "listring[current_player;enchanting_item]" mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
.. "listring[current_player;main]" "list[current_player;main;0.375,5.1;9,3;9]",
.. "listring[current_player;enchanting]"
.. "listring[current_player;main]" mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
.. "listring[current_player;enchanting_lapis]" "list[current_player;main;0.375,9.05;9,1;]",
.. "listring[current_player;main]"
.. "list[current_player;main;0,7.74;9,1;]" "listring[current_player;enchanting_item]",
.. "real_coordinates[true]" "listring[current_player;main]",
.. "image[3.15,0.6;7.6,4.1;mcl_enchanting_button_background.png]" "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 itemstack = inv:get_stack("enchanting_item", 1)
local player_levels = mcl_experience.get_level(player) local player_levels = mcl_experience.get_level(player)
local y = 0.65 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) local table_slots = mcl_enchanting.get_table_slots(player, itemstack, num_bookshelves)
for i, slot in ipairs(table_slots) do for i, slot in ipairs(table_slots) do
any_enchantment = any_enchantment or slot 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 enough_levels = slot and slot.level_requirement <= player_levels
local can_enchant = (slot and enough_lapis and enough_levels) local can_enchant = (slot and enough_lapis and enough_levels)
local ending = (can_enchant and "" or "_off") local ending = (can_enchant and "" or "_off")
local hover_ending = (can_enchant and "_hovered" or "_off") local hover_ending = (can_enchant and "_hovered" or "_off")
formspec = formspec formspec = formspec
.. "container[3.2," .. y .. "]" .. "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.5,1.3;button_" .. i .. ";]" 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 "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 "") .. (slot and slot.glyphs or "")
.. "container_end[]" .. "container_end[]"
y = y + 1.35 y = y + 1.3
end end
formspec = formspec 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) minetest.show_formspec(name, "mcl_enchanting:table", formspec)
end end
@ -555,7 +600,7 @@ function mcl_enchanting.handle_formspec_fields(player, formname, fields)
local meta = player:get_meta() local meta = player:get_meta()
local num_bookshelfes = meta:get_int("mcl_enchanting:num_bookshelves") local num_bookshelfes = meta:get_int("mcl_enchanting:num_bookshelves")
local itemstack = inv:get_stack("enchanting_item", 1) 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 if not inv:contains_item("enchanting_lapis", cost) then
return return
end end
@ -573,7 +618,7 @@ function mcl_enchanting.handle_formspec_fields(player, formname, fields)
mcl_enchanting.set_enchanted_itemstring(itemstack) mcl_enchanting.set_enchanted_itemstring(itemstack)
mcl_enchanting.set_enchantments(itemstack, slot.enchantments) mcl_enchanting.set_enchantments(itemstack, slot.enchantments)
inv:set_stack("enchanting_item", 1, itemstack) 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.reset_table_slots(player)
mcl_enchanting.show_enchanting_formspec(player) mcl_enchanting.show_enchanting_formspec(player)
awards.unlock(player:get_player_name(), "mcl:enchanter") awards.unlock(player:get_player_name(), "mcl:enchanter")
@ -603,7 +648,8 @@ function mcl_enchanting.is_enchanting_inventory_action(action, inventory, invent
end end
function mcl_enchanting.allow_inventory_action(player, action, inventory, inventory_info) 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 is_enchanting_action and do_limit then
if action == "move" then if action == "move" then
local listname = inventory_info.to_list local listname = inventory_info.to_list
@ -645,13 +691,15 @@ function mcl_enchanting.on_inventory_action(player, action, inventory, inventory
end end
function mcl_enchanting.schedule_book_animation(self, anim) 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 end
function mcl_enchanting.set_book_animation(self, anim) function mcl_enchanting.set_book_animation(self, anim)
local anim_index = mcl_enchanting.book_animations[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] local start, stop = mcl_enchanting.book_animation_steps[anim_index],
self.object:set_animation({x = start, y = stop}, mcl_enchanting.book_animation_speed, 0, mcl_enchanting.book_animation_loop[anim] or false) 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.scheduled_anim = nil
self.anim_length = (stop - start) / 40 self.anim_length = (stop - start) / 40
end end
@ -661,7 +709,7 @@ function mcl_enchanting.check_animation_schedule(self, dtime)
if schedanim then if schedanim then
schedanim.timer = schedanim.timer - dtime schedanim.timer = schedanim.timer - dtime
if schedanim.timer <= 0 then if schedanim.timer <= 0 then
mcl_enchanting.set_book_animation(self, schedanim.anim) mcl_enchanting.set_book_animation(self, schedanim.anim)
end end
end end
end end
@ -669,7 +717,7 @@ end
function mcl_enchanting.look_at(self, pos2) function mcl_enchanting.look_at(self, pos2)
local pos1 = self.object:get_pos() local pos1 = self.object:get_pos()
local vec = vector.subtract(pos1, pos2) 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) yaw = yaw + (pos1.x >= pos2.x and math.pi or 0)
self.object:set_yaw(yaw + math.pi) self.object:set_yaw(yaw + math.pi)
end end

@ -1,5 +1,6 @@
local S = minetest.get_translator(minetest.get_current_modname()) local S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
local F = minetest.formspec_escape
local LIGHT_ACTIVE_FURNACE = 13 local LIGHT_ACTIVE_FURNACE = 13
@ -8,60 +9,82 @@ local LIGHT_ACTIVE_FURNACE = 13
-- --
local function active_formspec(fuel_percent, item_percent) local function active_formspec(fuel_percent, item_percent)
return "size[9,8.75]".. return table.concat({
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]".. "formspec_version[4]",
"list[current_player;main;0,4.5;9,3;9]".. "size[11.75,10.425]",
mcl_formspec.get_itemslot_bg(0,4.5,9,3).. "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Furnace"))) .. "]",
"list[current_player;main;0,7.74;9,1;]".. mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
mcl_formspec.get_itemslot_bg(0,7.74,9,1).. "list[context;src;3.5,0.75;1,1;]",
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Furnace"))).."]"..
"list[context;src;2.75,0.5;1,1;]".. "image[3.5,2;1,1;default_furnace_fire_bg.png^[lowpart:" ..
mcl_formspec.get_itemslot_bg(2.75,0.5,1,1).. (100 - fuel_percent) .. ":default_furnace_fire_fg.png]",
"list[context;fuel;2.75,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1).. mcl_formspec.get_itemslot_bg_v4(3.5, 3.25, 1, 1),
"list[context;dst;5.75,1.5;1,1;]".. "list[context;fuel;3.5,3.25;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:".. "image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[lowpart:" ..
(100-fuel_percent)..":default_furnace_fire_fg.png]".. (item_percent) .. ":gui_furnace_arrow_fg.png^[transformR270]",
"image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[lowpart:".. mcl_formspec.get_itemslot_bg_v4(7.875, 2, 1, 1, 0.2),
(item_percent)..":gui_furnace_arrow_fg.png^[transformR270]".. "list[context;dst;7.875,2;1,1;]",
-- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed. "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]".. mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]".. "list[current_player;main;0.375,5.1;9,3;9]",
"listring[context;dst]"..
"listring[current_player;main]".. mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"listring[context;src]".. "list[current_player;main;0.375,9.05;9,1;]",
"listring[current_player;main]"..
"listring[context;fuel]".. -- Craft guide button temporarily removed due to Minetest bug.
"listring[current_player;main]" -- 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]",
})
end end
local inactive_formspec = "size[9,8.75]".. local inactive_formspec = table.concat({
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]".. "formspec_version[4]",
"list[current_player;main;0,4.5;9,3;9]".. "size[11.75,10.425]",
mcl_formspec.get_itemslot_bg(0,4.5,9,3).. "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Furnace"))) .. "]",
"list[current_player;main;0,7.74;9,1;]".. mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
mcl_formspec.get_itemslot_bg(0,7.74,9,1).. "list[context;src;3.5,0.75;1,1;]",
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Furnace"))).."]"..
"list[context;src;2.75,0.5;1,1;]".. "image[3.5,2;1,1;default_furnace_fire_bg.png]",
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_v4(3.5, 3.25, 1, 1),
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1).. "list[context;fuel;3.5,3.25;1,1;]",
"list[context;dst;5.75,1.5;1,1;]"..
mcl_formspec.get_itemslot_bg(5.75,1.5,1,1).. "image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[transformR270]",
"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]".. 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. -- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed. -- TODO: Add it back when the Minetest bug is fixed.
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]".. --"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]".. --"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
"listring[context;dst]"..
"listring[current_player;main]".. "listring[context;dst]",
"listring[context;src]".. "listring[current_player;main]",
"listring[current_player;main]".. "listring[context;src]",
"listring[context;fuel]".. "listring[current_player;main]",
"listring[current_player;main]" "listring[context;fuel]",
"listring[current_player;main]",
})
local receive_fields = function(pos, formname, fields, sender) local receive_fields = function(pos, formname, fields, sender)
if fields.craftguide then if fields.craftguide then
@ -71,7 +94,7 @@ end
local function give_xp(pos, player) local function give_xp(pos, player)
local meta = minetest.get_meta(pos) 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") local xp = meta:get_int("xp")
if xp > 0 then if xp > 0 then
if player 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 -- Test stack with size 1 because we burn one fuel at a time
local teststack = ItemStack(stack) local teststack = ItemStack(stack)
teststack:set_count(1) 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 if output.time ~= 0 then
-- Only allow to place 1 item if fuel get replaced by recipe. -- Only allow to place 1 item if fuel get replaced by recipe.
-- This is the case for lava buckets. -- 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 -- Check if we have cookable content: cookable
local aftercooked 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 cookable = cooked.time ~= 0
if cookable then if cookable then
-- Successful cooking requires space in dst slot and time -- 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 if cookable and not active then
-- We need to get new fuel -- We need to get new fuel
local afterfuel 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 if fuel.time == 0 then
-- No valid fuel in fuel list -- stop -- No valid fuel in fuel list -- stop
@ -356,7 +379,7 @@ local function furnace_node_timer(pos, elapsed)
if srclist[1]:get_name() == "mcl_sponges:sponge_wet" then if srclist[1]:get_name() == "mcl_sponges:sponge_wet" then
inv:set_stack("fuel", 1, "mcl_buckets:bucket_water") inv:set_stack("fuel", 1, "mcl_buckets:bucket_water")
fuellist = inv:get_list("fuel") fuellist = inv:get_list("fuel")
-- Also for river water -- Also for river water
elseif srclist[1]:get_name() == "mcl_sponges:sponge_wet_river_water" then elseif srclist[1]:get_name() == "mcl_sponges:sponge_wet_river_water" then
inv:set_stack("fuel", 1, "mcl_buckets:bucket_river_water") inv:set_stack("fuel", 1, "mcl_buckets:bucket_river_water")
fuellist = inv:get_list("fuel") fuellist = inv:get_list("fuel")
@ -366,7 +389,7 @@ local function furnace_node_timer(pos, elapsed)
srclist = inv:get_list("src") srclist = inv:get_list("src")
src_time = 0 src_time = 0
meta:set_int("xp", meta:get_int("xp") + 1) -- ToDo give each recipe an idividial XP count meta:set_int("xp", meta:get_int("xp") + 1) -- ToDo give each recipe an idividial XP count
end end
end end
@ -413,9 +436,9 @@ local function furnace_node_timer(pos, elapsed)
meta:set_float("fuel_time", fuel_time) meta:set_float("fuel_time", fuel_time)
meta:set_float("src_time", src_time) meta:set_float("src_time", src_time)
if srclist then if srclist then
meta:set_string("src_item", src_item) meta:set_string("src_item", src_item)
else else
meta:set_string("src_item", "") meta:set_string("src_item", "")
end end
meta:set_string("formspec", formspec) meta:set_string("formspec", formspec)
@ -440,11 +463,11 @@ minetest.register_node("mcl_furnaces:furnace", {
_tt_help = S("Uses fuel to smelt or cook items"), _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_longdesc = S("Furnaces cook or smelt several items, using a furnace fuel, into something else."),
_doc_items_usagehelp = _doc_items_usagehelp =
S("Use the furnace to open the furnace menu.").."\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("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 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("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."), 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, _doc_items_hidden = false,
tiles = { tiles = {
"default_furnace_top.png", "default_furnace_bottom.png", "default_furnace_top.png", "default_furnace_bottom.png",
@ -452,7 +475,7 @@ minetest.register_node("mcl_furnaces:furnace", {
"default_furnace_side.png", "default_furnace_front.png" "default_furnace_side.png", "default_furnace_front.png"
}, },
paramtype2 = "facedir", 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, is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(), sounds = mcl_sounds.node_sound_stone_defaults(),
@ -462,10 +485,11 @@ minetest.register_node("mcl_furnaces:furnace", {
local meta2 = meta:to_table() local meta2 = meta:to_table()
meta:from_table(oldmetadata) meta:from_table(oldmetadata)
local inv = meta:get_inventory() 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) local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then 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) minetest.add_item(p, stack)
end end
end end
@ -528,7 +552,7 @@ minetest.register_node("mcl_furnaces:furnace_active", {
paramtype = "light", paramtype = "light",
light_source = LIGHT_ACTIVE_FURNACE, light_source = LIGHT_ACTIVE_FURNACE,
drop = "mcl_furnaces: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, is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(), sounds = mcl_sounds.node_sound_stone_defaults(),
on_timer = furnace_node_timer, on_timer = furnace_node_timer,
@ -538,10 +562,11 @@ minetest.register_node("mcl_furnaces:furnace_active", {
local meta2 = meta local meta2 = meta
meta:from_table(oldmetadata) meta:from_table(oldmetadata)
local inv = meta:get_inventory() 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) local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then 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) minetest.add_item(p, stack)
end end
end end
@ -573,7 +598,7 @@ minetest.register_craft({
output = "mcl_furnaces:furnace", output = "mcl_furnaces:furnace",
recipe = { recipe = {
{ "group:cobble", "group:cobble", "group:cobble" }, { "group:cobble", "group:cobble", "group:cobble" },
{ "group:cobble", "", "group:cobble" }, { "group:cobble", "", "group:cobble" },
{ "group:cobble", "group:cobble", "group:cobble" }, { "group:cobble", "group:cobble", "group:cobble" },
} }
}) })
@ -586,7 +611,7 @@ end
minetest.register_lbm({ minetest.register_lbm({
label = "Active furnace flame particles", label = "Active furnace flame particles",
name = "mcl_furnaces:flames", name = "mcl_furnaces:flames",
nodenames = {"mcl_furnaces:furnace_active"}, nodenames = { "mcl_furnaces:furnace_active" },
run_at_every_load = true, run_at_every_load = true,
action = function(pos, node) action = function(pos, node)
spawn_flames(pos, node.param2) spawn_flames(pos, node.param2)

@ -1,37 +1,52 @@
-- Code based from mcl_anvils -- Code based from mcl_anvils
local S = minetest.get_translator(minetest.get_current_modname()) local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape
local C = minetest.colorize
local MAX_WEAR = 65535 local MAX_WEAR = 65535
-- formspecs local grindstone_formspec = table.concat({
local function get_grindstone_formspec() "formspec_version[6]",
return "size[9,8.75]".. "size[11.75,10.425]",
"image[3,1.5;1.5,1;gui_crafting_arrow.png]"..
"label[0,4.0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]".. "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Repair & Disenchant"))) .. "]",
"label[1,0.1;"..minetest.formspec_escape(minetest.colorize("#313131", S("Repair & Disenchant"))).."]"..
"list[context;main;0,0;8,4;]".. mcl_formspec.get_itemslot_bg_v4(2.875, 1.25, 1, 1),
"list[current_player;main;0,4.5;9,3;9]".. "list[context;input;2.875,1.25;1,1;]",
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]".. mcl_formspec.get_itemslot_bg_v4(2.875, 2.625, 1, 1),
mcl_formspec.get_itemslot_bg(0,7.74,9,1).. "list[context;input;2.875,2.625;1,1;1]",
"list[context;input;1,1;1,1;]"..
mcl_formspec.get_itemslot_bg(1,1,1,1).. "image[2.375,1;2,2.875;grindstone_gui_9.png;2]",
"list[context;input;1,2;1,1;1]"..
mcl_formspec.get_itemslot_bg(1,2,1,1).. "image[1.875,1.5;0.5,2.875;grindstone_gui_9.png;2]",
"list[context;output;6,1.5;1,1;]".. "image[4.375,1.5;0.5,2.875;grindstone_gui_9.png;2]",
mcl_formspec.get_itemslot_bg(6,1.5,1,1)..
"listring[context;output]".. "image[5.5,1.95;1.5,1;gui_crafting_arrow.png]",
"listring[current_player;main]"..
"listring[context;input]".. mcl_formspec.get_itemslot_bg_v4(7.875, 1.9375, 1, 1),
"listring[current_player;main]" "list[context;output;7.875,1.9375;1,1;]",
end
"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 -- Creates a new item with the wear of the items and custom name
local function create_new_item(name_item, meta, wear) local function create_new_item(name_item, meta, wear)
local new_item = ItemStack(name_item) local new_item = ItemStack(name_item)
if wear ~= nil then if wear ~= nil then
new_item:set_wear(wear) new_item:set_wear(wear)
end end
local new_meta = new_item:get_meta() local new_meta = new_item:get_meta()
new_meta:set_string("name", meta:get_string("name")) new_meta:set_string("name", meta:get_string("name"))
@ -86,7 +101,6 @@ local function fix_stack_size(stack)
return count return count
end end
-- Update the inventory slots of an grindstone node. -- Update the inventory slots of an grindstone node.
-- meta: Metadata of grindstone node -- meta: Metadata of grindstone node
local function update_grindstone_slots(meta) local function update_grindstone_slots(meta)
@ -122,8 +136,8 @@ local function update_grindstone_slots(meta)
else else
new_output = "" new_output = ""
end end
-- Check if at least one input has an item -- Check if at least one input has an item
-- Check if the item is's an enchanted book or tool -- Check if the item is's an enchanted book or tool
elseif (not input1:is_empty() and input2:is_empty()) or (input1:is_empty() and not input2:is_empty()) then elseif (not input1:is_empty() and input2:is_empty()) or (input1:is_empty() and not input2:is_empty()) then
if input2:is_empty() then if input2:is_empty() then
local def1 = input1:get_definition() local def1 = input1:get_definition()
@ -168,10 +182,10 @@ end
-- Drop any items inside the grindstone if destroyed -- Drop any items inside the grindstone if destroyed
local function drop_grindstone_items(pos, meta) local function drop_grindstone_items(pos, meta)
local inv = meta:get_inventory() 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) local stack = inv:get_stack("input", i)
if not stack:is_empty() then 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) minetest.add_item(p, stack)
end end
end end
@ -181,11 +195,11 @@ local node_box = {
type = "fixed", type = "fixed",
-- created with nodebox editor -- created with nodebox editor
fixed = { fixed = {
{-0.25, -0.25, -0.375, 0.25, 0.5, 0.375}, { -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.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.0625, -0.1875, 0.375, 0.3125, 0.1875 },
{0.25, -0.5, -0.125, 0.375, -0.0625, 0.125}, { 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.375, -0.5, -0.125, -0.25, -0.0625, 0.125 },
} }
} }
@ -193,11 +207,11 @@ minetest.register_node("mcl_grindstone:grindstone", {
description = S("Grindstone"), description = S("Grindstone"),
_tt_help = S("Used to disenchant/fix tools"), _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_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".. _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 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("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("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."), 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 = { tiles = {
"grindstone_top.png", "grindstone_top.png",
"grindstone_top.png", "grindstone_top.png",
@ -212,7 +226,7 @@ minetest.register_node("mcl_grindstone:grindstone", {
selection_box = node_box, selection_box = node_box,
collision_box = node_box, collision_box = node_box,
sounds = mcl_sounds.node_sound_stone_defaults(), 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) after_dig_node = function(pos, oldnode, oldmetadata, digger)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
@ -268,7 +282,7 @@ minetest.register_node("mcl_grindstone:grindstone", {
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
if from_list == "output" and to_list == "input" then if from_list == "output" and to_list == "input" then
local inv = meta:get_inventory() 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 if i ~= to_index then
local istack = inv:get_stack("input", i) local istack = inv:get_stack("input", i)
istack:set_count(math.max(0, istack:get_count() - count)) 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 if not input1:is_empty() and not input2:is_empty() then
-- Get xp earnt from the enchanted items -- Get xp earnt from the enchanted items
xp_earnt = calculate_xp(input1) + calculate_xp(input1) xp_earnt = calculate_xp(input1) + calculate_xp(input1)
input1:take_item() input1:take_item(1)
input2:take_item() input2:take_item(1)
inv:set_stack("input", 1, input1) inv:set_stack("input", 1, input1)
inv:set_stack("input", 2, input2) inv:set_stack("input", 2, input2)
else else
@ -320,14 +334,13 @@ minetest.register_node("mcl_grindstone:grindstone", {
local inv = meta:get_inventory() local inv = meta:get_inventory()
inv:set_size("input", 2) inv:set_size("input", 2)
inv:set_size("output", 1) inv:set_size("output", 1)
local form = get_grindstone_formspec() meta:set_string("formspec", grindstone_formspec)
meta:set_string("formspec", form)
end, end,
on_rightclick = function(pos, node, player, itemstack) on_rightclick = function(pos, node, player, itemstack)
if not player:get_player_control().sneak then if not player:get_player_control().sneak then
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
update_grindstone_slots(meta) update_grindstone_slots(meta)
meta:set_string("formspec", get_grindstone_formspec()) meta:set_string("formspec", grindstone_formspec)
end end
end, end,
_mcl_blast_resistance = 6, _mcl_blast_resistance = 6,
@ -337,7 +350,7 @@ minetest.register_node("mcl_grindstone:grindstone", {
minetest.register_craft({ minetest.register_craft({
output = "mcl_grindstone:grindstone", output = "mcl_grindstone:grindstone",
recipe = { recipe = {
{ "mcl_core:stick", "mcl_stairs:slab_stone_rough", "mcl_core:stick"}, { "mcl_core:stick", "mcl_stairs:slab_stone_rough", "mcl_core:stick" },
{ "group:wood", "", "group:wood"}, { "group:wood", "", "group:wood" },
} }
}) })

@ -12,15 +12,22 @@ end
--[[ BEGIN OF NODE DEFINITIONS ]] --[[ BEGIN OF NODE DEFINITIONS ]]
local mcl_hoppers_formspec = table.concat({ local mcl_hoppers_formspec = table.concat({
"size[9,7]", "formspec_version[4]",
"label[2,0;" .. F(C("#313131", S("Hopper"))) .. "]", "size[11.75,8.175]",
"list[context;main;2,0.5;5,1;]",
mcl_formspec.get_itemslot_bg(2, 0.5, 5, 1), "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Hopper"))) .. "]",
"label[0,2;" .. F(C("#313131", S("Inventory"))) .. "]",
"list[current_player;main;0,2.5;9,3;9]", mcl_formspec.get_itemslot_bg_v4(2.875, 0.75, 5, 1),
mcl_formspec.get_itemslot_bg(0, 2.5, 9, 3), "list[context;main;2.875,0.75;5,1;]",
"list[current_player;main;0,5.74;9,1;]",
mcl_formspec.get_itemslot_bg(0, 5.74, 9, 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[context;main]",
"listring[current_player;main]", "listring[current_player;main]",
}) })
@ -31,7 +38,7 @@ local mcl_hoppers_formspec = table.concat({
local def_hopper = { local def_hopper = {
inventory_image = "mcl_hoppers_item.png", inventory_image = "mcl_hoppers_item.png",
wield_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", drawtype = "nodebox",
paramtype = "light", paramtype = "light",
-- FIXME: mcl_hoppers_hopper_inside.png is unused by hoppers. -- FIXME: mcl_hoppers_hopper_inside.png is unused by hoppers.
@ -47,25 +54,25 @@ local def_hopper = {
type = "fixed", type = "fixed",
fixed = { fixed = {
--funnel walls --funnel walls
{-0.5, 0.0, 0.4, 0.5, 0.5, 0.5}, { -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.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.4, 0.5, 0.5 },
{-0.5, 0.0, -0.5, 0.5, 0.5, -0.4}, { -0.5, 0.0, -0.5, 0.5, 0.5, -0.4 },
--funnel base --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 --spout
{-0.3, -0.3, -0.3, 0.3, 0.0, 0.3}, { -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.1, -0.3, -0.1, 0.1, -0.5, 0.1 },
}, },
}, },
selection_box = { selection_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
--funnel --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 --spout
{-0.3, -0.3, -0.3, 0.3, 0.0, 0.3}, { -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.1, -0.3, -0.1, 0.1, -0.5, 0.1 },
}, },
}, },
is_ground_content = false, is_ground_content = false,
@ -248,25 +255,25 @@ local def_hopper_side = {
type = "fixed", type = "fixed",
fixed = { fixed = {
--funnel walls --funnel walls
{-0.5, 0.0, 0.4, 0.5, 0.5, 0.5}, { -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.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.4, 0.5, 0.5 },
{-0.5, 0.0, -0.5, 0.5, 0.5, -0.4}, { -0.5, 0.0, -0.5, 0.5, 0.5, -0.4 },
--funnel base --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 --spout
{-0.3, -0.3, -0.3, 0.3, 0.0, 0.3}, { -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.5, -0.3, -0.1, 0.1, -0.1, 0.1 },
}, },
}, },
selection_box = { selection_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
--funnel --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 --spout
{-0.3, -0.3, -0.3, 0.3, 0.0, 0.3}, { -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.5, -0.3, -0.1, 0.1, -0.1, 0.1 },
}, },
}, },
is_ground_content = false, is_ground_content = false,
@ -559,8 +566,8 @@ end
minetest.register_abm({ minetest.register_abm({
label = "Hopper/container item exchange", label = "Hopper/container item exchange",
nodenames = {"mcl_hoppers:hopper"}, nodenames = { "mcl_hoppers:hopper" },
neighbors = {"group:container"}, neighbors = { "group:container" },
interval = 1.0, interval = 1.0,
chance = 1, chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider) action = function(pos, node, active_object_count, active_object_count_wider)
@ -591,8 +598,8 @@ minetest.register_abm({
minetest.register_abm({ minetest.register_abm({
label = "Side-hopper/container item exchange", label = "Side-hopper/container item exchange",
nodenames = {"mcl_hoppers:hopper_side"}, nodenames = { "mcl_hoppers:hopper_side" },
neighbors = {"group:container"}, neighbors = { "group:container" },
interval = 1.0, interval = 1.0,
chance = 1, chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider) 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 S = minetest.get_translator("mcl_smithing_table")
local F = minetest.formspec_escape
local C = minetest.colorize
mcl_smithing_table = {} 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) function mcl_smithing_table.upgrade_item(itemstack)
local def = itemstack:get_definition() local def = itemstack:get_definition()
@ -29,28 +31,48 @@ function mcl_smithing_table.upgrade_item(itemstack)
return itemstack return itemstack
end end
-- Badly copied over from mcl_anvils local formspec = table.concat({
-- ToDo: Make better formspec "formspec_version[4]",
"size[11.75,10.425]",
local formspec = "size[9,9]" .. "label[4.125,0.375;" .. F(C(mcl_formspec.label_color, S("Upgrade Gear"))) .. "]",
"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]"
"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 function reset_upgraded_item(pos)
local inv = minetest.get_meta(pos):get_inventory() local inv = minetest.get_meta(pos):get_inventory()
local upgraded_item local upgraded_item
@ -66,8 +88,7 @@ minetest.register_node("mcl_smithing_table:table", {
description = S("Smithing table"), description = S("Smithing table"),
-- ToDo: Add _doc_items_longdesc and _doc_items_usagehelp -- 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 = { tiles = {
"mcl_smithing_table_top.png", "mcl_smithing_table_top.png",
@ -92,7 +113,8 @@ minetest.register_node("mcl_smithing_table:table", {
end, end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player) 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() return stack:get_count()
end end
@ -119,7 +141,7 @@ minetest.register_node("mcl_smithing_table:table", {
take_item("netherite") take_item("netherite")
-- ToDo: make epic sound -- 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 end
if listname == "upgraded_item" then if listname == "upgraded_item" then
if stack:get_name() == "mcl_farming:hoe_netherite" then if stack:get_name() == "mcl_farming:hoe_netherite" then
@ -138,8 +160,8 @@ minetest.register_node("mcl_smithing_table:table", {
minetest.register_craft({ minetest.register_craft({
output = "mcl_smithing_table:table", output = "mcl_smithing_table:table",
recipe = { recipe = {
{"mcl_core:iron_ingot", "mcl_core:iron_ingot", ""}, { "mcl_core:iron_ingot", "mcl_core:iron_ingot", "" },
{"group:wood", "group:wood", ""}, { "group:wood", "group:wood", "" },
{"group:wood", "group:wood", ""} { "group:wood", "group:wood", "" }
} },
}) })

@ -1,2 +1,2 @@
name = mcl_smithing_table 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 S = minetest.get_translator(minetest.get_current_modname())
local C = minetest.colorize
local F = minetest.formspec_escape
local LIGHT_ACTIVE_FURNACE = 13 local LIGHT_ACTIVE_FURNACE = 13
@ -8,60 +9,82 @@ local LIGHT_ACTIVE_FURNACE = 13
-- --
local function active_formspec(fuel_percent, item_percent) local function active_formspec(fuel_percent, item_percent)
return "size[9,8.75]".. return table.concat({
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]".. "formspec_version[4]",
"list[current_player;main;0,4.5;9,3;9]".. "size[11.75,10.425]",
mcl_formspec.get_itemslot_bg(0,4.5,9,3).. "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Smoker"))) .. "]",
"list[current_player;main;0,7.74;9,1;]".. mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
mcl_formspec.get_itemslot_bg(0,7.74,9,1).. "list[context;src;3.5,0.75;1,1;]",
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Smoker"))).."]"..
"list[context;src;2.75,0.5;1,1;]".. "image[3.5,2;1,1;default_furnace_fire_bg.png^[lowpart:" ..
mcl_formspec.get_itemslot_bg(2.75,0.5,1,1).. (100 - fuel_percent) .. ":default_furnace_fire_fg.png]",
"list[context;fuel;2.75,2.5;1,1;]"..
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1).. mcl_formspec.get_itemslot_bg_v4(3.5, 3.25, 1, 1),
"list[context;dst;5.75,1.5;1,1;]".. "list[context;fuel;3.5,3.25;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:".. "image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[lowpart:" ..
(100-fuel_percent)..":default_furnace_fire_fg.png]".. (item_percent) .. ":gui_furnace_arrow_fg.png^[transformR270]",
"image[4.1,1.5;1.5,1;gui_furnace_arrow_bg.png^[lowpart:".. mcl_formspec.get_itemslot_bg_v4(7.875, 2, 1, 1, 0.2),
(item_percent)..":gui_furnace_arrow_fg.png^[transformR270]".. "list[context;dst;7.875,2;1,1;]",
-- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed. "label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]".. mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]".. "list[current_player;main;0.375,5.1;9,3;9]",
"listring[context;dst]"..
"listring[current_player;main]".. mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"listring[context;src]".. "list[current_player;main;0.375,9.05;9,1;]",
"listring[current_player;main]"..
"listring[context;fuel]".. -- Craft guide button temporarily removed due to Minetest bug.
"listring[current_player;main]" -- 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]",
})
end end
local inactive_formspec = "size[9,8.75]".. local inactive_formspec = table.concat({
"label[0,4;"..minetest.formspec_escape(minetest.colorize("#313131", S("Inventory"))).."]".. "formspec_version[4]",
"list[current_player;main;0,4.5;9,3;9]".. "size[11.75,10.425]",
mcl_formspec.get_itemslot_bg(0,4.5,9,3).. "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Smoker"))) .. "]",
"list[current_player;main;0,7.74;9,1;]".. mcl_formspec.get_itemslot_bg_v4(3.5, 0.75, 1, 1),
mcl_formspec.get_itemslot_bg(0,7.74,9,1).. "list[context;src;3.5,0.75;1,1;]",
"label[2.75,0;"..minetest.formspec_escape(minetest.colorize("#313131", S("Smoker"))).."]"..
"list[context;src;2.75,0.5;1,1;]".. "image[3.5,2;1,1;default_furnace_fire_bg.png]",
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_v4(3.5, 3.25, 1, 1),
mcl_formspec.get_itemslot_bg(2.75,2.5,1,1).. "list[context;fuel;3.5,3.25;1,1;]",
"list[context;dst;5.75,1.5;1,1;]"..
mcl_formspec.get_itemslot_bg(5.75,1.5,1,1).. "image[5.25,2;1.5,1;gui_furnace_arrow_bg.png^[transformR270]",
"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]".. 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. -- Craft guide button temporarily removed due to Minetest bug.
-- TODO: Add it back when the Minetest bug is fixed. -- TODO: Add it back when the Minetest bug is fixed.
--"image_button[8,0;1,1;craftguide_book.png;craftguide;]".. --"image_button[8,0;1,1;craftguide_book.png;craftguide;]"..
--"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]".. --"tooltip[craftguide;"..minetest.formspec_escape(S("Recipe book")).."]"..
"listring[context;dst]"..
"listring[current_player;main]".. "listring[context;dst]",
"listring[context;src]".. "listring[current_player;main]",
"listring[current_player;main]".. "listring[context;src]",
"listring[context;fuel]".. "listring[current_player;main]",
"listring[current_player;main]" "listring[context;fuel]",
"listring[current_player;main]",
})
local receive_fields = function(pos, formname, fields, sender) local receive_fields = function(pos, formname, fields, sender)
if fields.craftguide then if fields.craftguide then
@ -71,7 +94,7 @@ end
local function give_xp(pos, player) local function give_xp(pos, player)
local meta = minetest.get_meta(pos) 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") local xp = meta:get_int("xp")
if xp > 0 then if xp > 0 then
if player 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 -- Test stack with size 1 because we burn one fuel at a time
local teststack = ItemStack(stack) local teststack = ItemStack(stack)
teststack:set_count(1) 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 if output.time ~= 0 then
-- Only allow to place 1 item if fuel get replaced by recipe. -- Only allow to place 1 item if fuel get replaced by recipe.
-- This is the case for lava buckets. -- 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 -- Check if we have cookable content: cookable
local aftercooked 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 cookable = minetest.get_item_group(inv:get_stack("src", 1):get_name(), "smoker_cookable") == 1
if cookable then if cookable then
-- Successful cooking requires space in dst slot and time -- 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 if cookable and not active then
-- We need to get new fuel -- We need to get new fuel
local afterfuel 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 if fuel.time == 0 then
-- No valid fuel in fuel list -- stop -- No valid fuel in fuel list -- stop
@ -343,7 +366,7 @@ local function smoker_node_timer(pos, elapsed)
srclist = inv:get_list("src") srclist = inv:get_list("src")
src_time = 0 src_time = 0
meta:set_int("xp", meta:get_int("xp") + 1) -- ToDo give each recipe an idividial XP count meta:set_int("xp", meta:get_int("xp") + 1) -- ToDo give each recipe an idividial XP count
end end
end end
@ -390,9 +413,9 @@ local function smoker_node_timer(pos, elapsed)
meta:set_float("fuel_time", fuel_time) meta:set_float("fuel_time", fuel_time)
meta:set_float("src_time", src_time) meta:set_float("src_time", src_time)
if srclist then if srclist then
meta:set_string("src_item", src_item) meta:set_string("src_item", src_item)
else else
meta:set_string("src_item", "") meta:set_string("src_item", "")
end end
meta:set_string("formspec", formspec) meta:set_string("formspec", formspec)
@ -415,13 +438,14 @@ end
minetest.register_node("mcl_smoker:smoker", { minetest.register_node("mcl_smoker:smoker", {
description = S("Smoker"), description = S("Smoker"),
_tt_help = S("Cooks food faster than furnace"), _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 = _doc_items_usagehelp =
S("Use the smoker to open the furnace menu.").."\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("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 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("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."), 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, _doc_items_hidden = false,
tiles = { tiles = {
"smoker_top.png", "smoker_bottom.png", "smoker_top.png", "smoker_bottom.png",
@ -429,7 +453,7 @@ minetest.register_node("mcl_smoker:smoker", {
"smoker_side.png", "smoker_front.png" "smoker_side.png", "smoker_front.png"
}, },
paramtype2 = "facedir", 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, is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(), sounds = mcl_sounds.node_sound_stone_defaults(),
@ -439,10 +463,14 @@ minetest.register_node("mcl_smoker:smoker", {
local meta2 = meta:to_table() local meta2 = meta:to_table()
meta:from_table(oldmetadata) meta:from_table(oldmetadata)
local inv = meta:get_inventory() 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) local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then 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) minetest.add_item(p, stack)
end end
end end
@ -499,14 +527,16 @@ minetest.register_node("mcl_smoker:smoker_active", {
tiles = { tiles = {
"smoker_top.png", "smoker_bottom.png", "smoker_top.png", "smoker_bottom.png",
"smoker_side.png", "smoker_side.png", "smoker_side.png", "smoker_side.png",
"smoker_side.png", {name = "smoker_front_on.png", "smoker_side.png", {
animation = {type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 48}}, name = "smoker_front_on.png",
animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 48 }
},
}, },
paramtype2 = "facedir", paramtype2 = "facedir",
paramtype = "light", paramtype = "light",
light_source = LIGHT_ACTIVE_FURNACE, light_source = LIGHT_ACTIVE_FURNACE,
drop = "mcl_smoker:smoker", 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, is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(), sounds = mcl_sounds.node_sound_stone_defaults(),
on_timer = smoker_node_timer, on_timer = smoker_node_timer,
@ -516,10 +546,14 @@ minetest.register_node("mcl_smoker:smoker_active", {
local meta2 = meta local meta2 = meta
meta:from_table(oldmetadata) meta:from_table(oldmetadata)
local inv = meta:get_inventory() 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) local stack = inv:get_stack(listname, 1)
if not stack:is_empty() then 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) minetest.add_item(p, stack)
end end
end end
@ -550,9 +584,9 @@ minetest.register_node("mcl_smoker:smoker_active", {
minetest.register_craft({ minetest.register_craft({
output = "mcl_smoker:smoker", output = "mcl_smoker:smoker",
recipe = { recipe = {
{ "", "group:tree", "" }, { "", "group:tree", "" },
{ "group:tree", "mcl_furnaces:furnace", "group:tree" }, { "group:tree", "mcl_furnaces:furnace", "group:tree" },
{ "", "group:tree", "" }, { "", "group:tree", "" },
} }
}) })
@ -564,7 +598,7 @@ end
minetest.register_lbm({ minetest.register_lbm({
label = "Active smoker flame particles", label = "Active smoker flame particles",
name = "mcl_smoker:flames", name = "mcl_smoker:flames",
nodenames = {"mcl_smoker:smoker_active"}, nodenames = { "mcl_smoker:smoker_active" },
run_at_every_load = true, run_at_every_load = true,
action = function(pos, node) action = function(pos, node)
spawn_flames(pos, node.param2) 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) player:get_inventory():set_size("hand", 1)
end) end)
mcl_gamemode.register_on_gamemode_change(function(player)
mcl_meshhand.update_player(player)
end)
if mcl_skins_enabled then if mcl_skins_enabled then
mcl_player.register_on_visual_change(mcl_meshhand.update_player) mcl_player.register_on_visual_change(mcl_meshhand.update_player)
else else

@ -1,5 +1,5 @@
name = mcl_meshhand name = mcl_meshhand
author = jordan4ibanez author = jordan4ibanez
description = Applies the player skin texture to the hand. 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 optional_depends = mcl_skins, mcl_custom_skins

@ -1,3 +1,4 @@
local string = string
local sf = string.format local sf = string.format
-- Minetest 0.4 mod: player -- Minetest 0.4 mod: player
@ -11,15 +12,16 @@ local animation_blend = 0
local function get_mouse_button(player) local function get_mouse_button(player)
local controls = player:get_player_control() local controls = player:get_player_control()
local get_wielded_item_name = player:get_wielded_item():get_name() 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 mcl_shields.wielding_shield(player, 1) and not mcl_shields.wielding_shield(player, 2) or controls.LMB then 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 return true
else else
return false return false
end end
end end
mcl_player.registered_player_models = { } mcl_player.registered_player_models = {}
-- Local for speed. -- Local for speed.
local models = mcl_player.registered_player_models local models = mcl_player.registered_player_models
@ -49,7 +51,7 @@ function mcl_player.player_get_animation(player)
model = player_model[name], model = player_model[name],
textures = textures, textures = textures,
animation = player_anim[name], animation = player_anim[name],
visibility = player_visibility[name] visibility = player_visible[name]
} }
end end
@ -94,7 +96,7 @@ function mcl_player.player_set_model(player, model_name)
player:set_properties({ player:set_properties({
mesh = model_name, mesh = model_name,
visual = "mesh", 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", damage_texture_modifier = "^[colorize:red:130",
}) })
update_player_textures(player) update_player_textures(player)
@ -133,6 +135,13 @@ function mcl_player.player_set_armor(player, texture)
update_player_textures(player) update_player_textures(player)
end 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) function mcl_player.get_player_formspec_model(player, x, y, w, h, fsname)
local name = player:get_player_name() local name = player:get_player_name()
local model = player_model[name] local model = player_model[name]
@ -165,7 +174,8 @@ minetest.register_on_joinplayer(function(player)
local name = player:get_player_name() local name = player:get_player_name()
mcl_player.player_attached[name] = false mcl_player.player_attached[name] = false
player_visible[name] = true 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_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>>>> player:set_fov(86.1) -- see <https://minecraft.gamepedia.com/Options#Video_settings>>>>
end) end)
@ -221,11 +231,13 @@ minetest.register_globalstep(function(dtime)
elseif mcl_playerplus.elytra[player] and mcl_playerplus.elytra[player].active then elseif mcl_playerplus.elytra[player] and mcl_playerplus.elytra[player].active then
player_set_animation(player, "stand") player_set_animation(player, "stand")
elseif walking and velocity.x > 0.35 elseif walking and velocity.x > 0.35
or walking and velocity.x < -0.35 or walking and velocity.x < -0.35
or walking and velocity.z > 0.35 or walking and velocity.z > 0.35
or walking and velocity.z < -0.35 then or walking and velocity.z < -0.35 then
local wielded_itemname = player:get_wielded_item():get_name() 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 if player_sneak[name] ~= controls.sneak then
player_anim[name] = nil player_anim[name] = nil
player_sneak[name] = controls.sneak player_sneak[name] = controls.sneak
@ -234,7 +246,8 @@ minetest.register_globalstep(function(dtime)
player_set_animation(player, "swim_walk_mine", animation_speed_mod) player_set_animation(player, "swim_walk_mine", animation_speed_mod)
elseif not controls.sneak and head_in_water and is_sprinting == true then elseif not controls.sneak and head_in_water and is_sprinting == true then
player_set_animation(player, "swim_walk", animation_speed_mod) 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) 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 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) 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