peilaus alkaen
				https://github.com/minetest-mods/moreblocks.git
				synced 2025-10-31 05:45:22 +01:00 
			
		
		
		
	Calling to 'circular_saw:reset(pos)' clears the 'output' inventory, thus subsequent 'circular_saw:get_cost' calls would return 'nil'. This commit fixes that by relying on (semi-)recent engine bug fixes.
		
			
				
	
	
		
			485 rivejä
		
	
	
		
			15 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			485 rivejä
		
	
	
		
			15 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| --[[
 | |
| More Blocks: circular saw
 | |
| 
 | |
| Copyright © 2011-2020 Hugo Locurcio, Sokomine and contributors.
 | |
| Licensed under the zlib license. See LICENSE.md for more information.
 | |
| --]]
 | |
| 
 | |
| local S = moreblocks.S
 | |
| local F = minetest.formspec_escape
 | |
| 
 | |
| circular_saw = {}
 | |
| 
 | |
| circular_saw.known_stairs = setmetatable({}, {
 | |
| 	__newindex = function(k, v)
 | |
| 		local modname = minetest.get_current_modname()
 | |
| 		print(("WARNING: mod %s tried to add node %s to the circular saw manually."):format(modname, v))
 | |
| 	end,
 | |
| })
 | |
| 
 | |
| if not core.features.physics_overrides_v2 then
 | |
| 	-- Notable engine PR: #13919
 | |
| 	core.log("warning", "[moreblocks] Detected Luanti/Minetest < 5.8.0. Inventory handling issues may occur.")
 | |
| end
 | |
| 
 | |
| 
 | |
| -- This is populated by stairsplus:register_all:
 | |
| circular_saw.known_nodes = {}
 | |
| 
 | |
| -- This is populated by stairsplus:register_micro:
 | |
| circular_saw.microblocks = {}
 | |
| 
 | |
| -- How many microblocks does this shape at the output inventory cost:
 | |
| -- It may cause slight loss, but no gain.
 | |
| 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
 | |
| }
 | |
| 
 | |
| circular_saw.names = {
 | |
| 	{"micro", "_1"},
 | |
| 	{"panel", "_1"},
 | |
| 	{"micro", "_2"},
 | |
| 	{"panel", "_2"},
 | |
| 	{"micro", "_4"},
 | |
| 	{"panel", "_4"},
 | |
| 	{"micro", ""},
 | |
| 	{"panel", ""},
 | |
| 
 | |
| 	{"micro", "_12"},
 | |
| 	{"panel", "_12"},
 | |
| 	{"micro", "_14"},
 | |
| 	{"panel", "_14"},
 | |
| 	{"micro", "_15"},
 | |
| 	{"panel", "_15"},
 | |
| 	{"stair", "_outer"},
 | |
| 	{"stair", ""},
 | |
| 
 | |
| 	{"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"},
 | |
| 	{"stair", "_half"},
 | |
| 	{"stair", "_alt_1"},
 | |
| 	{"stair", "_alt_2"},
 | |
| 	{"stair", "_alt_4"},
 | |
| 	{"stair", "_alt"},
 | |
| 
 | |
| 	{"slope", ""},
 | |
| 	{"slope", "_half"},
 | |
| 	{"slope", "_half_raised"},
 | |
| 	{"slope", "_inner"},
 | |
| 	{"slope", "_inner_half"},
 | |
| 	{"slope", "_inner_half_raised"},
 | |
| 	{"slope", "_inner_cut"},
 | |
| 	{"slope", "_inner_cut_half"},
 | |
| 
 | |
| 	{"slope", "_inner_cut_half_raised"},
 | |
| 	{"slope", "_outer"},
 | |
| 	{"slope", "_outer_half"},
 | |
| 	{"slope", "_outer_half_raised"},
 | |
| 	{"slope", "_outer_cut"},
 | |
| 	{"slope", "_outer_cut_half"},
 | |
| 	{"slope", "_outer_cut_half_raised"},
 | |
| 	{"slope", "_cut"},
 | |
| }
 | |
| 
 | |
| function circular_saw:get_cost(inv, stackname)
 | |
| 	local name = minetest.registered_aliases[stackname] or stackname
 | |
