moreblocks/circular_saw.lua

479 lines
15 KiB
Lua
Raw Permalink Normal View History

--[[
2015-01-12 17:46:22 +01:00
More Blocks: circular saw
2020-01-01 04:09:24 +01:00
Copyright © 2011-2020 Hugo Locurcio, Sokomine and contributors.
Licensed under the zlib license. See LICENSE.md for more information.
--]]
local S = moreblocks.S
2018-07-13 23:26:37 +02:00
local F = minetest.formspec_escape
2014-12-27 20:30:19 +01:00
2014-03-09 10:38:18 +01:00
circular_saw = {}
2013-07-11 20:33:02 +02:00
2014-03-09 10:38:18 +01:00
circular_saw.known_stairs = setmetatable({}, {
__newindex = function(k, v)
local modname = minetest.get_current_modname()
2021-11-14 02:14:19 +01:00
print(("WARNING: mod %s tried to add node %s to the circular saw manually."):format(modname, v))
2014-03-09 10:38:18 +01:00
end,
})
2013-07-11 20:33:02 +02:00
-- This is populated by stairsplus:register_all:
2014-03-09 10:38:18 +01:00
circular_saw.known_nodes = {}
2013-07-11 20:33:02 +02:00
-- How many microblocks does this shape at the output inventory cost:
-- It may cause slight loss, but no gain.
2014-03-09 10:38:18 +01:00
circular_saw.cost_in_microblocks = {
1, 1, 1, 1, 1, 1, 1, 2,
2, 3, 2, 4, 2, 4, 5, 6,
7, 1, 1, 2, 4, 6, 7, 8,
1, 2, 2, 3, 1, 1, 2, 4,
4, 2, 6, 7, 3, 7, 7, 4,
8, 3, 2, 6, 2, 1, 3, 4
2014-03-09 10:38:18 +01:00
}
circular_saw.names = {
{"micro", "_1"},
{"panel", "_1"},
{"micro", "_2"},
{"panel", "_2"},
{"micro", "_4"},
{"panel", "_4"},
{"micro", ""},
{"panel", ""},
2014-03-09 10:38:18 +01:00
{"micro", "_12"},
{"panel", "_12"},
{"micro", "_14"},
{"panel", "_14"},
{"micro", "_15"},
{"panel", "_15"},
{"stair", "_outer"},
{"stair", ""},
2014-03-09 10:38:18 +01:00
{"stair", "_inner"},
{"slab", "_1"},
{"slab", "_2"},
{"slab", "_quarter"},
{"slab", ""},
{"slab", "_three_quarter"},
{"slab", "_14"},
{"slab", "_15"},
{"slab", "_two_sides"},
{"slab", "_three_sides"},
{"slab", "_three_sides_u"},
2014-03-09 10:38:18 +01:00
{"stair", "_half"},
{"stair", "_alt_1"},
{"stair", "_alt_2"},
{"stair", "_alt_4"},
{"stair", "_alt"},
2015-06-06 10:30:49 +02:00
{"slope", ""},
{"slope", "_half"},
{"slope", "_half_raised"},
{"slope", "_inner"},
{"slope", "_inner_half"},
{"slope", "_inner_half_raised"},
2015-06-27 21:07:50 +02:00
{"slope", "_inner_cut"},
{"slope", "_inner_cut_half"},
2015-06-27 21:07:50 +02:00
{"slope", "_inner_cut_half_raised"},
2015-06-06 10:30:49 +02:00
{"slope", "_outer"},
{"slope", "_outer_half"},
{"slope", "_outer_half_raised"},
{"slope", "_outer_cut"},
{"slope", "_outer_cut_half"},
2015-06-27 21:07:50 +02:00
{"slope", "_outer_cut_half_raised"},
{"slope", "_cut"},
2014-03-09 10:38:18 +01:00
}
function circular_saw:get_cost(inv, stackname)
local name = minetest.registered_aliases[stackname] or stackname
2014-03-09 10:38:18 +01:00
for i, item in pairs(inv:get_list("output")) do
if item:get_name() == name then
2014-03-09 10:38:18 +01:00
return circular_saw.cost_in_microblocks[i]
end
end
end
2013-07-11 20:33:02 +02:00
2014-03-09 10:38:18 +01:00
function circular_saw:get_output_inv(modname, material, amount, max)
2014-07-20 21:58:12 +02:00
if (not max or max < 1 or max > 99) then max = 99 end
2014-03-09 10:38:18 +01:00
local list = {}
2015-06-06 11:38:26 +02:00
local pos = #list
-- If there is nothing inside, display empty inventory:
2014-03-09 10:38:18 +01:00
if amount < 1 then
return list
2013-07-11 20:33:02 +02:00
end
2015-06-14 20:42:23 +02:00
for i = 1, #circular_saw.names do
local t = circular_saw.names[i]
2014-03-09 10:38:18 +01:00
local cost = circular_saw.cost_in_microblocks[i]
2015-06-06 12:16:21 +02:00
local balance = math.min(math.floor(amount/cost), max)
local nodename = modname .. ":" .. t[1] .. "_" .. material .. t[2]
if minetest.registered_nodes[nodename] then
pos = pos + 1
list[pos] = nodename .. " " .. balance
end
2014-03-09 10:38:18 +01:00
end
return list
2013-07-11 20:33:02 +02:00
end
2014-03-09 10:38:18 +01:00
-- Reset empty circular_saw after last full block has been taken out
-- (or the circular_saw has been placed the first time)
-- Note: max_offered is not reset:
2014-03-09 10:38:18 +01:00
function circular_saw:reset(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
2018-07-13 14:03:30 +02:00
local owned_by = meta:get_string("owner")
if owned_by and owned_by ~= "" then
2021-11-14 02:14:19 +01:00
owned_by = (" ("..S("owned by @1", meta:get_string("owner"))..")")
2018-07-13 14:03:30 +02:00
else
owned_by = ""
end
2013-07-11 20:33:02 +02:00
2014-03-09 10:38:18 +01:00
inv:set_list("input", {})
inv:set_list("micro", {})
inv:set_list("output", {})
2013-07-11 20:33:02 +02:00
2018-07-13 14:03:30 +02:00
meta:set_int("anz", 0)
meta:set_string("infotext", S("Circular Saw is empty") .. owned_by)
2013-07-11 20:33:02 +02:00
end
2014-03-09 10:38:18 +01:00
-- Player has taken something out of the box or placed something inside
-- that amounts to count microblocks:
2014-03-09 10:38:18 +01:00
function circular_saw:update_inventory(pos, amount)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
2013-07-11 20:33:02 +02:00
2014-03-09 10:38:18 +01:00
amount = meta:get_int("anz") + amount
2013-07-11 20:33:02 +02:00
2019-04-21 16:57:42 +02:00
-- The material is recycled automatically.
2014-03-09 10:38:18 +01:00
inv:set_list("recycle", {})
2013-07-11 20:33:02 +02:00
2014-03-09 10:38:18 +01:00
if amount < 1 then -- If the last block is taken out.
self:reset(pos)
return
2013-07-11 20:33:02 +02:00
end
2014-03-09 10:38:18 +01:00
local stack = inv:get_stack("input", 1)
-- At least one "normal" block is necessary to see what kind of stairs are requested.
2014-03-09 10:38:18 +01:00
if stack:is_empty() then
-- Any microblocks not taken out yet are now lost.
-- (covers material loss in the machine)
self:reset(pos)
return
2013-07-11 20:33:02 +02:00
end
2014-08-20 19:21:47 +02:00
local node_name = stack:get_name() or ""
2018-07-14 00:36:35 +02:00
local node_def = stack:get_definition()
2014-08-10 17:26:11 +02:00
local name_parts = circular_saw.known_nodes[node_name] or ""
local modname = name_parts[1] or ""
local material = name_parts[2] or ""
2018-07-13 14:03:30 +02:00
local owned_by = meta:get_string("owner")
if owned_by and owned_by ~= "" then
2021-11-14 02:14:19 +01:00
owned_by = (" ("..S("owned by @1", meta:get_string("owner"))..")")
2018-07-13 14:03:30 +02:00
else
owned_by = ""
end
2013-07-11 20:33:02 +02:00
inv:set_list("input", { -- Display as many full blocks as possible:
2014-07-21 12:24:49 +02:00
node_name.. " " .. math.floor(amount / 8)
2014-03-09 10:38:18 +01:00
})
2013-07-11 20:33:02 +02:00
-- The stairnodes made of default nodes use moreblocks namespace, other mods keep own:
2014-03-09 10:38:18 +01:00
if modname == "default" then
modname = "moreblocks"
end
-- print("circular_saw set to " .. modname .. " : "
-- .. material .. " with " .. (amount) .. " microblocks.")
2014-03-09 10:38:18 +01:00
-- 0-7 microblocks may remain left-over:
2014-03-09 10:38:18 +01:00
inv:set_list("micro", {
modname .. ":micro_" .. material .. " " .. (amount % 8)
2014-03-09 10:38:18 +01:00
})
-- Display:
2014-03-09 10:38:18 +01:00
inv:set_list("output",
self:get_output_inv(modname, material, amount,
meta:get_int("max_offered")))
-- Store how many microblocks are available:
meta:set_int("anz", amount)
2014-03-09 10:38:18 +01:00
meta:set_string("infotext",
2018-07-14 00:36:35 +02:00
S("Circular Saw is working on @1",
node_def and node_def.description or material
) .. owned_by
2018-07-13 14:03:30 +02:00
)
2013-07-11 20:33:02 +02:00
end
-- The amount of items offered per shape can be configured:
2014-03-09 10:38:18 +01:00
function circular_saw.on_receive_fields(pos, formname, fields, sender)
local meta = minetest.get_meta(pos)
local max = tonumber(fields.max_offered)
if max and max > 0 then
meta:set_string("max_offered", max)
-- Update to show the correct number of items:
2014-03-09 10:38:18 +01:00
circular_saw:update_inventory(pos, 0)
2013-07-11 20:33:02 +02:00
end
end
2014-03-09 10:38:18 +01:00
-- Moving the inventory of the circular_saw around is not allowed because it
-- is a fictional inventory. Moving inventory around would be rather
-- impractical and make things more difficult to calculate:
2014-03-09 10:38:18 +01:00
function circular_saw.allow_metadata_inventory_move(
pos, from_list, from_index, to_list, to_index, count, player)
return 0
2013-07-11 20:33:02 +02:00
end
-- Only input- and recycle-slot are intended as input slots:
2014-03-09 10:38:18 +01:00
function circular_saw.allow_metadata_inventory_put(
pos, listname, index, stack, player)
-- The player is not allowed to put something in there:
2014-03-09 10:38:18 +01:00
if listname == "output" or listname == "micro" then
return 0
2013-07-11 20:33:02 +02:00
end
2014-03-09 10:38:18 +01:00
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stackname = stack:get_name()
local count = stack:get_count()
2013-07-11 20:33:02 +02:00
2019-04-21 16:57:42 +02:00
-- Only allow those items that are offered in the output inventory to be recycled:
2014-03-09 10:38:18 +01:00
if listname == "recycle" then
if not inv:contains_item("output", stackname) then
return 0
2013-07-11 20:33:02 +02:00
end
2014-03-09 10:38:18 +01:00
local stackmax = stack:get_stack_max()
local instack = inv:get_stack("input", 1)
local microstack = inv:get_stack("micro", 1)
local incount = instack:get_count()
local incost = (incount * 8) + microstack:get_count()
local maxcost = (stackmax * 8) + 7
local cost = circular_saw:get_cost(inv, stackname)
if (incost + cost) > maxcost then
return math.max((maxcost - incost) / cost, 0)
end
return count
2013-07-11 20:33:02 +02:00
end
-- Only accept certain blocks as input which are known to be craftable into stairs:
2014-03-09 10:38:18 +01:00
if listname == "input" then
if not inv:is_empty("input") then
if inv:get_stack("input", index):get_name() ~= stackname then
return 0
end
end
if not inv:is_empty("micro") then
local microstackname = inv:get_stack("micro", 1):get_name():gsub("^.+:micro_", "", 1)
local cutstackname = stackname:gsub("^.+:", "", 1)
if microstackname ~= cutstackname then
return 0
end
end
2014-03-09 10:38:18 +01:00
for name, t in pairs(circular_saw.known_nodes) do
if name == stackname and inv:room_for_item("input", stack) then
return count
end
2013-07-11 20:33:02 +02:00
end
2014-03-09 10:38:18 +01:00
return 0
2013-07-11 20:33:02 +02:00
end
end
-- Taking is allowed from all slots (even the internal microblock slot).
2014-03-09 10:38:18 +01:00
-- Putting something in is slightly more complicated than taking anything
-- because we have to make sure it is of a suitable material:
2014-03-09 10:38:18 +01:00
function circular_saw.on_metadata_inventory_put(
pos, listname, index, stack, player)
-- We need to find out if the circular_saw is already set to a
-- specific material or not:
2014-03-09 10:38:18 +01:00
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local stackname = stack:get_name()
local count = stack:get_count()
-- Putting something into the input slot is only possible if that had
-- been empty before or did contain something of the same material:
2014-03-09 10:38:18 +01:00
if listname == "input" then
-- Each new block is worth 8 microblocks:
2014-03-09 10:38:18 +01:00
circular_saw:update_inventory(pos, 8 * count)
elseif listname == "recycle" then
-- Lets look which shape this represents:
2014-03-09 10:38:18 +01:00
local cost = circular_saw:get_cost(inv, stackname)
local input_stack = inv:get_stack("input", 1)
-- check if this would not exceed input itemstack max_stacks
if input_stack:get_count() + ((cost * count) / 8) <= input_stack:get_stack_max() then
circular_saw:update_inventory(pos, cost * count)
end
2014-03-09 10:38:18 +01:00
end
end
2013-07-11 20:33:02 +02:00
function circular_saw.allow_metadata_inventory_take(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local input_stack = inv:get_stack(listname, index)
local player_inv = player:get_inventory()
if not player_inv:room_for_item("main", input_stack) then
return 0
else return stack:get_count()
end
end
2014-03-09 10:38:18 +01:00
function circular_saw.on_metadata_inventory_take(
pos, listname, index, stack, player)
-- Prevent (inbuilt) swapping between inventories with different blocks
-- corrupting player inventory or Saw with 'unknown' items.
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local input_stack = inv:get_stack(listname, index)
if not input_stack:is_empty() and input_stack:get_name()~=stack:get_name() then
local player_inv = player:get_inventory()
-- Prevent arbitrary item duplication.
inv:remove_item(listname, input_stack)
if player_inv:room_for_item("main", input_stack) then
player_inv:add_item("main", input_stack)
end
circular_saw:reset(pos)
return
end
2014-03-09 10:38:18 +01:00
-- If it is one of the offered stairs: find out how many
2019-04-21 16:57:42 +02:00
-- microblocks have to be subtracted:
2014-03-09 10:38:18 +01:00
if listname == "output" then
-- We do know how much each block at each position costs:
2014-03-09 10:38:18 +01:00
local cost = circular_saw.cost_in_microblocks[index]
* stack:get_count()
circular_saw:update_inventory(pos, -cost)
elseif listname == "micro" then
-- Each microblock costs 1 microblock:
2014-03-09 10:38:18 +01:00
circular_saw:update_inventory(pos, -stack:get_count())
elseif listname == "input" then
-- Each normal (= full) block taken costs 8 microblocks:
2014-03-09 10:38:18 +01:00
circular_saw:update_inventory(pos, 8 * -stack:get_count())
2013-07-11 20:33:02 +02:00
end
-- The recycle field plays no role here since it is processed immediately.
2013-07-11 20:33:02 +02:00
end
local has_default_mod = minetest.get_modpath("default")
2014-03-09 10:38:18 +01:00
function circular_saw.on_construct(pos)
local meta = minetest.get_meta(pos)
local fancy_inv = ""
if has_default_mod then
-- prepend background and slot styles from default if available
fancy_inv = default.gui_bg..default.gui_bg_img..default.gui_slots
end
2017-09-18 13:49:36 +02:00
meta:set_string(
2021-11-14 02:14:19 +01:00
--FIXME Not work with @n in this part bug in minetest/minetest#7450.
2017-09-18 13:49:36 +02:00
"formspec", "size[11,10]"..fancy_inv..
2021-11-14 02:14:19 +01:00
"label[0,0;" ..S("Input material").. "]" ..
"list[current_name;input;1.7,0;1,1;]" ..
2018-07-13 23:26:37 +02:00
"label[0,1;" ..F(S("Left-over")).. "]" ..
2021-11-14 02:14:19 +01:00
"list[current_name;micro;1.7,1;1,1;]" ..
"label[0,2;" ..F(S("Recycle output")).. "]" ..
"list[current_name;recycle;1.7,2;1,1;]" ..
2018-07-13 23:26:37 +02:00
"field[0.3,3.5;1,1;max_offered;" ..F(S("Max")).. ":;${max_offered}]" ..
2021-11-14 02:14:19 +01:00
"button[1,3.2;1.7,1;Set;" ..F(S("Set")).. "]" ..
2017-09-18 13:49:36 +02:00
"list[current_name;output;2.8,0;8,6;]" ..
"list[current_player;main;1.5,6.25;8,4;]" ..
"listring[current_name;output]" ..
"listring[current_player;main]" ..
"listring[current_name;input]" ..
"listring[current_player;main]" ..
"listring[current_name;micro]" ..
"listring[current_player;main]" ..
"listring[current_name;recycle]" ..
"listring[current_player;main]"
)
2014-03-09 10:38:18 +01:00
meta:set_int("anz", 0) -- No microblocks inside yet.
2014-03-09 10:38:18 +01:00
meta:set_string("max_offered", 99) -- How many items of this kind are offered by default?
meta:set_string("infotext", S("Circular Saw is empty"))
2013-07-11 20:33:02 +02:00
local inv = meta:get_inventory()
inv:set_size("input", 1) -- Input slot for full blocks of material x.
inv:set_size("micro", 1) -- Storage for 1-7 surplus microblocks.
inv:set_size("recycle", 1) -- Surplus partial blocks can be placed here.
2015-06-27 21:07:50 +02:00
inv:set_size("output", 6*8) -- 6x8 versions of stair-parts of material x.
2013-07-11 20:33:02 +02:00
2014-03-09 10:38:18 +01:00
circular_saw:reset(pos)
2013-07-11 20:33:02 +02:00
end
2014-03-09 10:38:18 +01:00
function circular_saw.can_dig(pos,player)
local meta = minetest.get_meta(pos)
2013-07-11 20:33:02 +02:00
local inv = meta:get_inventory()
2014-03-09 10:38:18 +01:00
if not inv:is_empty("input") or
not inv:is_empty("micro") or
not inv:is_empty("recycle") then
2013-07-11 20:33:02 +02:00
return false
end
-- Can be dug by anyone when empty, not only by the owner:
2013-07-11 20:33:02 +02:00
return true
2014-03-09 10:38:18 +01:00
end
2013-07-11 20:33:02 +02:00
minetest.register_node("moreblocks:circular_saw", {
description = S("Circular Saw"),
drawtype = "nodebox",
2014-03-09 10:38:18 +01:00
node_box = {
type = "fixed",
2014-03-09 10:38:18 +01:00
fixed = {
{-0.4, -0.5, -0.4, -0.25, 0.25, -0.25}, -- Leg
{0.25, -0.5, 0.25, 0.4, 0.25, 0.4}, -- Leg
{-0.4, -0.5, 0.25, -0.25, 0.25, 0.4}, -- Leg
2014-03-09 10:38:18 +01:00
{0.25, -0.5, -0.4, 0.4, 0.25, -0.25}, -- Leg
{-0.5, 0.25, -0.5, 0.5, 0.375, 0.5}, -- Tabletop
{-0.01, 0.4375, -0.125, 0.01, 0.5, 0.125}, -- Saw blade (top)
{-0.01, 0.375, -0.1875, 0.01, 0.4375, 0.1875}, -- Saw blade (bottom)
{-0.25, -0.0625, -0.25, 0.25, 0.25, 0.25}, -- Motor case
2013-07-11 20:33:02 +02:00
},
2014-03-09 10:38:18 +01:00
},
tiles = {"moreblocks_circular_saw_top.png",
"moreblocks_circular_saw_bottom.png",
"moreblocks_circular_saw_side.png"},
paramtype = "light",
2014-03-09 10:38:18 +01:00
sunlight_propagates = true,
paramtype2 = "facedir",
groups = {choppy = 2,oddly_breakable_by_hand = 2},
is_ground_content = false,
sounds = moreblocks.node_sound_wood_defaults(),
2014-03-09 10:38:18 +01:00
on_construct = circular_saw.on_construct,
can_dig = circular_saw.can_dig,
-- Set the owner of this circular saw.
2014-03-09 10:38:18 +01:00
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
local owner = placer and placer:get_player_name() or ""
2018-07-13 14:03:30 +02:00
local owned_by = owner
if owner ~= "" then
owned_by = (" (%s)"):format(S("owned by @1", owner))
2018-07-13 14:03:30 +02:00
end
2014-03-09 10:38:18 +01:00
meta:set_string("owner", owner)
meta:set_string("infotext", S("Circular Saw is empty") .. owned_by)
2014-03-09 10:38:18 +01:00
end,
-- The amount of items offered per shape can be configured:
2014-03-09 10:38:18 +01:00
on_receive_fields = circular_saw.on_receive_fields,
allow_metadata_inventory_move = circular_saw.allow_metadata_inventory_move,
-- Only input- and recycle-slot are intended as input slots:
2014-03-09 10:38:18 +01:00
allow_metadata_inventory_put = circular_saw.allow_metadata_inventory_put,
allow_metadata_inventory_take = circular_saw.allow_metadata_inventory_take,
2014-03-09 10:38:18 +01:00
-- Taking is allowed from all slots (even the internal microblock slot). Moving is forbidden.
-- Putting something in is slightly more complicated than taking anything because we have to make sure it is of a suitable material:
2014-03-09 10:38:18 +01:00
on_metadata_inventory_put = circular_saw.on_metadata_inventory_put,
on_metadata_inventory_take = circular_saw.on_metadata_inventory_take,
2013-07-11 20:33:02 +02:00
})