-- 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.data.hardness;
grindcost = def.grindcost or 1;
grindvalue = def.grindvalue;
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
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;
};
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;
};
};
get = function(name,class)
local c = sorcery.itemclass.classes[class]
local o
if not c then return false 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;
}