sorcery  smelter.lua at [794d5b023a]

File smelter.lua artifact 07ab2b0675 part of check-in 794d5b023a


-- alloying furnace
--
-- there are several kinds of alloy furnace, with varying
-- capabilities. the simplest can alloy two simple metals;
-- the most complex can alloy four high-grade metals such
-- as titanium, platinum, iridium, or levitanium.
--
-- furnace recipes follow a pattern: the number of crucibles
-- determines the input slots, the type of crucible determines
-- how hot the furnace can get, and various other components
-- (like a coolant circulator) can be added to allow the
-- creation of exotic metals.
--
-- alloy furnaces produce ingots that can later be melted down
-- again and cast into a mold to produce items of particular
-- shapes.

-- there are four kinds of crucibles: clay, aluminum, platinum,
-- and duranium.  clay crucibles are made by molding clay into
-- the proper shape and then firing it in a furnace. others
-- are made by casting.

local fragments_per_ingot = 4

for _, c in pairs { 'clay', 'aluminum', 'platinum', 'duranium' } do
	minetest.register_craftitem('sorcery:crucible_' .. c, {
		description = sorcery.lib.str.capitalize(c .. ' crucible');
		inventory_image = 'sorcery_crucible_' .. c .. '.png';
	})
end

minetest.register_craftitem('sorcery:crucible_clay_molding', {
	description = sorcery.lib.str.capitalize('Crucible molding');
	inventory_image = 'sorcery_crucible_clay_molding.png';
})

minetest.register_craft {
	recipe = {
		{ 'default:clay_lump', '', 'default:clay_lump'};
		{ 'default:clay_lump', '', 'default:clay_lump'};
		{ 'default:clay_lump', 'default:clay_lump', 'default:clay_lump'};
	};
	output = 'sorcery:crucible_clay_molding';
}
minetest.register_craft {
	type = 'shapeless';
	recipe = { 'sorcery:crucible_clay_molding' };
	output = 'default:clay_lump 7';
}
minetest.register_craft {
	type = 'cooking';
	recipe = 'sorcery:crucible_clay_molding';
	cooktime = 40;
	output = 'sorcery:crucible_clay';
}