| 	for i, item in pairs(inv:get_list("output")) do
 | |
| 		if item:get_name() == name then
 | |
| 			return circular_saw.cost_in_microblocks[i]
 | |
| 		end
 | |
| 	end
 | |
| end
 | |
| 
 | |
| function circular_saw:get_output_inv(modname, material, amount, max)
 | |
| 	if (not max or max < 1 or max > 99) then max = 99 end
 | |
| 
 | |
| 	local list = {}
 | |
| 	local pos = #list
 | |
| 
 | |
| 	-- If there is nothing inside, display empty inventory:
 | |
| 	if amount < 1 then
 | |
| 		return list
 | |
| 	end
 | |
| 
 | |
| 	for i = 1, #circular_saw.names do
 | |
| 		local t = circular_saw.names[i]
 | |
| 		local cost = circular_saw.cost_in_microblocks[i]
 | |
| 		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
 | |
| 	end
 | |
| 	return list
 | |
| end
 | |
| 
 | |
| 
 | |
| -- 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:
 | |
| function circular_saw:reset(pos)
 | |
| 	local meta = minetest.get_meta(pos)
 | |
| 	local inv  = meta:get_inventory()
 | |
| 	local owned_by = meta:get_string("owner")
 | |
| 
 | |
| 	if owned_by and owned_by ~= "" then
 | |
| 		owned_by = (" ("..S("owned by @1", meta:get_string("owner"))..")")
 | |
| 	else
 | |
| 		owned_by = ""
 | |
| 	end
 | |
| 
 | |
| 	inv:set_list("input",  {})
 | |
| 	inv:set_list("micro",  {})
 | |
| 
 | |
| 	local microblockcount = inv:get_stack("micro",1):get_count()
 | |
| 	meta:set_int("anz", microblockcount)
 | |
| 	if microblockcount == 0 then
 | |
| 		meta:set_string("infotext", S("Circular Saw is empty") .. owned_by)
 | |
| 		inv:set_list("output", {})
 | |
| 	end
 | |
| end
 | |
| 
 | |
| 
 | |
| -- Player has taken something out of the box or placed something inside
 | |
| -- that amounts to count microblocks:
 | |
| function circular_saw:update_inventory(pos, amount)
 | |
| 	local meta          = minetest.get_meta(pos)
 | |
| 	local inv           = meta:get_inventory()
 | |
| 
 | |
| 	amount = meta:get_int("anz") + amount
 | |
| 
 | |
| 	-- The material is recycled automatically.
 | |
| 	inv:set_list("recycle",  {})
 | |
| 
 | |
| 	if amount < 1 then -- If the last block is taken out.
 | |
| 		self:reset(pos)
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	local stack = inv:get_stack("input",  1)
 | |
| 	local microstack = inv:get_stack("micro",1)
 | |
| 
 | |
| 	-- At least one (micro)block is necessary to see what kind of stairs are requested.
 | |
| 	if stack:is_empty() and microstack:is_empty() then
 | |
| 
 | |
| 		self:reset(pos)
 | |
| 		return
 | |
| 
 | |
| 	end
 | |
| 
 | |
| 	local node_name = circular_saw.microblocks[microstack:get_name()] or stack:get_name() or ""
 | |
| 	local node_def = stack:get_definition()
 | |
| 	local name_parts = circular_saw.known_nodes[node_name] or ""
 | |
| 	local modname  = name_parts[1]
 | |
| 	local material = name_parts[2]
 | |
| 	local owned_by = meta:get_string("owner")
 | |
| 
 | |
| 	if owned_by and owned_by ~= "" then
 | |
| 		owned_by = (" ("..S("owned by @1", meta:get_string("owner"))..")")
 | |
| 	else
 | |
| 		owned_by = ""
 | |
| 	end
 | |
| 
 | |
| 	inv:set_list("input", { -- Display as many full blocks as possible:
 | |
| 		node_name.. " " .. math.floor(amount / 8)
 | |
| 	})
 | |
| 
 | |
| 	-- The stairnodes made of default nodes use moreblocks namespace, other mods keep own:
 | |
| 	if modname == "default" then
 | |
| 		modname = "moreblocks"
 | |
| 	end
 | |
