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)