local constants = {
rune_mine_interval = 90;
-- how often a powered forge rolls for new runes
rune_cache_max = 4;
-- how many runes a runeforge can hold at a time
rune_grades = {'Fragile', 'Shoddy', 'Ordinary', 'Pristine'};
-- how many grades of rune quality/power there are
}
sorcery.register.runes.foreach('sorcery:generate',{},function(name,rune)
local id = 'sorcery:rune_' .. name
rune.image = rune.image or string.format('sorcery_rune_%s.png',name)
rune.item = id
minetest.register_craftitem(id, {
description = sorcery.lib.color(rune.tone):readable():fmt(rune.name .. ' Rune');
short_description = rune.name .. ' Rune';
inventory_image = rune.image;
stack_max = 1;
groups = {
sorcery_rune = 1;
not_in_creative_inventory = 1;
};
_proto = { id = name, data = rune; };
})
end)
local rune_set = function(stack,r)
local m = stack:get_meta()
local def = stack:get_definition()._proto.data
local grade
if r.grade then grade = r.grade
elseif m:contains('rune_grade') then grade = m:get_int('rune_grade') end
local qpfx = constants.rune_grades[grade]
local title = sorcery.lib.color(def.tone):readable():fmt(string.format('%s %s Rune',qpfx,def.name))
m:set_int('rune_grade',grade)
m:set_string('description',title)
end
sorcery.amulet = {}
sorcery.amulet.setrune = function(stack,rune)
local m = stack:get_meta()
if rune then
local rp = rune:get_definition()._proto
local rg = rune:get_meta():get_int('rune_grade')
m:set_string('amulet_rune', rp.id)
m:set_int('amulet_rune_grade', rg)
local spell = sorcery.amulet.getspell(stack)
if not spell then return nil end
local name = string.format('Amulet of %s', spell.name)
m:set_string('description', sorcery.lib.ui.tooltip {
title = name;
color = spell.tone;
desc = spell.desc;
})
else
m:set_string('description','')
m:set_string('amulet_rune','')
m:set_string('amulet_rune_grade','')
end
return stack
end
sorcery.amulet.getrune = function(stack)
local m = stack:get_meta()
if not m:contains('amulet_rune') then return nil end
local rune = m:get_string('amulet_rune')
local grade = m:get_int('amulet_rune_grade')
local rs = ItemStack(sorcery.data.runes[rune].item)
rune_set(rs, {grade = grade})
return rs
end
sorcery.amulet.getspell = function(stack)
local m = stack:get_meta()
local proto = stack:get_definition()._sorcery.amulet
local rune = m:get_string('amulet_rune')
local rd = sorcery.data.runes[rune]
local spell = rd.amulets[proto.base]
if not spell then return nil end
local title,desc,cast = spell.name, spell.desc, spell.cast
if proto.frame and spell.frame and spell.frame[proto.frame] then
local sp = spell.frame[proto.frame]
title = sp.name or title
desc = sp.desc or desc
cast = sp.desc or cast
end
return {
rune = rune;
spell = spell;
name = title;
desc = desc;
cast = cast;
tone = sorcery.lib.color(rd.tone);
}
end
local runeforge_update = function(pos,time)
local m = minetest.get_meta(pos)
local i = m:get_inventory()
local l = sorcery.ley.netcaps(pos,time or 1)
local pow_min = l.self.powerdraw >= l.self.minpower
local pow_max = l.self.powerdraw >= l.self.maxpower
if time and pow_min then -- roll for runes
local rolls = math.floor(time/constants.rune_mine_interval)
local newrunes = {}
for _=1,rolls do
local choices = {}
for name,rune in pairs(sorcery.data.runes) do
if rune.minpower*time <= l.self.powerdraw and math.random(rune.rarity) == 1 then
local n = ItemStack(rune.item)
choices[#choices + 1] = n
end
end
if #choices > 0 then newrunes[#newrunes + 1] = choices[math.random(#choices)] end
end
print('rolled for runes, got', dump(newrunes))
for _,r in pairs(newrunes) do
if i:room_for_item('cache',r) then
local qual = math.random(#constants.rune_grades)
rune_set(r,{grade = qual})
i:add_item('cache',r)
end
end
end
local spec = string.format([[
formspec_version[3] size[10.25,8] real_coordinates[true]
list[context;cache;%f,0.25;%u,1;]
list[context;amulet;3.40,1.50;1,1;]
list[context;active;5.90,1.50;1,1;]
list[current_player;main;0.25,3;8,4;]
image[0.25,0.50;1,1;sorcery_statlamp_%s.png]
]], (10.5 - constants.rune_cache_max*1.25)/2, constants.rune_cache_max,
pow_max and 'green' or (pow_min and 'yellow') or 'off')
m:set_string('formspec',spec)
return true
end
local rfbox = {
type = 'fixed';
fixed = {
-0.5, -0.5, -0.5;
0.5, 0.1, 0.5;
};
};
minetest.register_node('sorcery:runeforge', {
description = 'Rune Forge';
drawtype = 'mesh';
mesh = 'sorcery-runeforge.obj';
sunlight_propagates = true;
paramtype = 'light';
paramtype2 = 'facedir';
selection_box = rfbox;
collision_box = rfbox;
groups = {
choppy = 2;
oddly_breakable_by_hand = 2;
sorcery_magitech = 1;
sorcery_tech = 1;
sorcery_ley_device = 1;
};
tiles = {
'default_diamond_block.png';
'default_tin_block.png';
'sorcery_metal_iridium_shiny.png';
'sorcery_metal_vidrium_shiny.png';
'default_copper_block.png';
};
_sorcery = {
ley = {
mode = 'consume';
affinity = {'praxic'};
power = function(pos,time)
local max,min = 0
for _,r in pairs(sorcery.data.runes) do
if r.minpower > max then max = r.minpower end
if min == nil or r.minpower < min then min = r.minpower end
end
return min*time,max*time
end;
};
on_leychange = runeforge_update;
recipe = {
note = 'Periodically creates runes when sufficiently powered and can be used to imbue them into an amulet, giving it a powerful magical effect';
};
};
on_construct = function(pos)
local m = minetest.get_meta(pos)
local i = m:get_inventory()
i:set_size('cache',constants.rune_cache_max)
i:set_size('amulet',1)
i:set_size('active',1)
m:set_string('infotext','Rune Forge')
runeforge_update(pos)
minetest.get_node_timer(pos):start(constants.rune_mine_interval)
end;
after_dig_node = sorcery.lib.node.purge_only {'amulet'};
on_timer = runeforge_update;
on_metadata_inventory_move = function(pos, fl,fi, tl,ti, count, user)
local inv = minetest.get_meta(pos):get_inventory()
if fl == 'active' then
inv:set_stack('amulet',1,sorcery.amulet.setrune(inv:get_stack('amulet',1)))
elseif tl == 'active' then
inv:set_stack('amulet',1,sorcery.amulet.setrune(inv:get_stack('amulet',1), inv:get_stack(tl,ti)))
end
end;
on_metadata_inventory_put = function(pos, list, idx, stack, user)
if list == 'amulet' then
local inv = minetest.get_meta(pos):get_inventory()
inv:set_stack('active',1,ItemStack(sorcery.amulet.getrune(stack)))
end
end;
on_metadata_inventory_take = function(pos, list, idx, stack, user)
if list == 'amulet' then
minetest.get_meta(pos):get_inventory():set_stack('active',1,ItemStack())
end
end;
allow_metadata_inventory_put = function(pos,list,idx,stack,user)
if list == 'amulet' then
if minetest.get_item_group(stack:get_name(), 'sorcery_amulet') ~= 0 then
return 1
end
end
return 0
end;
allow_metadata_inventory_take = function(pos,list,idx,stack,user)
if list == 'amulet' then return 1 end
return 0
end;
allow_metadata_inventory_move = function(pos, fl,fi, tl,ti, count, user)
if fl == 'cache' then
if tl == 'cache' then return 1 end
if tl == 'active' then
local inv = minetest.get_meta(pos):get_inventory()
if not inv:is_empty('amulet') then
local amulet = inv:get_stack('amulet',1)
local rune = inv:get_stack(fl,fi)
if sorcery.data.runes[rune:get_definition()._proto.id].amulets[amulet:get_definition()._sorcery.amulet.base] then
return 1
end
end
end
end
if fl == 'active' then
if tl == 'cache' then return 1 end
end
return 0
end;
})
do local m = sorcery.data.metals
-- temporary recipe until a fancier multi-part crafting path can be come up with
-- TODO: better than this
minetest.register_craft {
output = 'sorcery:runeforge';
recipe = {
{'default:copper_ingot',m.vidrium.parts.block,'default:copper_ingot'};
{'default:diamond',m.iridium.parts.ingot,'default:diamond'};
{'default:tin_ingot','sorcery:core_syncretic','default:tin_ingot'};
};
}
end