Differences From
Artifact [aa52733fca]:
1 1 -- contains functions for determining information about
2 2 -- the nearest leyline and its currents
3 3
4 4 sorcery.ley = {}
5 5
6 --- 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, telestratic-affine leylines will charge wands enchanted with telestratic spells more quickly than leylines lacking this affinity.
6 +-- 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.
7 +-- 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)
8 +-- 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.
7 9
8 10 sorcery.ley.estimate = function(pos)
9 11 local affs = {
10 12 'praxic'; 'counterpraxic'; 'cognic';
11 13 'mandatic'; 'occlutic'; 'imperic';
12 14 'syncretic'; 'entropic';
13 15 };
................................................................................
42 44 privs = { server = true };
43 45 func = function(caller,params)
44 46 local pos = minetest.get_player_by_name(caller):get_pos()
45 47 local ley = sorcery.ley.estimate(pos)
46 48 minetest.chat_send_player(caller, 'Leyline force ' .. tostring(ley.force) .. ' with affinities ' .. table.concat(ley.aff, ','))
47 49 end;
48 50 })
51 +
52 +-- 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.
53 +-- 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.
54 +
55 +minetest.register_node('sorcery:conduit', {
56 + description = 'Conduit';
57 + tiles = {
58 + 'sorcery_conduit_copper_top.png';
59 + 'sorcery_conduit_copper_top.png';
60 + 'sorcery_conduit_copper_side.png';
61 + };
62 + groups = {
63 + sorcery_ley_device = 1;
64 + cracky = 3;
65 + };
66 + _sorcery = {
67 + ley = { mode = 'signal'; power = 10 };
68 + };
69 +})
70 +minetest.register_craft {
71 + output = 'sorcery:conduit 4';
72 + recipe = {
73 + {'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'};
74 + {'default:copper_ingot', 'sorcery:electrumblock', 'default:copper_ingot'};
75 + {'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'};
76 + };
77 +};
78 +minetest.register_craft {
79 + output = 'sorcery:wire 4';
80 + recipe = {
81 + {'', 'basic_materials:copper_wire',''};
82 + {'', 'sorcery:fragment_electrum', ''};
83 + {'', 'basic_materials:copper_wire',''};
84 + }
85 +};
86 +
87 +sorcery.ley.field_to_current = function(strength,time)
88 + local ley_factor = 0.25
89 + -- a ley harvester will produce this much current with
90 + -- access to a full-strength leyline
91 +
92 + return strength * ley_factor * time;
93 +end
94 +
95 +do -- register condenser
96 + local gem = sorcery.lib.image('default_diamond_block.png')
97 + local amethyst = gem:multiply(sorcery.lib.color(sorcery.data.gems.amethyst.tone))
98 + local emerald = gem:multiply(sorcery.lib.color(sorcery.data.gems.emerald.tone))
99 + local box = {
100 + type = 'fixed';
101 + fixed = {
102 + -0.5, -0.5, -0.5;
103 + 0.5, 1.2, 0.5;
104 + };
105 + };
106 + minetest.register_node('sorcery:condenser', {
107 + description = 'Condenser';
108 + drawtype = 'mesh';
109 + mesh = 'sorcery-condenser.obj';
110 + selection_box = box;
111 + collision_box = box;
112 + tiles = {
113 + amethyst:render();
114 + 'sorcery_condenser.png';
115 + 'default_tin_block.png';
116 + 'default_stone.png';
117 + 'default_copper_block.png';
118 + emerald:render();
119 + };
120 + groups = {
121 + cracky = 2;
122 + sorcery_ley_device = 1;
123 + };
124 + on_construct = function(pos)
125 + local meta = minetest.get_meta(pos)
126 + meta:set_string('infotext','Condenser')
127 + end;
128 + _sorcery = {
129 + ley = { mode = 'produce' };
130 + on_leycalc = function(pos,time)
131 + local l = sorcery.ley.estimate(pos)
132 + return {
133 + power = sorcery.ley.field_to_current(l.force, time);
134 + affinity = l.aff;
135 + }
136 + end;
137 + };
138 + })
139 +end
140 +
141 +minetest.register_craft {
142 + output = 'sorcery:condenser';
143 + recipe = {
144 + {'sorcery:accumulator'};
145 + {'sorcery:conduit'};
146 + };
147 +}
148 +sorcery.ley.mapnet = function(startpos,power)
149 + -- this function returns a list of all the nodes accessible from
150 + -- a ley network and their associated positions
151 + local net = {}
152 + power = power or 0
153 +
154 + local devices = {
155 + consume = {};
156 + produce = {};
157 + signal = {};
158 + }
159 + local numfound = 0
160 + local maxconduct = 0
161 + local minconduct
162 + local foundp = function(p)
163 + for k in pairs(net) do
164 + if vector.equals(p,k) then return true end
165 + end
166 + return false
167 + end
168 + -- we're implementing this with a recursive function to start with
169 + -- but this could rapidly lead to stack overflows so we should
170 + -- replace it with a linear one at some point
171 + local function find(positions)
172 + local searchnext = {}
173 + for _,pos in pairs(positions) do
174 + for _,p in pairs {
175 + {x = 0, z = 0, y = 0};
176 + {x = -1, z = 0, y = 0};
177 + {x = 1, z = 0, y = 0};
178 + {x = 0, z = -1, y = 0};
179 + {x = 0, z = 1, y = 0};
180 + {x = 0, z = 0, y = -1};
181 + {x = 0, z = 0, y = 1};
182 + } do local sum = vector.add(pos,p)
183 + if not foundp(sum) then
184 + local nodename = minetest.get_node(sum).name
185 + if minetest.get_item_group(nodename,'sorcery_ley_device') ~= 0
186 + or sorcery.data.compat.ley[nodename] then
187 + local d = sorcery.ley.sample(pos,1,nodename)
188 + assert(d.mode == 'signal'
189 + or d.mode == 'consume'
190 + or d.mode == 'produce')
191 + devices[d.mode][#(devices[d.mode]) + 1] = {
192 + id = nodename; pos = sum;
193 + }
194 + if d.mode == 'signal' then
195 + if d.power > power then
196 + if minconduct then
197 + if d.power < minconduct then
198 + minconduct = d.power
199 + end
200 + else minconduct = d.power end
201 + if d.power > maxconduct then
202 + maxconduct = d.power
203 + end
204 + end
205 + end
206 + numfound = numfound + 1;
207 + net[sum] = nodename;
208 + searchnext[#searchnext + 1] = sum;
209 + end
210 + end
211 + end
212 + end
213 + if #searchnext > 0 then find(searchnext) end
214 + end
215 +
216 + find{startpos}
217 +
218 + if numfound > 0 then
219 + return {
220 + count = numfound;
221 + map = net;
222 + devices = devices;
223 + conduct = {
224 + min = minconduct;
225 + max = maxconduct;
226 + };
227 + }
228 + else return nil end
229 +end
230 +
231 +do local afftbl = {
232 + [1] = 'praxic'; [2] = 'counterpraxic';
233 + [3] = 'cognic'; [4] = 'syncretic';
234 + [5] = 'mandatic'; [6] = 'occlutic';
235 + [7] = 'imperic'; [8] = 'entropic';
236 + }
237 + local modetbl = {
238 + [0] = 'none';
239 + [1] = 'consume';
240 + [2] = 'produce';
241 + [3] = 'signal';
242 + }
243 + for i=1,#afftbl do afftbl [afftbl [i]] = i end
244 + for i=1,#modetbl do modetbl[modetbl[i]] = i end
245 + local m = sorcery.lib.marshal
246 + local enc, dec = m.transcoder {
247 + mode = m.t.u8;
248 + power = m.t.u32; -- power generated/consumed * 10,000
249 + affinity = m.g.array(m.t.u8); -- indexes into afftbl
250 + }
251 + sorcery.ley.encode = function(l)
252 + local idxs = {}
253 + for _,k in pairs(l.affinity) do
254 + idxs[#idxs+1] = afftbl[k]
255 + end
256 + return meta_armor(enc {
257 + mode = modetbl[l.mode];
258 + power = l.power * 10000;
259 + affinity = idxs;
260 + }, true)
261 + end
262 + sorcery.ley.decode = function(str)
263 + local obj = dec(meta_dearmor(str,true))
264 + local affs = {}
265 + for _,k in pairs(obj.affinity) do
266 + affs[#affs+1] = afftbl[k]
267 + end
268 + return {
269 + mode = modetbl[obj.mode];
270 + power = obj.power / 10000.0;
271 + affinity = affs;
272 + }
273 + end
274 +end
275 +sorcery.ley.setnode = function(pos,l)
276 + local meta = minetest.get_node(pos)
277 + meta:set_string('sorcery:ley',sorcery.ley.encode(l))
278 +end
279 +
280 +sorcery.ley.sample = function(pos,timespan,name)
281 + -- returns how much ley-force can be transmitted by a
282 + -- device over timespan
283 + name = name or minetest.get_node(pos).name
284 + local props = minetest.registered_nodes[name]._sorcery
285 + local callback = props and props.on_leycalc or nil
286 + local p,a,m
287 + if callback then
288 + local gen = callback(pos,timespan)
289 + p = gen.power
290 + a = gen.affinity
291 + m = gen.mode
292 + end
293 +
294 + if not (p and a and m) then
295 + local nm = minetest.get_meta(pos)
296 + if nm:contains('sorcery:ley') then
297 + local l = sorcery.ley.decode(nm:get_string('sorcery:ley'))
298 + p = p or sorcery.ley.field_to_current(l.power,timespan)
299 + a = a or l.affinity
300 + m = m or l.mode
301 + end
302 + end
303 +
304 + if (not (p and a and m)) and props and props.ley then
305 + p = p or sorcery.ley.field_to_current(props.ley.power,timespan)
306 + a = a or props.ley.affinity
307 + m = m or props.ley.mode
308 + end
309 +
310 + if (not (p and a and m)) then
311 + local compat = sorcery.data.compat.ley[name]
312 + if compat then
313 + p = p or sorcery.ley.field_to_current(compat.power,timespan)
314 + a = a or compat.affinity
315 + m = m or compat.mode
316 + end
317 + end
318 +
319 + return {
320 + power = p or 0;
321 + mode = m or 'none';
322 + affinity = a or {};
323 + }
324 +end
325 +
326 +sorcery.ley.netcaps = function(pos,timespan,exclude)
327 + local net = sorcery.ley.mapnet(pos)
328 + local maxpower = 0
329 + local freepower = 0
330 + local affs,usedaffs = {},{}
331 + for _,n in pairs(net.devices.produce) do
332 + if not exclude or not vector.equals(n.pos,exclude) then
333 + local ln = sorcery.ley.sample(n.pos,timespan,n.id)
334 + maxpower = maxpower + ln.power
335 + for _,a in pairs(ln.affinity) do
336 + affs[a] = (affs[a] or 0) + 1
337 + end
338 + end
339 + end
340 + freepower = maxpower;
341 + for _,n in pairs(net.devices.consume) do
342 + if not exclude or not vector.equals(n.pos,exclude) then
343 + local ln = sorcery.ley.sample(n.pos,timespan,n.id)
344 + freepower = freepower - ln.power
345 + for _,a in pairs(ln.affinity) do
346 + usedaffs[a] = (usedaffs[a] or 0) + 1
347 + end
348 + end
349 + end
350 +
351 + return {
352 + net = net;
353 + freepower = freepower;
354 + maxpower = maxpower;
355 + affinity = affs;
356 + affinity_balance = usedaffs;
357 + }
358 +end