-- contains functions for determining information about
-- the nearest leyline and its currents
sorcery.ley = {}
-- leylines are invisible force-currents that rise up from the core of the earth, carrying magical energy upwards. they weaken as they get closer to the surface. each leyline also has between one and three 'affinities', which control how easily they can be wielded to perform particular sorts of magic. for instance, praxic-affine leylines will charge wands enchanted with praxic spells more quickly than leylines lacking this affinity.
-- leylines are one of two mystic energy forms; the other is aetheric energy, which is beamed down from Heaven by the sun during the day and is the power wielded by the gods. mortals can make limited use of aetheric force by collecting it and beaming it from place to place -- see aether.lua (aether is stored and manipulated by use of diamond)
-- leylines are always available, unlike aetheric force, which can only be collected during the day. but aetheric force is accessible wherever one can see the sky, and the higher up you are, the more you can collect, whereas leylines vary randomly in strength and affinity by position.
sorcery.ley.estimate = function(pos)
local affs = {
'praxic'; 'counterpraxic'; 'cognic';
'mandatic'; 'occlutic'; 'imperic';
'syncretic'; 'entropic';
};
local forcemap = minetest.get_perlin(0xe9a01d, 3, 2, 150)
local aff1map = minetest.get_perlin(0x10eb03, 3, 2, 300)
local aff2map = minetest.get_perlin(0x491e12, 3, 2, 240)
local txpos = { --- :( :( :( :(
x = pos.x;
y = pos.z;
z = pos.y;
}
local normalize = function(map)
local v = map:get_2d(txpos)
return (v + 6) / 12 -- seriously??
end
local zfac = (txpos.z / -1024) + 1
local force = math.min(1, normalize(forcemap) * zfac)
local aff1 = affs[math.ceil(#affs * normalize(aff1map))] or 'fail'
local aff2v, aff2 = math.ceil(normalize(aff2map) * (#affs * 2))
if aff2v <= #affs then aff2 = affs[aff2v] end
return {
force = force;
aff = { aff1, aff2 };
}
end
minetest.register_chatcommand('leyline', {
description = 'See details about local ley force';
privs = { server = true };
func = function(caller,params)
local pos = minetest.get_player_by_name(caller):get_pos()
local ley = sorcery.ley.estimate(pos)
minetest.chat_send_player(caller, 'Leyline force ' .. tostring(ley.force) .. ' with affinities ' .. table.concat(ley.aff, ','))
end;
})
-- leyline energy can be transmitted via a conduit from a leysink. however, it cannot be stored like aetheric energy can be; leyline energy must be drawn when needed unless it is bound up in an enchantment (which simply delays its expression). leysinks provide a constant source of ley-force.
-- there are two nodes for transmitting leyline energy, wires and conduits. wires transmit a limited amount of energy, but are cheap and small. conduits transmit much more, but are expensive and take up full blocks. both are composed of electrum, the carrier, and copper, which prevents the ley-force from leaking out as dangerous radiance.
minetest.register_node('sorcery:conduit', {
description = 'Conduit';
tiles = {
'sorcery_conduit_copper_top.png';
'sorcery_conduit_copper_top.png';
'sorcery_conduit_copper_side.png';
};
groups = {
sorcery_ley_device = 1;
cracky = 3;
};
_sorcery = {
ley = { mode = 'signal'; power = 10 };
};
})
minetest.register_craft {
output = 'sorcery:conduit 4';
recipe = {
{'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'};
{'default:copper_ingot', 'sorcery:electrumblock', 'default:copper_ingot'};
{'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'};
};
};
minetest.register_craft {
output = 'sorcery:wire 4';
recipe = {
{'', 'basic_materials:copper_wire',''};
{'', 'sorcery:fragment_electrum', ''};
{'', 'basic_materials:copper_wire',''};
}
};
sorcery.ley.field_to_current = function(strength,time)
local ley_factor = 0.25
-- a ley harvester will produce this much current with
-- access to a full-strength leyline
return strength * ley_factor * time;
end
do -- register condenser
local gem = sorcery.lib.image('default_diamond_block.png')
local amethyst = gem:multiply(sorcery.lib.color(sorcery.data.gems.amethyst.tone))
local emerald = gem:multiply(sorcery.lib.color(sorcery.data.gems.emerald.tone))
local box = {
type = 'fixed';
fixed = {
-0.5, -0.5, -0.5;
0.5, 1.2, 0.5;
};
};
minetest.register_node('sorcery:condenser', {
description = 'Condenser';
drawtype = 'mesh';
mesh = 'sorcery-condenser.obj';
selection_box = box;
collision_box = box;
tiles = {
amethyst:render();
'sorcery_condenser.png';
'default_tin_block.png';
'default_stone.png';
'default_copper_block.png';
emerald:render();
};
groups = {
cracky = 2;
sorcery_ley_device = 1;
};
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string('infotext','Condenser')
end;
_sorcery = {
ley = { mode = 'produce' };
on_leycalc = function(pos,time)
local l = sorcery.ley.estimate(pos)
return {
power = sorcery.ley.field_to_current(l.force, time);
affinity = l.aff;
}
end;
};
})
end
minetest.register_craft {
output = 'sorcery:condenser';
recipe = {
{'sorcery:accumulator'};
{'sorcery:conduit'};
};
}
sorcery.ley.mapnet = function(startpos,power)
-- this function returns a list of all the nodes accessible from
-- a ley network and their associated positions
local net = {}
power = power or 0
local devices = {
consume = {};
produce = {};
signal = {};
}
local numfound = 0
local maxconduct = 0
local minconduct
local foundp = function(p)
for k in pairs(net) do
if vector.equals(p,k) then return true end
end
return false
end
-- we're implementing this with a recursive function to start with
-- but this could rapidly lead to stack overflows so we should
-- replace it with a linear one at some point
local function find(positions)
local searchnext = {}
for _,pos in pairs(positions) do
for _,p in pairs {
{x = 0, z = 0, y = 0};
{x = -1, z = 0, y = 0};
{x = 1, z = 0, y = 0};
{x = 0, z = -1, y = 0};
{x = 0, z = 1, y = 0};
{x = 0, z = 0, y = -1};
{x = 0, z = 0, y = 1};
} do local sum = vector.add(pos,p)
if not foundp(sum) then
local nodename = minetest.get_node(sum).name
if minetest.get_item_group(nodename,'sorcery_ley_device') ~= 0
or sorcery.data.compat.ley[nodename] then
local d = sorcery.ley.sample(pos,1,nodename)
assert(d.mode == 'signal'
or d.mode == 'consume'
or d.mode == 'produce')
devices[d.mode][#(devices[d.mode]) + 1] = {
id = nodename; pos = sum;
}
if d.mode == 'signal' then
if d.power > power then
if minconduct then
if d.power < minconduct then
minconduct = d.power
end
else minconduct = d.power end
if d.power > maxconduct then
maxconduct = d.power
end
end
end
numfound = numfound + 1;
net[sum] = nodename;
searchnext[#searchnext + 1] = sum;
end
end
end
end
if #searchnext > 0 then find(searchnext) end
end
find{startpos}
if numfound > 0 then
return {
count = numfound;
map = net;
devices = devices;
conduct = {
min = minconduct;
max = maxconduct;
};
}
else return nil end
end
do local afftbl = {
[1] = 'praxic'; [2] = 'counterpraxic';
[3] = 'cognic'; [4] = 'syncretic';
[5] = 'mandatic'; [6] = 'occlutic';
[7] = 'imperic'; [8] = 'entropic';
}
local modetbl = {
[0] = 'none';
[1] = 'consume';
[2] = 'produce';
[3] = 'signal';
}
for i=1,#afftbl do afftbl [afftbl [i]] = i end
for i=1,#modetbl do modetbl[modetbl[i]] = i end
local m = sorcery.lib.marshal
local enc, dec = m.transcoder {
mode = m.t.u8;
power = m.t.u32; -- power generated/consumed * 10,000
affinity = m.g.array(m.t.u8); -- indexes into afftbl
}
sorcery.ley.encode = function(l)
local idxs = {}
for _,k in pairs(l.affinity) do
idxs[#idxs+1] = afftbl[k]
end
return meta_armor(enc {
mode = modetbl[l.mode];
power = l.power * 10000;
affinity = idxs;
}, true)
end
sorcery.ley.decode = function(str)
local obj = dec(meta_dearmor(str,true))
local affs = {}
for _,k in pairs(obj.affinity) do
affs[#affs+1] = afftbl[k]
end
return {
mode = modetbl[obj.mode];
power = obj.power / 10000.0;
affinity = affs;
}
end
end
sorcery.ley.setnode = function(pos,l)
local meta = minetest.get_node(pos)
meta:set_string('sorcery:ley',sorcery.ley.encode(l))
end
sorcery.ley.sample = function(pos,timespan,name)
-- returns how much ley-force can be transmitted by a
-- device over timespan
name = name or minetest.get_node(pos).name
local props = minetest.registered_nodes[name]._sorcery
local callback = props and props.on_leycalc or nil
local p,a,m
if callback then
local gen = callback(pos,timespan)
p = gen.power
a = gen.affinity
m = gen.mode
end
if not (p and a and m) then
local nm = minetest.get_meta(pos)
if nm:contains('sorcery:ley') then
local l = sorcery.ley.decode(nm:get_string('sorcery:ley'))
p = p or sorcery.ley.field_to_current(l.power,timespan)
a = a or l.affinity
m = m or l.mode
end
end
if (not (p and a and m)) and props and props.ley then
p = p or sorcery.ley.field_to_current(props.ley.power,timespan)
a = a or props.ley.affinity
m = m or props.ley.mode
end
if (not (p and a and m)) then
local compat = sorcery.data.compat.ley[name]
if compat then
p = p or sorcery.ley.field_to_current(compat.power,timespan)
a = a or compat.affinity
m = m or compat.mode
end
end
return {
power = p or 0;
mode = m or 'none';
affinity = a or {};
}
end
sorcery.ley.netcaps = function(pos,timespan,exclude)
local net = sorcery.ley.mapnet(pos)
local maxpower = 0
local freepower = 0
local affs,usedaffs = {},{}
for _,n in pairs(net.devices.produce) do
if not exclude or not vector.equals(n.pos,exclude) then
local ln = sorcery.ley.sample(n.pos,timespan,n.id)
maxpower = maxpower + ln.power
for _,a in pairs(ln.affinity) do
affs[a] = (affs[a] or 0) + 1
end
end
end
freepower = maxpower;
for _,n in pairs(net.devices.consume) do
if not exclude or not vector.equals(n.pos,exclude) then
local ln = sorcery.ley.sample(n.pos,timespan,n.id)
freepower = freepower - ln.power
for _,a in pairs(ln.affinity) do
usedaffs[a] = (usedaffs[a] or 0) + 1
end
end
end
return {
net = net;
freepower = freepower;
maxpower = maxpower;
affinity = affs;
affinity_balance = usedaffs;
}
end