sorcery  enchanter.lua at [0a49ac4849]

File enchanter.lua artifact f08588c5d9 part of check-in 0a49ac4849


local hitbox = {
	type = 'fixed';
	fixed = {
		-0.5, -0.5, -0.5;
		0.5, 0.1, 0.5;
	};
}

local enchantable_tools = {
	pickaxe = {}, pick = {};
	axe = {};
	sword = {};
	sickle = {};
	shovel = {};
	hoe = {};
};

sorcery.enchant = {} do
	local m = sorcery.lib.marshal
	local ench_t = m.g.struct {
		id = m.t.str;
		slot = m.t.u8;
		boost = m.t.u8; -- every enchantment has an intrinsic force
		-- separate from the confluence of the slot, which is
		-- determined by the composition of the wand used to generate
		-- it (for instance, a gold-wired wand at low wear, or a wand
		-- with specific gemstones, may have a boost level above 0)
	}
	local pack, unpack = m.transcoder {
		spells = m.g.array(8, ench_t);
		energy = m.t.u16;
	}
	local key = 'sorcery_enchantment_recs'
	sorcery.enchant.set = function(stack, data)
		local meta = stack:get_meta()
		meta:set_string(key, pack(data))
	end
	sorcery.enchant.get = function(stack)
		local meta = stack:get_meta()
		if meta:contains(key) then
			local data = meta:get_string(key)
			return unpack(data)
		else
			return {
				spells = {};
				energy = 0;
			}
		end
	end
	sorcery.enchant.strength = function(stack,id)
		-- this functions should be used whenever you need to
		-- determine the power of a particular enchantment on
		-- an enchanted item.
		local e = sorcery.enchant.get(stack)
		local p = 0.0
		local slots = sorcery.matreg.lookup[stack:get_name()].data.slots
		-- TODO handle strength-boosting spells!
		for _,s in pairs(e.spells) do
			if s.id == id then p = p + slots[s.slot] end
		end
		return p
	end
	sorcery.enchant.stackup = function(stack)
		-- stack update function. this should be called whenever
		-- the enchantment status of a stack changes; it will
		-- alter/reset tool capabilities and tooltip as necessary
		local e = sorcery.enchant.get(stack)
		local meta = stack:get_meta()
		local def = stack:get_definition()
		meta:set_string('tool_capabilities','')
		local done = {}
		local props = {}
		for _,s in pairs(e.spells) do
			if done[s.id] then goto skip end
			done[s.id] = true
			local pwr = sorcery.enchant.strength(stack,s.id)
				-- somewhat wasteful…
			local name, color, desc = sorcery.data.enchants[s.id].apply(stack,pwr)
			props[#props+1] = {
				title = name;
				desc = desc;
				color = sorcery.lib.color(desc);
			}
		::skip::end
		if #e.spells > 0 then
			meta:set_string('description', sorcery.lib.ui.tooltip {
				title = 'Enchanted ' .. def.description;
				props = props;
			})
		else
			meta:set_string('description','')
		end
	end
end

local enchanter_getsubj = function(item)
	if not item:is_empty() then
		for group, spells in pairs(enchantable_tools) do
			if minetest.get_item_group(item:get_name(), group) ~= 0 then
				return group, sorcery.matreg.lookup[item:get_name()]
			end
		end
	end
	return false
