Differences From
Artifact [112e3b2a50]:
71 71 output = 'sorcery:conduit 4';
72 72 recipe = {
73 73 {'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'};
74 74 {'default:copper_ingot', 'sorcery:electrumblock', 'default:copper_ingot'};
75 75 {'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'};
76 76 };
77 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',''};
78 +
79 +local makeswitch = function(switch, desc, tex, tiles, power)
80 + for _,active in pairs{true,false} do
81 + local turn = function(pos)
82 + local n = minetest.get_node(pos)
83 + minetest.sound_play('doors_steel_door_open', {
84 + gain = 0.7;
85 + pos = pos;
86 + }, true)
87 + local leymap = active and sorcery.ley.mapnet(pos) or nil
88 + minetest.swap_node(pos, {
89 + name = active and (switch .. '_off')
90 + or switch;
91 + param1 = n.param1;
92 + param2 = n.param2;
93 + })
94 + if active then
95 + -- if we're turning it off, use the old map,
96 + -- because post-swap the network will be
97 + -- broken and notify won't reach everyone
98 + leymap.map[leymap.startpos] = nil
99 + sorcery.ley.notifymap(leymap.map)
100 + else sorcery.ley.notify(pos) end
101 + end
102 + local tl = table.copy(tiles)
103 + tl[6] = tex .. '^sorcery_ley_switch_panel.png^sorcery_ley_switch_' .. (active and 'down' or 'up') .. '.png';
104 + minetest.register_node(switch .. (active and '' or '_off'), {
105 + description = desc;
106 + drop = switch;
107 + tiles = tl;
108 + paramtype2 = 'facedir';
109 + groups = {
110 + cracky = 2; choppy = 1;
111 + punch_operable = 1;
112 + sorcery_ley_device = active and 1 or 0;
113 + };
114 + _sorcery = {
115 + ley = active and {
116 + mode = 'signal'; power = power;
117 + } or nil;
118 + };
119 + on_punch = function(pos,node,puncher,point)
120 + if puncher ~= nil then
121 + if puncher:get_wielded_item():is_empty() then
122 + turn(pos)
123 + end
124 + end
125 + return minetest.node_punch(pos,node,puncher,point)
126 + end;
127 + on_rightclick = turn;
128 + })
129 + end
130 +end
131 +
132 +for _,b in pairs {
133 + {'Applewood', 'wood', 'default_wood.png'};
134 + {'Junglewood', 'junglewood', 'default_junglewood.png'};
135 + {'Pine', 'pine_wood', 'default_pine_wood.png'};
136 + {'Acacia', 'acacia_wood', 'default_pine_wood.png'};
137 + {'Aspen', 'aspen_wood', 'default_aspen_wood.png'};
138 + {'Stone', 'stone', 'default_stone.png'};
139 + {'Cobblestone', 'cobble', 'default_cobble.png'};
140 + {'Stone Brick', 'stonebrick', 'default_stone_brick.png'};
141 + {'Brick', 'brick', 'default_brick.png'};
142 +} do
143 + local id = 'sorcery:conduit_half_' .. b[2]
144 + local switch = 'sorcery:conduit_switch_' .. b[2]
145 + local item = (b[4] or 'default') .. ':' .. b[2]
146 + local tex = b[3]
147 + local mod = '^[lowpart:50:'
148 + local sidemod = '^[transformR270^[lowpart:50:'
149 + local unflip = '^[transformR90'
150 + local tiles = {
151 + 'sorcery_conduit_copper_top.png'..mod..tex; -- top
152 + tex..mod..'sorcery_conduit_copper_top.png';
153 + tex .. sidemod .. 'sorcery_conduit_copper_side.png' .. unflip; -- side
154 + 'sorcery_conduit_copper_side.png' .. sidemod .. tex .. unflip; -- side
155 + 'sorcery_conduit_copper_side.png'; -- back
156 + tex; -- front
157 + }
158 + minetest.register_node(id, {
159 + description = 'Half-' .. b[1] .. ' Conduit';
160 + paramtype2 = 'facedir';
161 + groups = {
162 + cracky = 2;
163 + choppy = 1;
164 + sorcery_ley_device = 1;
165 + sorcery_ley_conduit = 1;
166 + };
167 + _sorcery = {
168 + ley = { mode = 'signal'; power = 5; }
169 + };
170 + tiles = tiles;
171 + })
172 + minetest.register_craft {
173 + output = id .. ' 4';
174 + recipe = {
175 + {item, 'sorcery:conduit'};
176 + {item, 'sorcery:conduit'};
177 + };
178 + };
179 + makeswitch(switch, b[1] .. " Conduit Switch", tex, tiles, 5)
180 + minetest.register_craft {
181 + output = switch;
182 + recipe = {
183 + {'xdecor:lever_off',id};
184 + };
84 185 }
85 -};
186 +end
187 +makeswitch('sorcery:conduit_switch', "Conduit Switch", 'sorcery_conduit_copper_side.png', {
188 + 'sorcery_conduit_copper_top.png';
189 + 'sorcery_conduit_copper_top.png';
190 + 'sorcery_conduit_copper_side.png';
191 + 'sorcery_conduit_copper_side.png';
192 + 'sorcery_conduit_copper_side.png';
193 + 'sorcery_conduit_copper_side.png';
194 +}, 10)
195 +minetest.register_craft {
196 + output = 'sorcery:conduit_switch';
197 + recipe = {
198 + {'xdecor:lever_off','sorcery:conduit'};
199 + };
200 +}
201 +
202 +for name,metal in pairs(sorcery.data.metals) do
203 + if metal.conduct then
204 + local cable = 'sorcery:cable_' .. name
205 + minetest.register_node(cable, {
206 + description = sorcery.lib.str.capitalize(name) .. " Cable";
207 + drawtype = 'nodebox';
208 + groups = {
209 + sorcery_ley_device = 1; snappy = 3; attached = 1;
210 + sorcery_ley_cable = 1;
211 + };
212 + _sorcery = {
213 + ley = { mode = 'signal', power = metal.conduct };
214 + };
215 + sunlight_propagates = true;
216 + node_box = {
217 + type = 'connected';
218 + disconnected = { -0.05, -0.35, -0.40; 0.05, -0.25, 0.40 };
219 + connect_front = { -0.05, -0.35, -0.50; 0.05, -0.25, 0.05 };
220 + connect_back = { -0.05, -0.35, -0.05; 0.05, -0.25, 0.50 };
221 + connect_right = { -0.05, -0.35, -0.05; 0.50, -0.25, 0.05 };
222 + connect_left = { -0.50, -0.35, -0.05; 0.05, -0.25, 0.05 };
223 + connect_top = { -0.05, -0.25, -0.05; 0.05, 0.50, 0.05 };
224 + connect_bottom = { -0.05, -0.50, -0.05; 0.05, -0.35, 0.05 };
225 + };
226 + connects_to = { 'group:sorcery_ley_device', 'default:mese' };
227 + -- harcoding mese is kind of cheating -- figure out a
228 + -- better way to do this for the longterm
229 + paramtype = 'light';
230 + -- paramtype2 = 'facedir';
231 + after_place_node = function(pos, placer, stack, point)
232 + local vec = vector.subtract(point.under, pos)
233 + local n = minetest.get_node(pos)
234 + n.param2 = minetest.dir_to_facedir(vec)
235 + minetest.swap_node(pos,n)
236 + end;
237 + tiles = { 'sorcery_ley_plug.png' };
238 + })
239 +
240 + minetest.register_craft {
241 + output = cable .. ' 8';
242 + recipe = {
243 + {'basic_materials:copper_wire','basic_materials:copper_wire','basic_materials:copper_wire'};
244 + { metal.parts.fragment, metal.parts.fragment, metal.parts.fragment };
245 + {'basic_materials:copper_wire','basic_materials:copper_wire','basic_materials:copper_wire'};
246 + };
247 + replacements = {
248 + {'basic_materials:copper_wire', 'basic_materials:empty_spool'};
249 + {'basic_materials:copper_wire', 'basic_materials:empty_spool'};
250 + {'basic_materials:copper_wire', 'basic_materials:empty_spool'};
251 + {'basic_materials:copper_wire', 'basic_materials:empty_spool'};
252 + {'basic_materials:copper_wire', 'basic_materials:empty_spool'};
253 + {'basic_materials:copper_wire', 'basic_materials:empty_spool'};
254 + };
255 + };
256 + end
257 +end
258 +
259 +-- ley.notify will normally be called automatically, but if a
260 +-- ley-producer or consume has fluctuating levels of energy
261 +-- consumption, it should call this function when levels change
262 +sorcery.ley.notifymap = function(map)
263 + for pos,name in pairs(map) do
264 + local props = minetest.registered_nodes[name]._sorcery
265 + if props and props.on_leychange then
266 + props.on_leychange(pos)
267 + end
268 + end
269 +end
270 +sorcery.ley.notify = function(pos)
271 + local n = sorcery.ley.mapnet(pos)
272 + sorcery.ley.notifymap(n.map)
273 +end
86 274
87 275 sorcery.ley.field_to_current = function(strength,time)
88 276 local ley_factor = 0.25
89 277 -- a ley harvester will produce this much current with
90 278 -- access to a full-strength leyline
91 279
92 280 return strength * ley_factor * time;
................................................................................
102 290 -0.5, -0.5, -0.5;
103 291 0.5, 1.2, 0.5;
104 292 };
105 293 };
106 294 minetest.register_node('sorcery:condenser', {
107 295 description = 'Condenser';
108 296 drawtype = 'mesh';
297 + paramtype2 = 'facedir';
109 298 mesh = 'sorcery-condenser.obj';
110 299 selection_box = box;
111 300 collision_box = box;
112 301 tiles = {
113 302 amethyst:render();
114 303 'sorcery_condenser.png';
115 304 'default_tin_block.png';
................................................................................
122 311 sorcery_ley_device = 1;
123 312 };
124 313 on_construct = function(pos)
125 314 local meta = minetest.get_meta(pos)
126 315 meta:set_string('infotext','Condenser')
127 316 end;
128 317 _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;
318 + ley = { mode = 'produce';
319 + power = function(pos,time)
320 + return sorcery.ley.field_to_current(sorcery.ley.estimate(pos).force, time);
321 + end;
322 + affinity = function(pos)
323 + return sorcery.ley.estimate(pos).aff
324 + end;
325 + };
137 326 };
138 327 })
139 328 end
140 329
141 330 minetest.register_craft {
142 331 output = 'sorcery:condenser';
143 332 recipe = {
144 333 {'sorcery:accumulator'};
145 334 {'sorcery:conduit'};
146 335 };
147 336 }
337 +sorcery.ley.txofs = {
338 + {x = 0, z = 0, y = 0};
339 + {x = -1, z = 0, y = 0};
340 + {x = 1, z = 0, y = 0};
341 + {x = 0, z = -1, y = 0};
342 + {x = 0, z = 1, y = 0};
343 + {x = 0, z = 0, y = -1};
344 + {x = 0, z = 0, y = 1};
345 +}
148 346 sorcery.ley.mapnet = function(startpos,power)
149 347 -- this function returns a list of all the nodes accessible from
150 348 -- a ley network and their associated positions
151 - local net = {}
349 + local net,checked = {},{}
152 350 power = power or 0
153 351
154 352 local devices = {
155 353 consume = {};
156 354 produce = {};
157 355 signal = {};
158 356 }
159 357 local numfound = 0
160 358 local maxconduct = 0
161 359 local minconduct
360 + local startkey
162 361 local foundp = function(p)
163 - for k in pairs(net) do
362 + for _,k in pairs(checked) do
164 363 if vector.equals(p,k) then return true end
165 364 end
166 365 return false
167 366 end
168 367 -- we're implementing this with a recursive function to start with
169 368 -- but this could rapidly lead to stack overflows so we should
170 369 -- replace it with a linear one at some point
171 370 local function find(positions)
172 371 local searchnext = {}
173 372 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)
373 + for _,p in pairs(sorcery.ley.txofs) do
374 + local sum = vector.add(pos,p)
183 375 if not foundp(sum) then
376 + checked[#checked + 1] = sum
184 377 local nodename = minetest.get_node(sum).name
378 + if nodename == 'ignore' then
379 + minetest.load_area(sum)
380 + nodename = minetest.get_node(sum).name
381 + end
185 382 if minetest.get_item_group(nodename,'sorcery_ley_device') ~= 0
186 383 or sorcery.data.compat.ley[nodename] then
187 - local d = sorcery.ley.sample(pos,1,nodename)
384 + local d = sorcery.ley.sample(pos,1,nodename,{query={mode=true}})
188 385 assert(d.mode == 'signal'
189 386 or d.mode == 'consume'
190 387 or d.mode == 'produce')
191 388 devices[d.mode][#(devices[d.mode]) + 1] = {
192 389 id = nodename; pos = sum;
193 390 }
194 391 if d.mode == 'signal' then
392 + d.power = sorcery.ley.sample(pos,1,nodename,{query={power=true}}).power
195 393 if d.power > power then
196 394 if minconduct then
197 395 if d.power < minconduct then
198 396 minconduct = d.power
199 397 end
200 398 else minconduct = d.power end
201 399 if d.power > maxconduct then
202 400 maxconduct = d.power
203 401 end
204 402 end
205 403 end
206 404 numfound = numfound + 1;
207 405 net[sum] = nodename;
406 + if not startkey then
407 + if vector.equals(startpos,sum) then
408 + startkey = sum
409 + end
410 + end
208 411 searchnext[#searchnext + 1] = sum;
209 412 end
210 413 end
211 414 end
212 415 end
213 416 if #searchnext > 0 then find(searchnext) end
214 417 end
................................................................................
216 419 find{startpos}
217 420
218 421 if numfound > 0 then
219 422 return {
220 423 count = numfound;
221 424 map = net;
222 425 devices = devices;
426 + startpos = startkey;
223 427 conduct = {
224 428 min = minconduct;
225 429 max = maxconduct;
226 430 };
227 431 }
228 432 else return nil end
229 433 end
................................................................................
241 445 [3] = 'signal';
242 446 }
243 447 for i=1,#afftbl do afftbl [afftbl [i]] = i end
244 448 for i=1,#modetbl do modetbl[modetbl[i]] = i end
245 449 local m = sorcery.lib.marshal
246 450 local enc, dec = m.transcoder {
247 451 mode = m.t.u8;
248 - power = m.t.u32; -- power generated/consumed * 10,000
452 + minpower = m.t.u32; -- min power generated/consumed * 10,000
453 + maxpower = m.t.u32; -- max power generated/consumed * 10,000
249 454 affinity = m.g.array(m.t.u8); -- indexes into afftbl
250 455 }
251 456 sorcery.ley.encode = function(l)
252 457 local idxs = {}
253 458 for _,k in pairs(l.affinity) do
254 459 idxs[#idxs+1] = afftbl[k]
255 460 end
256 461 return meta_armor(enc {
257 462 mode = modetbl[l.mode];
258 - power = l.power * 10000;
463 + minpower = l.minpower * 10000;
464 + maxpower = l.maxpower * 10000;
259 465 affinity = idxs;
260 466 }, true)
261 467 end
262 468 sorcery.ley.decode = function(str)
263 469 local obj = dec(meta_dearmor(str,true))
264 470 local affs = {}
265 471 for _,k in pairs(obj.affinity) do
266 472 affs[#affs+1] = afftbl[k]
267 473 end
268 474 return {
269 475 mode = modetbl[obj.mode];
270 - power = obj.power / 10000.0;
476 + minpower = obj.minpower / 10000.0;
477 + maxpower = obj.maxpower / 10000.0;
478 + power = (obj.minpower == obj.maxpower) and obj.minpower or nil;
271 479 affinity = affs;
272 480 }
273 481 end
274 482 end
275 483 sorcery.ley.setnode = function(pos,l)
276 484 local meta = minetest.get_node(pos)
277 485 meta:set_string('sorcery:ley',sorcery.ley.encode(l))
278 486 end
279 487
280 -sorcery.ley.sample = function(pos,timespan,name)
488 +sorcery.ley.sample = function(pos,timespan,name,flags)
281 489 -- returns how much ley-force can be transmitted by a
282 490 -- device over timespan
491 + local ret = {}
283 492 name = name or minetest.get_node(pos).name
493 + flags = flags or {}
494 + flags.query = flags.query or {
495 + mode = true; power = true; affinity = true;
496 + minpower = true; maxpower = true;
497 + }
284 498 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
499 +
500 + local evaluate = function(v)
501 + if type(v) == 'function' then
502 + return v(pos)
503 + else return v end
292 504 end
293 505
294 - if not (p and a and m) then
506 + local leymeta do
295 507 local nm = minetest.get_meta(pos)
296 508 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
509 + leymeta = sorcery.ley.decode(nm:get_string('sorcery:ley'))
510 + end
511 + end
512 +
513 + local compat = sorcery.data.compat.ley[name]
514 +
515 + local lookup = function(k,default)
516 + if leymeta and leymeta[k] then return leymeta[k]
517 + elseif props and props.ley and props.ley[k] then return props.ley[k]
518 + elseif compat and compat[k] then return compat[k]
519 + else return default end
520 + end
521 + if flags.query.mode then ret.mode = evaluate(lookup('mode','none')) end
522 + if flags.query.affinity then ret.affinity = evaluate(lookup('affinity',{})) end
523 + if flags.query.minpower or flags.query.maxpower or flags.query.power then
524 + local condset = function(name,var)
525 + if flags.query[name] then ret[name] = var end
526 + end
527 + local p = lookup('power')
528 + if p then
529 + if type(p) == 'function' then
530 + -- we have a single function to calculate power usage; we need to
531 + -- check whether it returns min,max or a constant
532 + local min, max = p(pos,timespan)
533 + if (not max) or min == max then
534 + ret.power = min
535 + condset('power',min)
536 + condset('minpower',min)
537 + condset('maxpower',min)
538 + else
539 + condset('minpower',min)
540 + condset('maxpower',max)
541 + end
542 + else -- power usage is simply a constant
543 + condset('power',p)
544 + condset('minpower',p)
545 + condset('maxpower',p)
546 + end
547 + else
548 + local feval = function(v)
549 + if type(v) == 'function' then
550 + return v(pos,timespan)
551 + else return v * timespan end
552 + end
553 + local min = feval(lookup('minpower'))
554 + local max = feval(lookup('maxpower'))
555 + condset('minpower',min)
556 + condset('maxpower',max)
557 + if min == max then condset('power',min) end
301 558 end
302 559 end
303 560
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
561 + if ret.power then
562 + if flags.query.minpower and not ret.minpower then ret.minpower = power end
563 + if flags.query.maxpower and not ret.maxpower then ret.maxpower = power end
308 564 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 - }
565 + return ret
324 566 end
325 567
326 568 sorcery.ley.netcaps = function(pos,timespan,exclude)
327 569 local net = sorcery.ley.mapnet(pos)
328 570 local maxpower = 0
329 571 local freepower = 0
330 572 local affs,usedaffs = {},{}
573 + local flexpowerdevs = {}
574 + local devself
331 575 for _,n in pairs(net.devices.produce) do
576 + if vector.equals(pos,n.pos) then devself = n end
332 577 if not exclude or not vector.equals(n.pos,exclude) then
333 578 local ln = sorcery.ley.sample(n.pos,timespan,n.id)
579 + n.powersupply = ln.power
580 + n.affinity = ln.affinity
334 581 maxpower = maxpower + ln.power
582 + -- production power does not vary, tho at some point it
583 + -- might be useful to enable some kind of power scaling
335 584 for _,a in pairs(ln.affinity) do
336 585 affs[a] = (affs[a] or 0) + 1
337 586 end
338 587 end
339 588 end
340 589 freepower = maxpower;
341 590 for _,n in pairs(net.devices.consume) do
591 + if vector.equals(pos,n.pos) then devself = n end
342 592 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
593 + local ln = sorcery.ley.sample(n.pos,timespan,n.id, {
594 + query = { power = true; minpower = true; maxpower = true; affinity = true; };
595 + })
596 + n.powerdraw = (ln.minpower <= freepower) and ln.minpower or 0
597 + freepower = freepower - n.powerdraw
598 + -- merge in sample data and return it along with the map
599 + n.minpower = ln.minpower
600 + n.maxpower = ln.maxpower
601 + n.affinity = ln.affinity
602 + if ln.maxpower > ln.minpower then
603 + flexpowerdevs[#flexpowerdevs+1] = n
604 + end
345 605 for _,a in pairs(ln.affinity) do
346 606 usedaffs[a] = (usedaffs[a] or 0) + 1
347 607 end
348 608 end
349 609 end
610 +
611 + -- now we know the following: all devices; if possible, have been
612 + -- given the minimum amount of power they need to run. if freepower
613 + -- < 0 then the network is overloaded and inoperable. if freepower>0,
614 + -- we now need to distribute the remaining power to devices that
615 + -- have a variable power consumption. there's no clean way of doing
616 + -- this, so we use the following algorithm:
617 + -- 1. take a list of devices that want more power
618 + -- 2. divide the amount of free power by the number of such devices
619 + -- to derive the maximum power that can be allocated to any device
620 + -- 3. iterate through the devices. increase their power consumption by
621 + -- the maximum term. any device that is satiated can be removed from
622 + -- the list.
623 + -- 4. if there is still power remaining, repeat until there is not.
624 +
625 + while freepower > 0 and #flexpowerdevs > 0 do
626 + local nextiter = {}
627 + local maxgive = freepower / #flexpowerdevs
628 + for _,d in pairs(flexpowerdevs) do
629 + local give = math.min(maxgive,d.maxpower - d.powerdraw)
630 + freepower = freepower - give
631 + d.powerdraw = d.powerdraw + give
632 + if d.powerdraw < d.maxpower then
633 + nextiter[#nextiter+1] = d
634 + end
635 + end
636 + flexpowerdevs = nextiter
637 + end
350 638
351 639 return {
352 640 net = net;
353 641 freepower = freepower;
354 642 maxpower = maxpower;
355 643 affinity = affs;
356 644 affinity_balance = usedaffs;
645 + self = devself;
357 646 }
358 647 end
648 +
649 +minetest.register_on_placenode(function(pos, node)
650 + if minetest.get_item_group(node.name, 'sorcery_ley_device') ~= 0 then
651 + sorcery.ley.notify(pos)
652 + end
653 +end)
654 +
655 +local constants = {
656 + generator_max_energy_output = 5;
657 + -- how much energy a generator makes after
658 +
659 + generator_time_to_max_energy = 150;
660 + -- seconds of activity
661 +
662 + generator_power_drain_speed = 0.1;
663 + -- points of energy output drained per second of no fuel
664 +}
665 +local update_generator = function(pos)
666 + minetest.get_node_timer(pos):start(1)
667 +end
668 +local generator_update_formspec = function(pos)
669 + local meta = minetest.get_meta(pos)
670 + local burnprog = math.min(1,meta:get_float('burnleft') / meta:get_float('burntime'))
671 + local power = meta:get_float('power')
672 + local inv = meta:get_inventory()
673 + local lamps = ''
674 + for i=0,4 do
675 + local color
676 + if power - i >= 1 then
677 + color = 'red'
678 + elseif power - i > 0 then
679 + color = 'yellow'
680 + else
681 + color = 'off'
682 + end
683 + lamps = lamps .. string.format([[
684 + image[%f,0.5;1,1;sorcery_statlamp_%s.png]
685 + ]], 2.5 + i, color)
686 + end
687 + meta:set_string('formspec', string.format([[
688 + size[8,5.8]
689 + list[context;fuel;0.5,0.5;1,1]
690 + list[current_player;main;0,2;8,4]
691 + image[1.5,0.5;1,1;default_furnace_fire_bg.png^[lowpart:%u%%:default_furnace_fire_fg.png]
692 + ]], math.floor(burnprog * 100)) .. lamps)
693 +end
694 +for _,active in pairs{true,false} do
695 + local id = 'sorcery:generator' .. (active and '_active' or '')
696 + minetest.register_node(id, {
697 + description = 'Generator';
698 + paramtype2 = 'facedir';
699 + groups = { cracky = 2; sorcery_ley_device = 1; };
700 + drop = 'sorcery:generator';
701 + tiles = {
702 + 'sorcery_ley_generator_top.png';
703 + 'sorcery_ley_generator_bottom.png';
704 + 'sorcery_ley_generator_side.png';
705 + 'sorcery_ley_generator_side.png';
706 + 'sorcery_ley_generator_back.png';
707 + 'sorcery_ley_generator_front_' .. (active and 'on' or 'off') .. '.png';
708 + };
709 + on_construct = function(pos)
710 + local meta = minetest.get_meta(pos)
711 + local inv = meta:get_inventory()
712 + meta:set_string('infotext','Generator')
713 + meta:set_float('burntime',0)
714 + meta:set_float('burnleft',0)
715 + meta:set_float('power',0)
716 + generator_update_formspec(pos)
717 + inv:set_size('fuel',1)
718 + end;
719 + after_dig_node = sorcery.lib.node.purge_container;
720 + on_metadata_inventory_put = update_generator;
721 + on_metadata_inventory_take = update_generator;
722 + on_timer = function(pos,delta)
723 + local meta = minetest.get_meta(pos)
724 + local inv = meta:get_inventory()
725 + local self = minetest.get_node(pos)
726 + local timeleft = meta:get_float('burnleft') - delta
727 + local again = false
728 + local power = meta:get_float('power')
729 + local burning = active
730 + if timeleft < 0 then timeleft = 0 end
731 + if not active or timeleft == 0 then
732 + if inv:is_empty('fuel') then
733 + -- no fuel, can't start/keep going. drain power if
734 + -- necessary, otherwise bail
735 + burning = false
736 + if power > 0 then
737 + power = math.max(0, power - constants.generator_power_drain_speed)
738 + again = true
739 + end
740 + else
741 + -- fuel is present, let's burn it
742 + local res,decin = minetest.get_craft_result {
743 + method = 'fuel';
744 + items = {inv:get_stack('fuel',1)};
745 + }
746 + meta:set_float('burntime',res.time)
747 + timeleft = res.time
748 + inv:set_stack('fuel',1,decin.items[1])
749 + again = true
750 + burning = true
751 + end
752 + else
753 + local eps = constants.generator_max_energy_output / constants.generator_time_to_max_energy
754 + power = math.min(constants.generator_max_energy_output, power + eps*delta)
755 + again = true
756 + end
757 + ::stop:: meta:set_float('power',power)
758 + meta:set_float('burnleft',timeleft)
759 + generator_update_formspec(pos)
760 + if burning and not active then
761 + minetest.swap_node(pos, {
762 + name = 'sorcery:generator_active';
763 + param1 = self.param1, param2 = self.param2;
764 + })
765 + elseif active and not burning then
766 + minetest.swap_node(pos, {
767 + name = 'sorcery:generator';
768 + param1 = self.param1, param2 = self.param2;
769 + })
770 + end
771 + return again
772 + end;
773 + allow_metadata_inventory_put = function(pos,listname,index,stack,user)
774 + local res = minetest.get_craft_result {
775 + method = 'fuel';
776 + items = {stack};
777 + }
778 + if res.time ~= 0 then return stack:get_count()
779 + else return 0 end
780 + end;
781 + _sorcery = {
782 + ley = {
783 + mode = 'produce', affinity = {'praxic'};
784 + power = function(pos,delta)
785 + local meta = minetest.get_meta(pos)
786 + return meta:get_float('power') * delta;
787 + end;
788 + };
789 + };
790 + })
791 +end