sorcery  Artifact [ba9d479786]

Artifact ba9d4797868a03f376d64da920bd84253251af2c97b3b34b116d4c0dc9e72eb2:

  • File itemclass.lua — part of check-in [96c5289a2a] at 2020-10-21 03:35:35 on branch trunk — add rune forges, runes, amulet frames, write sacrifice spell, touch up amulet graphics, enable enchantment of amulets (though spells cannot yet be cast), defuckulate syncresis core icon, unfuckitize sneaky leycalc bug that's probably been the cause of some long-standing wackiness, add item classes, add some more textures, disbungle various other asstastrophes, remove sneaky old debug code, improve library code, add utility for uploading merge requests (user: lexi, size: 5018) [annotate] [blame] [check-ins using]

-- in theory, minetest groups are supposed to allow us to
-- give consistent, cross-mod classes to items, and easily
-- detect whether items fit into a particular class. unfortunately,
-- they don't really work for this purpose because we often
-- need to attach additional data to items that are outside
-- of our control (and the default mod's authors are amazingly
-- lax in grouping items; for instance, diamonds and mese
-- crystals aren't even part of a 'gem' or 'crystal' group!)
-- this module allows us to consistently classify items, and
-- easily maintain complex hierarchies of subclasses. whether
-- an item belongs to a class can be determined by checking
-- its groups, consulting compat tables, calling a custom
-- predicate function (possibly to check for a _sorcery
-- defprop), or recursing through a list of subclasses.
-- this also means that matters of identity are all controlled
-- from a central location.
sorcery.itemclass = {
	classes = {
		-- gem/crystalline and metal/metallic differentiate
		-- between crafting materials (i.e. gems or ingots
		-- themselves) and items crafted from those materials.
		-- the former includes only crafting materials, the
		-- latter includes both.
		gem = {
			compat = 'gems';
			groups = { 'gem', 'crystal'; };
			predicate = function(name)
				if minetest.get_item_group(name, 'sorcery_gem') ~= 0 
				or minetest.get_item_group(name, 'sorcery_shard') ~= 0 then
					return minetest.registered_items[name]._sorcery.material;
				end
			end;
		};
		crystalline = {
			subclass = {'gem'};
			predicate = function(name)
				local mat = sorcery.matreg.lookup[name]
				if mat and mat.gem then return mat end
			end;
		};
		grindable = {
			compat = 'grindables';
			subclass = {'metallic'};
			conform = {
				metallic = function(m)
					if m and m.data and m.data.parts and m.data.parts.powder then
						return {
							hardness = m.data.hardness;
							grindcost = 1;
							grindvalue = m.value or 1;
							powder = m.data.parts.powder;
						}
					end
				end;
			};
			predicate = function(name)
				local def = minetest.registered_items[name]._sorcery
				if not def then return nil end
				def = def.material
				if def and def.grindvalue then
					return {
						hardness = def.hardness or def.data.hardness;
						grindcost = def.grindcost or 1;
						grindvalue = def.grindvalue or def.value;
						powder = def.powder or def.data.parts.powder;
					}
				end
			end;
		};
		metal = {
			predicate = function(name)
				-- metallookup is a table of 'primary' metal
				-- items, like ingots, fragments, and powders
				return sorcery.data.metallookup[name]
			end;
		};
		metallic = {
			subclass = {'metal'};
			predicate = function(name)
				-- matreg is a registry binding crafted items,
				-- like armors and tools, to the material they
				-- are made out of. it's necessary because not
				-- all items we want to interact with have
				-- definitions under our control
				local mat = sorcery.matreg.lookup[name]
				if mat and mat.metal then return mat end
				local prop = minetest.registered_items[name]._sorcery
				if prop and prop.material and prop.material.metal then
					return prop.material
				end
			end;
		};
		material = {
			subclass = {'metallic','crystalline'};
		};
		ore = {
			groups = { 'ore' };
			compat = 'ore';
			predicate = function(name)
				-- maybe revise this at some point once sorcery is extricated
				-- from instant_ores and we have more control over the items
				-- we generate
				local orepfx = "stone_with_" -- }:<
				local barename = string.sub(name, string.find(name, ':') + 1)
				if string.sub(barename,1,string.len(orepfx)) == orepfx then
					local iname = string.sub(barename,string.len(orepfx) + 1)
					if sorcery.data.metals[iname] then
						return { metal = true, id = iname }
					elseif sorcery.data.gems[iname] then
						return { gem = true, id = iname }
					end
				end
			end;
		};
		fuel = {
			groups = {'fuel','flammable'};
			predicate = function(name)
				local c,di = minetest.get_craft_result {
					method = 'fuel';
					width = 1;
					items = { ItemStack(name) };
				}
				if c.time and c.time > 0 then
					return {
						burntime = c.time;
						leftover = di and di[1];
					}
				end
			end;
		};
	};
	get = function(name,class)
		local c = sorcery.itemclass.classes[class]
		local o
		if not c then return false end
		if type(name) ~= 'string' then name = name:get_name() end

		if c.predicate then 
			o = c.predicate(name)
			if o then return o end
		end

		if c.compat then
			o = sorcery.data.compat[c.compat][name]
			if o then return o end
		end

		if c.subclass then
			for _,s in pairs(c.subclass) do
				o = sorcery.itemclass.get(name,s)
				if o then
					if c.conform and c.conform[s] then
						return c.conform[s](o)
					else return o end
				end
			end
		end

		if c.groups then
			for _,g in pairs(c.groups) do
				o = minetest.get_item_group(name,g) 
				if o > 0 then return { kind = o } end
			end
			o = nil
		end

		return false
	end;
}