sorcery  Artifact [c4c14b0c08]

Artifact c4c14b0c08563aa39831986659536fb8882b17850b066125e9e1f7d4cf929a34:

  • File infuser.lua — part of check-in [147592b8e9] at 2020-10-26 03:58:08 on branch trunk — add over-time spellcasting abstraction to enable metamagic and in particular disjunction, add more animations and sound effects, add excavation spell, possibly some others, forget when the last commit was, edit a bunch of magitech to make it subject to the disjunction mechanism (throw up a disjunction aura and waltz right through those force fields bby, wheee), also illumination spells, tweak runeforge and rune frequence to better the balance and also limit player frustration, move some math functions into their own library category, various tweaks and bugfixes, probably other shit i don't remember (user: lexi, size: 7825) [annotate] [blame] [check-ins using]

local infuser_formspec = function(percent)
	return string.format([[
		size[8,7]
		list[context;infusion;3.5,0;1,1;]
		list[context;potions;2.5,1.7;1,1;0]
			list[context;potions;3.5,2;1,1;1]
			list[context;potions;4.5,1.7;1,1;2]
			image[2.5,1.7;1,1;vessels_shelf_slot.png]
			image[3.5,2;1,1;vessels_shelf_slot.png]
			image[4.5,1.7;1,1;vessels_shelf_slot.png]
		image[3.5,1;1,1;gui_furnace_arrow_bg.png^[lowpart:%d:gui_furnace_arrow_fg.png^[transformR180]
		list[current_player;main;0,3.3;8,4;]
		listring[context;infusion]
		listring[current_player;main]
		listring[context;potions]
		listring[current_player;main]
		listring[context;infusion]
	]], percent)
end

local infuser_stop = function(pos)
	local meta = minetest.get_meta(pos)
	meta:set_float('runtime', 0)
	meta:set_string('formspec', infuser_formspec(0))
	meta:set_string('infotext', 'Infuser')
	minetest.get_node_timer(pos):stop()
end

local elixir_can_apply = function(elixir, potion)
	-- accepts an elixir def and potion def
	if elixir        == nil or
	   elixir._proto == nil or
	   potion        == nil then return false end

	if elixir._proto.apply and potion.on_use then
		-- the ingredient is an elixir and at least one potion
		-- is a fully enchanted, usable potion
		if elixir._proto.flag and potion._proto and
		   potion._proto['no_' .. elixir._proto.flag] == true then
			-- does the elixir have a property used to denote
			-- compatibility? if so, check the potion to see if it's
			-- marked as incompatible
			return false
		else
			return true
		end
	end
	
	return false
end

local effects_table = function(potion)
	local meta = potion:get_meta()
	local tbl = {}
	for k,v in pairs(sorcery.data.elixirs) do
		if not v.flag then goto skip end
		local val = meta:get_int(v.flag)
		if val > 0 then
			local aff, title, desc = v.describe(potion)
			if val > 3 then title = title .. ' x' .. val
			elseif val == 3 then title = 'thrice-' .. title
			elseif val == 2 then title = 'twice-' .. title
			end
			tbl[#tbl + 1] = {
				title = sorcery.lib.str.capitalize(title);
				desc = desc;
				affinity = aff;
			}
		end
	::skip::end
	return tbl
end

local infuser_timer = function(pos, elapsed)
	local meta = minetest.get_meta(pos)

	local inv = meta:get_inventory()
	local infusion = inv:get_list('infusion')
	local potions = inv:get_list('potions')
	local elixir = infusion[1]:get_definition()
	local probe = sorcery.spell.probe(pos)
	if probe.disjunction then return true end

	local potionct = 0

	do
		local ingredient -- *eyeroll*
		if infusion[1]:is_empty() then goto cancel end
		ingredient = infusion[1]:get_name()
		for i = 1,#potions do
			if potions[i]:is_empty() then goto skip end
			potionct = potionct + 1
			local base = potions[i]:get_name()
			local potion = potions[i]:get_definition()
			if elixir_can_apply(elixir,potion) then
				-- at least one combination makes a valid potion;
				-- we can start the infuser
				goto start
			end
			for _,v in pairs(sorcery.register.infusions.db) do
				if v.infuse == ingredient and v.into == base then
					-- at least one combination makes a valid
					-- potion; we can start the infuser
					goto start
				end
			end
		::skip:: end

		::cancel:: do
			infuser_stop(pos)
			return false
		end

		::start::
	end

	local time = meta:get_float("runtime") or 0
	local newtime = time + elapsed
	local infusion_time = potionct * (15 * 60) -- 15 minutes per potion
		-- FIXME make dependent on recipe
	local percent = math.min(100, math.floor(100 * (newtime / infusion_time)))
	local spawn = function(particle, scale, amt)
		minetest.add_particlespawner {
			amount = amt;
			time = 15;
			minpos = pos;
			maxpos = pos;
			minvel = {x = -0.1, y = 0.05, z = -0.1};
			maxvel = {x = 0.1, y = 0.1, z = 0.1};
			minacc = {x = 0, y = 0.1, z = 0};
			maxacc = {x = 0, y = 0.4, z = 0};
			minexptime = 2;
			maxexptime = 4;
			minsize = 0.5 * scale;
			maxsize = 3 * scale;
			glow = 14;
			texture = particle;
			animation = {
				type = "vertical_frames";
				aspect_h = 16;
				aspect_w = 16;
				length = 4.1;
			};
		}
	end
	-- for i=0,4 do
		spawn('sorcery_spark.png^[multiply:#FF8FDD', 1, 32 * 4)
	-- end
	-- for i=0,4 do
		spawn('sorcery_spark.png^[multiply:#FFB1F6', 0.5, 64 * 4)
	-- end
	
	local discharge = sorcery.lib.node.discharger(pos)
	
	if newtime >= infusion_time then
		-- finished
		local ingredient = infusion[1]:get_name()
		for i = 1,#potions do
			if potions[i]:is_empty() then goto skip end
			local base = potions[i]:get_name()
			local potion = potions[i]:get_definition()
			if elixir_can_apply(elixir, potion) then
				local newstack = inv:get_stack('potions',i)
				elixir._proto.apply(newstack, potion._proto)
				newstack:get_meta():set_string('description', sorcery.lib.ui.tooltip {
					title = potion._proto.name .. ' Draught';
					desc = potion._proto.desc;
					color = sorcery.lib.color(potion._proto.color):readable();
					props = effects_table(newstack);
				});
				inv:set_stack('potions',i,discharge(newstack))
			else
				for _,v in pairs(sorcery.register.infusions.db) do
					if v.infuse == ingredient and v.into == base then
						-- transform the base into the infusion
						inv:set_stack('potions',i,discharge(ItemStack(v.output)))
					end
				end
			end
		::skip:: end

		inv:set_stack('infusion',1,ItemStack(sorcery.register.residue.db[ingredient]))

		infuser_stop(pos)
		return false
	else
		meta:set_float('runtime', newtime)
		meta:set_string('formspec', infuser_formspec(percent))
		meta:set_string('infotext', 'Infuser (active)')
		return true
	end
end

local infuser_start = function(pos)
	local meta = minetest.get_meta(pos)
	infuser_stop(pos)
	infuser_timer(pos,0)
	minetest.get_node_timer(pos):start(15)
end

minetest.register_node("sorcery:infuser", {
	description = "Infuser";
	drawtype = "mesh";
	mesh = "sorcery-infuser.obj";
	paramtype2 = "facedir";
	after_dig_node = sorcery.lib.node.purge_container;
	tiles = { -- FIXME
		"default_stone.png",
		"default_copper_block.png",
		"default_steel_block.png",
		"default_bronze_block.png",
		"default_tin_block.png",
	};
	paramtype2 = 'facedir';
	groups = {
		cracky = 2, oddly_breakable_by_hand = 1, heavy = 1;
		sorcery_alchemy = 1, sorcery_magitech = 1;
	};
	_sorcery = {
		recipe = {
			note = 'Infuse special ingredients into liquids to create and alter powerful potions';
		};
	};
	selection_box = {
		type = 'fixed';
		fixed = {
			-0.37, -0.5, -0.37,
			 0.37,  0.5,  0.37
		};
	};
	collision_box = {
		type = 'fixed';
		fixed = {
			-0.37, -0.5, -0.37,
			 0.37,  0.5,  0.37
		};
	};

	on_construct = function(pos)
		local meta = minetest.get_meta(pos)
		local inv = meta:get_inventory()
		inv:set_size('infusion', 1)
		inv:set_size('potions', 3)
		meta:set_string('infotext','Infuser')
		infuser_timer(pos,0)
	end;

	on_timer = infuser_timer;

	on_metadata_inventory_move = infuser_start;
	on_metadata_inventory_put = infuser_start;
	allow_metadata_inventory_put = function(pos, listname, index, stack, player)
		local meta = minetest.get_meta(pos)
		local inv = meta:get_inventory()
		if not inv:get_stack(listname,index):is_empty() then
			return 0
		end

		if listname == 'infusion' then
			return 1
		elseif listname == 'potions' then
			if minetest.get_item_group(stack:get_name(), "vessel") >= 1 then
				return 1
			end
		end
		return 0
	end;
	allow_metadata_inventory_move = function(pos, from_list, from_idx, to_list, to_idx, count, player)
		local meta = minetest.get_meta(pos)
		local inv = meta:get_inventory()
		if inv:get_stack(to_list, to_idx):is_empty() then
			if to_list == 'potions' and to_list ~= from_list then
				if minetest.get_item_group(
					--[[name]] inv:get_stack(from_list, from_idx):get_name(),
					--[[group]] "vessel") >= 1 then
					return 1
				else
					return 0
				end
			else
				return 1
			end
		else
			return 0
		end
	end;
})