end
local enchanter_update = function(pos)
	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()
	local item = inv:get_stack('item',1)
	local slots = ''
	local itype, imat = enchanter_getsubj(item)
	if itype and imat and imat.data.slots then
		local n = #imat.data.slots
		local sw, sh = 2.1, 2.1;
		local w = sw * n;
		local spells = sorcery.enchant.get(item).spells
		for i=1,n do
			local slot=imat.data.slots[i]
			local x = (4 - (w/2) + (sw * (i-1))) + 0.2
			local offtbl = {
				[1] = {0};
				[2] = {0.3, 0.3};
				[3] = {0.3,   0, 0.3};
				[4] = {0.3,   0,   0, 0.3};
				[5] = {0.3,   0, 0.1,   0, 0.3};
				[6] = {0.3, 0.1,   0, 0.1, 0.3};
			};
			local y = 3.1 - offtbl[n][i]
			local affs = #slot.affinity
			local iconf = math.min(math.floor(slot.confluence * 10), 20)
			local pwr = ''
			local ap = {}
			for i,aff in pairs(slot.affinity) do
				pwr = pwr .. string.format([[
					image[%f,%f;%f,%f;sorcery_pentacle_power_%s.png^[verticalframe:20:%u]
				]], x,y, sw,sh, aff, math.max(1,iconf - i))
				ap[#ap+1] = {
					title = sorcery.lib.str.capitalize(aff) .. ' affinity';
					color = sorcery.lib.color(sorcery.data.affinities[aff].color);
					desc = sorcery.data.affinities[aff].desc;
				}
			end
			local hovertitle = 'Empty spell slot';
			local conf = tostring(math.floor(slot.confluence*100)) .. '%'
			local hoverdesc = 'An enchantment of one the following affinities can be anchored into this slot at ' .. conf .. ' confluence';
			for _,sp in pairs(spells) do
				if sp.slot == i then
					hovertitle = sorcery.lib.str.capitalize(sp.id)
					hoverdesc = 'An enchantment is anchored in this slot at ' .. conf .. ' confluence'
					break
				end
			end
			slots = slots .. string.format([[
				image[%f,%f;%f,%f;sorcery_pentacle.png]
				tooltip[%f,%f;%f,%f;%s;%s;%s]
			]],
				x,y, sw,sh,
				x+0.20,y+0.16, sw-0.84,sh-0.76,
				minetest.formspec_escape(sorcery.lib.ui.tooltip {
					title = hovertitle;
					desc = hoverdesc;
					props = ap;
				}),
				'#37002C','#FFC8F5'
			) .. pwr
		end
	end

	meta:set_string('formspec', [[
		size[8,8.5]
		background[-0.25,-0.25;8.5,9;sorcery_enchanter_bg.png;true]
		image[2.13,0;4.35,4;sorcery_enchanter_glyphs.png]
		list[context;foci;3.5,0;1,1;0]
		list[context;item;3.5,1.2;1,1;]
		list[context;foci;2.5,2;1,1;1]
		list[context;foci;4.5,2;1,1;2]
		list[current_player;main;0,4.7;8,4;]
		listring[current_player;main]
		listring[context;item]
	]] .. slots)
end

minetest.register_node('sorcery:enchanter', {
	description = 'Enchanter';
	drawtype = 'mesh';
	mesh = 'sorcery-enchanter.obj';
	paramtype = 'light';
	paramtype2 = 'facedir';
	groups = { cracky = 2, oddly_breakable_by_hand = 2 };
	sunlight_propagates = true;
	selection_box = hitbox;
	collision_box = hitbox;
	tiles = {
		"default_obsidian.png";
		"default_steel_block.png";
		"default_bronze_block.png";
		"default_junglewood.png";
		"default_gold_block.png";
	};
	on_construct = function(pos)
		local meta = minetest.get_meta(pos)
		local inv = meta:get_inventory()
		inv:set_size('item', 1)
		inv:set_size('foci', 3)
		enchanter_update(pos)
	end;
	on_metadata_inventory_put  = enchanter_update;
	on_metadata_inventory_move = enchanter_update;
	on_metadata_inventory_take = enchanter_update;
})

for i=1,10 do
	minetest.register_node('sorcery:air_flash_' .. i, {
		drawtype = 'airlike';
		pointable = false; walkable = false;
		buildable_to = true;
		sunlight_propagates = true;
		light_source = i + 4;
		on_construct = function(pos)
			minetest.get_node_timer(pos):start(0.05)
		end;
		on_timer = function(pos)
			if i <= 2 then minetest.remove_node(pos) else
				minetest.set_node(pos, {name='sorcery:air_flash_1'})
				return true
			end
		end
	});
end

minetest.register_on_dignode(function(pos, node, puncher)
	if puncher == nil then return end -- i don't know why
	-- this is necessary but you get rare crashed without it

	-- we're goint to do something VERY evil here and
	-- replace the air with a "glow-air" that removes
	-- itself after a short period of time, to create
	-- a flash of light when an enchanted tool's used
	-- to dig out a node
	local tool = puncher:get_wielded_item()
	local meta = tool:get_meta()
	local sparks = {}
	local spark = function(name,color)
		if meta:contains('enchant_' .. name) then
			sparks[#sparks + 1] = {
				color = color;
				count = meta:get_int('enchant_' .. name);
			}
		end
	end
	spark('durable',sorcery.lib.color(0,89,245))
	spark('fast',sorcery.lib.color(245,147,89))
	if #sparks == 0 then return end
	if math.random(5) == 1 then
		minetest.set_node(pos, {name='sorcery:air_flash_' .. tostring(math.random(10))})
	end
	local range = function(min, max)
		local span = max - min
		local val = math.random() * span
		return val + min
	end
	for _,s in pairs(sparks) do
		for i=0,math.floor(s.count * range(1,3))  do
			local life = range(0.3,1);
			minetest.add_particle {
				pos = {
					x = pos.x + range(-0.5,0.5);
					z = pos.z + range(-0.5,0.5);
					y = pos.y + range(-0.5,0.5);
				};
				acceleration = {
					x = range(-0.5,0.5);
					z = range(-0.5,0.5);
					y = -0.1;
				};
				velocity = {
					x = range(-1.3,1.3);
					z = range(-1.3,1.3);
					y = range( 0.3,0.9);
				};
				expirationtime = life;
				size = range(0.5,1.5);
				vertical = true;
				texture = sorcery.lib.image('sorcery_spark.png'):multiply(s.color:brighten(1.2)):render();
				glow = 14;
				animation = {
					type = "vertical_frames";
					aspect_w = 16;
					aspect_h = 16;
					length = life * 1.1;
				};
			}
		end
	end
end)