| 	-- print("circular_saw set to " .. modname .. " : "
 | |
| 	--	.. material .. " with " .. (amount) .. " microblocks.")
 | |
| 
 | |
| 	-- 0-7 microblocks may remain left-over:
 | |
| 	inv:set_list("micro", {
 | |
| 		modname .. ":micro_" .. material .. " " .. (amount % 8)
 | |
| 	})
 | |
| 
 | |
| 	-- Display:
 | |
| 	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)
 | |
| 
 | |
| 	meta:set_string("infotext",
 | |
| 		S("Circular Saw is working on @1",
 | |
| 			node_def and node_def.description or material
 | |
| 		) .. owned_by
 | |
| 	)
 | |
| end
 | |
| 
 | |
| 
 | |
| -- The amount of items offered per shape can be configured:
 | |
| 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:
 | |
| 		circular_saw:update_inventory(pos, 0)
 | |
| 	end
 | |
| end
 | |
| 
 | |
| 
 | |
| -- 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:
 | |
| function circular_saw.allow_metadata_inventory_move(
 | |
| 		pos, from_list, from_index, to_list, to_index, count, player)
 | |
| 	return 0
 | |
| end
 | |
| 
 | |
| 
 | |
| -- Only input- and recycle-slot (and microblock slot when empty) are intended as input slots:
 | |
| function circular_saw.allow_metadata_inventory_put(
 | |
| 		pos, listname, index, stack, player)
 | |
| 	-- The player is not allowed to put something in there:
 | |
| 	if listname == "output" then
 | |
| 		return 0
 | |
| 	end
 | |
| 
 | |
| 	local meta = minetest.get_meta(pos)
 | |
| 	local inv  = meta:get_inventory()
 | |
| 	local stackname = stack:get_name()
 | |
| 	local count = stack:get_count()
 | |
| 
 | |
| 	-- Only allow those items that are offered in the output inventory to be recycled:
 | |
| 	if listname == "recycle" then
 | |
| 		if not inv:contains_item("output", stackname) then
 | |
| 			return 0
 | |
| 		end
 | |
| 		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
 | |
| 	end
 | |
| 
 | |
| 	-- Only accept certain blocks as input which are known to be craftable into stairs:
 | |
| 	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
 | |
| 		for name, t in pairs(circular_saw.known_nodes) do
 | |
| 			if name == stackname and inv:room_for_item("input", stack) then
 | |
| 				return count
 | |
| 			end
 | |
| 		end
 | |
| 		return 0
 | |
| 	end
 | |
| 
 | |
| 	if listname == "micro" then
 | |
| 		if not (inv:is_empty("input") and inv:is_empty("micro")) then return 0 end
 | |
| 		for name, t in pairs(circular_saw.microblocks) do
 | |
| 			if name == stackname and inv:room_for_item("input", stack) then
 | |
| 				return count
 | |
| 			end
 | |
| 		end
 | |
| 		return 0
 | |
| 	end
 | |
| end
 | |
| 
 | |
| -- Taking is allowed from all slots (even the internal microblock slot).
 | |
| -- Putting something in is slightly more complicated than taking anything
 | |
| -- because we have to make sure it is of a suitable material:
 | |
| 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:
 | |
| 	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:
 | |
| 	if listname == "input" then
 | |
| 		-- Each new block is worth 8 microblocks:
 | |
| 		circular_saw:update_inventory(pos, 8 * count)
 | |
| 	elseif listname == "micro" then
 | |
| 		circular_saw:update_inventory(pos, count)
 | |
| 	elseif listname == "recycle" then
 | |
| 		-- Lets look which shape this represents:
 | |
| 		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
 | |
| 	end
 | |
| end
 | |
| 
 | |
| 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
 | |
| 
 | |
| function circular_saw.on_metadata_inventory_take(
 | |
| 		pos, listname, index, stack, player)
 | |
| 	-- If it is one of the offered stairs: find out how many
 | |
| 	-- microblocks have to be subtracted:
 | |
| 	if listname == "output" then
 | |
| 		-- We do know how much each block at each position costs:
 | |
| 		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:
 | |
| 		circular_saw:update_inventory(pos, -stack:get_count())
 | |
| 	elseif listname == "input" then
 | |
| 		-- Each normal (= full) block taken costs 8 microblocks:
 | |
