local u = sorcery.lib
local m = u.marshal
local tunepack, tuneunpack = m.transcoder {
code = m.t.u16; -- disambiguate just in case
partner_x = m.t.s32;
partner_y = m.t.s32;
partner_z = m.t.s32;
-- note that the meaning of 'partner' is a bit confusing. attuning
-- is a directional relationship; that is, every item can be tuned
-- to only one item (its partner), but more items can be tuned to
-- it. while it will in most cases be reciprocal, this will not
-- always be the case (e.g. with portals)
}
sorcery.attunement = {
nodeid = function(node)
local meta = minetest.get_meta(node)
local id
if meta:contains('sorcery:identity') then
id = meta:get_string('sorcery:identity')
else
id = sorcery.lib.str.rand(16)
meta:set_string('sorcery:identity', id)
end
return id
end;
decode = function(raw)
local obj = tuneunpack(u.str.meta_dearmor(raw,true))
return {
code = obj.code;
partner = {
x = obj.partner_x;
y = obj.partner_y;
z = obj.partner_z;
};
}
end;
encode = function(tune)
return u.str.meta_armor(tunepack {
code = tune.code;
partner_x = tune.partner.x;
partner_y = tune.partner.y;
partner_z = tune.partner.z;
}, true)
end;
get = function(node)
local raw = minetest.get_meta(node):get_string('sorcery:attune')
if not raw then return nil end
return sorcery.attunement.decode(raw)
end;
verify = function(node)
local tune = sorcery.attunement.get(node)
if not tune then return nil end
local ptnr = sorcery.attunement.get(tune.partner)
if not ptnr then return nil end
local nodename =minetest.get_node(node).name
local ptnrname =minetest.get_node(tune.partner).name
local ndef = minetest.registered_nodes[nodename]
local pdef = minetest.registered_nodes[ptnrname]
if not (ndef and ndef._sorcery and pdef and pdef._sorcery and
ndef._sorcery.attune and pdef._sorcery.attune) then return nil end
ndef = ndef._sorcery.attune pdef = pdef._sorcery.attune
if ndef.reciprocal and (not vector.equals(ptnr.partner, node)) then return nil end
if tune.code == ptnr.code then
return tune, ptnr
end
return nil
end;
set = function(node,tune)
local meta = minetest.get_meta(node)
meta:set_string('sorcery:attune', sorcery.attunement.encode(tune))
meta:mark_as_private('sorcery:attune')
end;
pair = function(reciprocal,src,dest)
local d, code = sorcery.attunement.get(src)
local gdef = function(n)
local m = minetest.registered_nodes[minetest.get_node(n).name]
if m and m._sorcery and m._sorcery.attune then
return m._sorcery.attune
end
return nil
end
local dsrc, dtgt = gdef(src), gdef(tgt)
if not (dsrc and dtgt) then return nil end
if not (dsrc.source and dtgt.target) then return nil end
if dtgt.accepts ~= dsrc.class then return nil end
if reciprocal or not d then
code = math.random(0,65536)
sorcery.attunement.set(src, {
code = code;
partner = dest;
})
else code = d.code end
sorcery.attunement.set(dest, {
code = code;
partner = src;
})
end;
}