local smelter_formspec = function(kind, fuel_progress, smelt_progress)
	local layouts = {
		[1] = {w = 1, h = 1}; [2] = {w = 2, h = 1}; [3] = {w = 3, h = 1};
		[4] = {w = 2, h = 2}; [5] = {w = 3, h = 2}; [6] = {w = 3, h = 2};
	}
	local inpos  = { x = 2.8, y = 1 }
	local insize = layouts[kind.size]
	local outpos = { x = 5.2, y = 2.4 }
	local outsize = layouts[kind.outsize]
	local fuelpos = { x = 2.8, y = 3.4 }
	local fuelsize = layouts[kind.fuelsize]
	return string.format([[
		size[8,8]
		list[context;input;%f,%f;%f,%f;]
		list[context;output;%f,%f;%f,%f;]
		list[context;fuel;%f,%f;%f,%f;]
		list[current_player;main;0,4.2;8,4]

		image[2.3,1.9;1,1;default_furnace_fire_bg.png^[lowpart:%u%%:default_furnace_fire_fg.png]
		image[3.2,1.9;1,1;gui_furnace_arrow_bg.png^[lowpart:%u%%:gui_furnace_arrow_fg.png^[transformR270]

		listring[context;output] listring[current_player;main]
		listring[context;input]  listring[current_player;main]
		listring[context;fuel]   listring[current_player;main]
	]],
	--input
		inpos.x - insize.w/2, -- pos
		inpos.y - insize.h/2, 
		insize.w, insize.h, -- size
	--output
		outpos.x - outsize.w/2, -- pos
		outpos.y - outsize.h/2, 
		outsize.w, outsize.h, -- size
	--fuel
		fuelpos.x - fuelsize.w/2, -- pos
		fuelpos.y - fuelsize.h/2, 
		fuelsize.w, fuelsize.h, -- size

		fuel_progress, smelt_progress
	)
end

local find_recipe = function(inv)
	local mix = {}
	local count = 0
	for i=1,inv:get_size('input') do
		local m = inv:get_stack('input',i)
		if m:is_empty() then goto skip end
		local l = sorcery.data.metallookup[m:get_name()]
		if not l then return false end
		mix[l.id] = (mix[l.id] or 0) + l.value
		count = count + l.value
	::skip::end
	-- everything is metal, we've finished summing it up.
	-- let's see if the assembled items match the ratio
	-- specified in any of the smelting recipes.
	local matches = 0
	for _,rec in pairs(sorcery.data.alloys) do
		local fac = nil
		local meltpoint = 1
		if rec.metals == nil then goto skip_recipe end
		for metal, ratio in pairs(rec.metals) do
			if mix[metal] and mix[metal] % ratio == 0 then
				if fac then
					if mix[metal] / ratio ~= fac then goto skip_recipe end
				else fac = math.floor(mix[metal] / ratio) end
				local m = sorcery.data.metals[metal]
				if m.meltpoint then
					meltpoint = math.max(meltpoint, m.meltpoint) end
			else goto skip_recipe end
		end
		do return rec, count, fac, meltpoint end
	::skip_recipe::end
	return false
end

local update_smelter = function(pos)
	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()
	local proto = minetest.registered_nodes[minetest.get_node(pos).name]._proto
	local recipe, count, factor, meltpoint = find_recipe(inv)
	if recipe and proto.temp >= meltpoint then
		minetest.get_node_timer(pos):start(1)
	else
		meta:set_float('burntime',0)
	end
end

local smelter_step = function(kind,active,pos,delta)
	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()
	local recipe, count, factor = find_recipe(inv)
	local cooktime
	local elapsed = meta:get_float('burntime') + delta
	meta:set_float('burnleft',meta:get_float('burnleft') - delta)
	if (not active) and (not recipe) then return false end
	if meta:get_float('burnleft') <= 0 or not active then
		if recipe then
			local burn, frep = minetest.get_craft_result {
				method = 'fuel', width = 1;
				items = { inv:get_stack('fuel',1) };
			}
			if burn.time == 0 then goto nofuel end
			inv:set_stack('fuel', 1, frep.items[1])
			meta:set_float('burnleft',burn.time)
			meta:set_float('burnmax',burn.time)
			if not active then
				minetest.swap_node(pos,
					sorcery.lib.tbl.merge(minetest.get_node(pos), {
						name = kind.id .. '_active'
					}))
				active = true
			end
		else goto nofuel end
	end

	if not recipe then goto update end
	
	cooktime = ((recipe.cooktime / fragments_per_ingot) * count) / factor
	if elapsed >= cooktime then
		elapsed = 0
		-- remove used items
		for i=1,inv:get_size('input') do
			local s = inv:get_stack('input',i)
			if s:is_empty() then goto skip end
			s:take_item(1) inv:set_stack('input',i,s)
		::skip::end

		local outstack
		if count % fragments_per_ingot == 0 then
			outstack = ItemStack {
				name = sorcery.data.metals[recipe.output].ingot or 'sorcery:' .. recipe.output .. '_ingot';
				count = count / fragments_per_ingot;
			}
		else
			outstack = ItemStack {
				name = 'sorcery:fragment_' .. recipe.output;
				count = count;
			}
		end

		local leftover = inv:add_item('output',outstack)
		if not leftover:is_empty() then
			minetest.add_item(pos, leftover)
		end
	end

	::update::
		meta:set_float('burntime',elapsed)
		meta:set_string('formspec', smelter_formspec(kind,
			math.min(1, meta:get_float('burnleft') /
						meta:get_float('burnmax')
					) * 100, -- fuel
			(cooktime and math.min(1, elapsed / cooktime) * 100) or 0 -- smelt
		))
		do return active end

	::nofuel::
		if active then
			minetest.swap_node(pos,
				sorcery.lib.tbl.merge(minetest.get_node(pos), { name = kind.id }))
		end
		meta:set_float('burnleft',0) -- just in case
	::noburn::
		meta:set_float('burntime',0) -- just in case
		meta:set_string('formspec', smelter_formspec(kind, 0, 0))
		return false
end

local register_smelter = function(kind)
	local recipe = {{},{};
		{'default:stone','default:furnace','default:stone'};
	} do
		local on = kind.crucible
		local ti = 'default:tin_ingot'
		local cu = 'default:copper_ingot'
		local crucmap = {
			[2] = { {cu,cu,cu}, {on,ti,on} };
			[3] = { {cu,on,cu}, {on,ti,on} };
			[4] = { {on,cu,on}, {on,ti,on} };
			[5] = { {on,cu,on}, {on,on,on} };
			[6] = { {on,on,on}, {on,on,on} };
		};
		for y=1,2 do recipe[y] = crucmap[kind.size][y] end
	end
	
	local desc = 'smelter';
	if kind.temp_name then desc = kind.temp_name .. ' ' .. desc end
	if kind.size_name then desc = kind.size_name .. ' ' .. desc end
	desc = sorcery.lib.str.capitalize(desc);
	local id = 'sorcery:smelter_' .. kind.material .. kind.size_name
	kind.id = id
	for _, active in pairs {false, true} do
		minetest.register_node((active and id .. '_active') or id, {
			_proto = kind;
			description = desc;
			drop = id;
			groups = { cracky = 2; };
			paramtype2 = 'facedir';
			light_source = (active and 9) or 0;
			on_construct = function(pos)
				local meta = minetest.get_meta(pos)
				local inv = meta:get_inventory()
				inv:set_size('input',kind.size)
				inv:set_size('output',kind.outsize)
				inv:set_size('fuel',kind.fuelsize)
				meta:set_string('infotext', desc)
				meta:set_string('formspec', smelter_formspec(kind, 0, 0))
				meta:set_float('burnleft',0)
				meta:set_float('burnmax',0)
				meta:set_float('burntime',0)
			end;
			on_metadata_inventory_put = update_smelter;
			on_metadata_inventory_move = update_smelter;
			on_metadata_inventory_take = update_smelter;
			on_timer = function(pos,delta) return smelter_step(kind, active, pos, delta) end;
			-- allow_metadata_inventory_put = function(pos, listname, index, stack, player)
			-- end;
			tiles = {
				'sorcery_smelter_top_' .. tostring(kind.size) .. '.png';
				'sorcery_smelter_bottom.png';
				'sorcery_smelter_side.png';
				'sorcery_smelter_side.png';
				'sorcery_smelter_side.png';
				'sorcery_smelter_front' .. ((active and '_hot') or '') .. '.png';
			};
		})
	end
	minetest.register_craft {
		recipe = recipe;
		output = id;
	}
end

for _, s in pairs {
	{2, 'small'};
	{3, 'large'};
	{4, 'grand'};
} do for _, t in pairs {
	{1, nil, 'clay'};
	{2, 'hot','aluminum'};
	{3, 'volcanic','platinum'};
	{4, 'stellar','duranium'};
	{5, 'nova','impervium'};
} do
	register_smelter {
		size = s[1], size_name = s[2];
		temp = t[1], temp_name = t[2];
		material = t[3];
		crucible = 'sorcery:crucible_' .. t[3];
		outsize = 4, fuelsize = 1;
	}
end end