| 		circular_saw:update_inventory(pos, 8 * -stack:get_count())
 | |
| 	end
 | |
| 	-- The recycle field plays no role here since it is processed immediately.
 | |
| end
 | |
| 
 | |
| local has_default_mod = minetest.get_modpath("default")
 | |
| 
 | |
| 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
 | |
| 	meta:set_string(
 | |
| 		--FIXME Not work with @n in this part bug in minetest/minetest#7450.
 | |
| 		"formspec", "size[11,10]"..fancy_inv..
 | |
| 		"label[0,0;" ..S("Input material").. "]" ..
 | |
| 		"list[current_name;input;1.7,0;1,1;]" ..
 | |
| 		"label[0,1;" ..F(S("Left-over")).. "]" ..
 | |
| 		"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;]" ..
 | |
| 		"field[0.3,3.5;1,1;max_offered;" ..F(S("Max")).. ":;${max_offered}]" ..
 | |
| 		"button[1,3.2;1.7,1;Set;" ..F(S("Set")).. "]" ..
 | |
| 		"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]"
 | |
| 	)
 | |
| 
 | |
| 	meta:set_int("anz", 0) -- No microblocks inside yet.
 | |
| 	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"))
 | |
| 
 | |
| 	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.
 | |
| 	inv:set_size("output", 6*8) -- 6x8 versions of stair-parts of material x.
 | |
| 
 | |
| 	circular_saw:reset(pos)
 | |
| end
 | |
| 
 | |
| 
 | |
| function circular_saw.can_dig(pos,player)
 | |
| 	local meta = minetest.get_meta(pos)
 | |
| 	local inv = meta:get_inventory()
 | |
| 	if not inv:is_empty("input") or
 | |
| 	   not inv:is_empty("micro") or
 | |
| 	   not inv:is_empty("recycle") then
 | |
| 		return false
 | |
| 	end
 | |
| 	-- Can be dug by anyone when empty, not only by the owner:
 | |
| 	return true
 | |
| end
 | |
| 
 | |
| minetest.register_node("moreblocks:circular_saw",  {
 | |
| 	description = S("Circular Saw"),
 | |
| 	drawtype = "nodebox",
 | |
| 	node_box = {
 | |
| 		type = "fixed",
 | |
| 		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
 | |
| 			{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
 | |
| 		},
 | |
| 	},
 | |
| 	tiles = {"moreblocks_circular_saw_top.png",
 | |
| 		"moreblocks_circular_saw_bottom.png",
 | |
| 		"moreblocks_circular_saw_side.png"},
 | |
| 	paramtype = "light",
 | |
| 	sunlight_propagates = true,
 | |
| 	paramtype2 = "facedir",
 | |
| 	groups = {choppy = 2,oddly_breakable_by_hand = 2},
 | |
| 	is_ground_content = false,
 | |
| 	sounds = moreblocks.node_sound_wood_defaults(),
 | |
| 	on_construct = circular_saw.on_construct,
 | |
| 	can_dig = circular_saw.can_dig,
 | |
| 	-- Set the owner of this circular saw.
 | |
| 	after_place_node = function(pos, placer)
 | |
| 		local meta = minetest.get_meta(pos)
 | |
| 		local owner = placer and placer:get_player_name() or ""
 | |
| 		local owned_by = owner
 | |
| 
 | |
| 		if owner ~= "" then
 | |
| 			owned_by = (" (%s)"):format(S("owned by @1", owner))
 | |
| 		end
 | |
| 
 | |
| 		meta:set_string("owner",  owner)
 | |
| 		meta:set_string("infotext", S("Circular Saw is empty") .. owned_by)
 | |
| 	end,
 | |
| 
 | |
| 	-- The amount of items offered per shape can be configured:
 | |
| 	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:
 | |
| 	allow_metadata_inventory_put = circular_saw.allow_metadata_inventory_put,
 | |
| 	allow_metadata_inventory_take = circular_saw.allow_metadata_inventory_take,
 | |
| 	-- 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:
 | |
| 	on_metadata_inventory_put = circular_saw.on_metadata_inventory_put,
 | |
| 	on_metadata_inventory_take = circular_saw.on_metadata_inventory_take,
 | |
| })
 |