sorcery  Check-in [f7f6898cbd]

Overview
Comment:add force field emitters, generators, rework leyline distribution algorithm, add utility function for automatically dumping out inventories when an item is dug, add draconium and tyrannium alloys (tentatively), various fixes
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: f7f6898cbda00c7271bbe38e53f5e936538e041b399e1dfa82a17c35fbeda42d
User & Date: lexi on 2020-08-21 11:52:17
Other Links: manifest | tags
Context
2020-08-28
14:08
add recipes, cookbooks, disassembly (to create recipes from items), attunement, farcasters, and portals; various edits for bug fixes and improvements check-in: 9ef6cbcf31 user: lexi tags: trunk
2020-08-21
11:52
add force field emitters, generators, rework leyline distribution algorithm, add utility function for automatically dumping out inventories when an item is dug, add draconium and tyrannium alloys (tentatively), various fixes check-in: f7f6898cbd user: lexi tags: trunk
2020-08-17
13:35
fix bugs, finish grinder, add conduits and condensers to extract and transmit energy from leylines check-in: 9278734b41 user: lexi tags: trunk
Changes

Modified coins.lua from [e5af87fe4c] to [f5d6bba856].

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
	end
	inv:set_stack('output',1,ItemStack(nil))
	do return end

	::foundmetal:: if not inv:is_empty('gem') then
		local id = slot_gem:get_name()
		for name,gem in pairs(sorcery.data.gems) do
			print('scanning gem',name,dump(gem))
			if gem.foreign then
				if id == gem.foreign then gemname = name 
					else goto skip end
			else
				if id == 'sorcery:gem_' .. name then gemname = name
					else goto skip end
			end
			coincount = math.min(coincount, slot_gem:get_count())
			do break end
		::skip::end
	end
	
	coincount = coincount * coins_per_ingot
	print('names',coinname,gemname)

	local coinname = 'sorcery:coin_' .. metalname ..
		((gemname and '_' .. gemname) or '')

	inv:set_stack('output',1,ItemStack {
		name = coinname;
		count = coincount;







<













<







49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68

69
70
71
72
73
74
75
	end
	inv:set_stack('output',1,ItemStack(nil))
	do return end

	::foundmetal:: if not inv:is_empty('gem') then
		local id = slot_gem:get_name()
		for name,gem in pairs(sorcery.data.gems) do

			if gem.foreign then
				if id == gem.foreign then gemname = name 
					else goto skip end
			else
				if id == 'sorcery:gem_' .. name then gemname = name
					else goto skip end
			end
			coincount = math.min(coincount, slot_gem:get_count())
			do break end
		::skip::end
	end
	
	coincount = coincount * coins_per_ingot


	local coinname = 'sorcery:coin_' .. metalname ..
		((gemname and '_' .. gemname) or '')

	inv:set_stack('output',1,ItemStack {
		name = coinname;
		count = coincount;

Modified data/enchants.lua from [9a3424a733] to [fc080855fd].

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
			{lens = 'concave',   gem = 'mese',     dmg = 1};
			{lens = 'concave',   gem = 'sapphire', dmg = 1};
		};
		desc = 'some damage is repaired when used to mine ore or kill an attacker';
		on_dig = function(ctx)
			local orepfx = "stone_with_" -- }:<
			-- local oredrop = ' lump'
			local dug = minetest.get_node(ctx.target.under)
			local barename = string.sub(dug.name, string.find(dug.name, ':') + 1)
			print('is ore? ',dug.name,barename)
			if minetest.get_item_group(dug.name, ore) ~= 0 or
			   string.sub(barename,1,string.len(orepfx)) == orepfx 
			then
				print('is ore!')
				ctx.tool:add_wear(-(sorcery.enchant.strength(ctx.tool,'harvest') * 2000))
				ctx.cost = 3
			end
		end;
	};
	conserve = { -- use less magical energy
		name = 'Conserve';







<
|
<
|


<







58
59
60
61
62
63
64

65

66
67
68

69
70
71
72
73
74
75
			{lens = 'concave',   gem = 'mese',     dmg = 1};
			{lens = 'concave',   gem = 'sapphire', dmg = 1};
		};
		desc = 'some damage is repaired when used to mine ore or kill an attacker';
		on_dig = function(ctx)
			local orepfx = "stone_with_" -- }:<
			-- local oredrop = ' lump'

			local barename = string.sub(ctx.node.name, string.find(ctx.node.name, ':') + 1)

			if minetest.get_item_group(ctx.node.name, 'ore') ~= 0 or
			   string.sub(barename,1,string.len(orepfx)) == orepfx 
			then

				ctx.tool:add_wear(-(sorcery.enchant.strength(ctx.tool,'harvest') * 2000))
				ctx.cost = 3
			end
		end;
	};
	conserve = { -- use less magical energy
		name = 'Conserve';

Modified data/gems.lua from [6638dda90c] to [c84e6c472a].

19
20
21
22
23
24
25


26
27
28
29
30
31
32
..
33
34
35
36
37
38
39

40
41
42
43
44
45
46
return {
	diamond = {
		foreign = 'default:diamond';
		tone = {137,240,255};
		items = default_items('diamond');
		tools = true, armor = true;
		maxenergy = 2000;


		slots = {
			{affinity = {'praxic','counterpraxic'}, confluence = 1};
			{affinity = {'praxic','syncretic'}, confluence = 0.6};
			{affinity = {'counterpraxic', 'entropic'}, confluence = 0.7};
		};
	};
	mese = {
................................................................................
		foreign = 'default:mese_crystal';
		foreign_shard = 'default:mese_crystal_fragment';
		tone = {255,253,94};
		energysource = 5;
		maxenergy = 600;
		items = default_items('mese');
		tools = true, armor = true;

		slots = {
			{affinity = {'praxic'}, confluence = 1};
			{affinity = {'praxic'}, confluence = 0.5};
			{affinity = {'cognic'}, confluence = 1};
			{affinity = {'syncretic'}, confluence = 0.9};
		};
	};







>
>







 







>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
return {
	diamond = {
		foreign = 'default:diamond';
		tone = {137,240,255};
		items = default_items('diamond');
		tools = true, armor = true;
		maxenergy = 2000;
		hardness = 8;
		randomfind = false;
		slots = {
			{affinity = {'praxic','counterpraxic'}, confluence = 1};
			{affinity = {'praxic','syncretic'}, confluence = 0.6};
			{affinity = {'counterpraxic', 'entropic'}, confluence = 0.7};
		};
	};
	mese = {
................................................................................
		foreign = 'default:mese_crystal';
		foreign_shard = 'default:mese_crystal_fragment';
		tone = {255,253,94};
		energysource = 5;
		maxenergy = 600;
		items = default_items('mese');
		tools = true, armor = true;
		randomfind = false;
		slots = {
			{affinity = {'praxic'}, confluence = 1};
			{affinity = {'praxic'}, confluence = 0.5};
			{affinity = {'cognic'}, confluence = 1};
			{affinity = {'syncretic'}, confluence = 0.9};
		};
	};

Modified data/greases.lua from [018bd8935a] to [0a57245423].

59
60
61
62
63
64
65
66







67


		color = {222,57,239};
		core = { 'sorcery:oil_mystic', 'sorcery:oil_dawn', 'sorcery:oil_mushroom' };
		mix = {
			'sorcery:powder_cobalt';
			'sorcery:powder_cobalt';
			'sorcery:extract_grape';
		};
	};







}










>
>
>
>
>
>
>
|
>
>
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
		color = {222,57,239};
		core = { 'sorcery:oil_mystic', 'sorcery:oil_dawn', 'sorcery:oil_mushroom' };
		mix = {
			'sorcery:powder_cobalt';
			'sorcery:powder_cobalt';
			'sorcery:extract_grape';
		};
	};
	neutralizing = {
		color = {221,148,95};
		core = { 'sorcery:oil_dawn', 'sorcery:oil_berry' };
		mix = {
			'sorcery:powder_aluminum';
			'sorcery:powder_lithium';
			'sorcery:extract_pine';
		};
	};
}

Modified data/metals.lua from [f9ae7215e3] to [a6eaeb7ad1].

27
28
29
30
31
32
33

34
35
36
37
38
39
40
...
136
137
138
139
140
141
142

143
144
145
146
147
148
149
...
172
173
174
175
176
177
178

179
180
181
182
183
184
185
...
197
198
199
200
201
202
203

204
205
206
207
208
209
210
...
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278







279
280
281
282
283
284
285
286
287
288
289
290
291

292
293
294
295
296
297
298
...
299
300
301
302
303
304
305


306




















307







308

	copper = {
		dye = 'orange';
		ingot = 'default:copper_ingot';
		block = 'default:copperblock';
		tone = {255,176,61};
		no_tools = true; no_armor = true;
		hardness = 2;

	};
	brass = {
		tone = {255,226,87};
		dye = 'bright_orange';
		artificial = true;
		no_tools = true; no_armor = true;
		hardness = 3;
................................................................................
		no_armor = true; no_tools = true;
		power = 1; cooktime = 8; hardness = 1;
	};
	electrum = {
		tone = {212, 255, 0}, alpha = 80;
		artificial = true;
		hardness = 1;

		dye = 'bright_green';
		mix = {
			metals = {
				silver = 1;
				gold = 1;
			};
		};
................................................................................
		rarity = 17;
		durabilty = 900;
		hardness = 6;
		power = 3;
		speed = 3.5;
		cooktime = 30;
		maxenergy = 3500;

		slots = {
			{
				affinity = {'counterpraxic'};
				confluence = 0.65;
				interference = {speed = 1};
			};
		}
................................................................................
		tone = {209,88,241}, alpha = 80;
		dye = 'purple';
		rarity = 18;
		meltpoint = 3;
		cooktime = 340;
		hardness = 7;
		maxenergy = 1800;

		durability = 1900;
		speed = 3.2;
		img = {
			-- ingot = 'sorcery_iridium_ingot.png';
			-- block = 'sorcery_iridium_block.png';
		};
		slots = {
................................................................................
	};
	impervium = {
		tone = {226,255,107}, alpha = 90;
		cooktime = 260;
		meltpoint = 5;
		artificial = true;
		speed = 2.1;
		hardness = 20;
		durability = 5300;
		maxenergy = 2300;
		watercool = true;
		mix = {
			metals = {
				duranium = 4;
				iridium = 2;
................................................................................
	};
	eternium = {
		tone = {156,82,222}, alpha = 100;
		cooktime = 500;
		meltpoint = 6;
		artificial = true;
		speed = 2;
		hardness = 9;
		maxenergy = 1200;
		durability = 8100;
		watercool = true;
		mix = {
			metals = {
				iridium = 2;
				tungsten = 2;
				lithium = 1;
			};
		};







		slots = {
			{affinity={'praxic','entropic'}, confluence = 1.4};
			{affinity={'praxic','syncretic'}, confluence = 1.1};
		}
	};
	unobtanium = {
		tone = {114,255,214}, alpha = 120;
		meltpoint = 3;
		cooktime = 330;
		artificial = true;
		maxenergy = 4000;
		hardness = 7;
		durability = 3300;

		speed = 3.4;
		slots = {
			{affinity={'praxic'}, confluence = 0.7};
			{affinity={'counterpraxic'}, confluence = 1.2};
			{affinity={'cognic'}, confluence = 1.1};
		};
		mix = {
................................................................................
			metals = {
				impervium = 8;
				eternium = 4;
				cobalt = 1;
			};
		};
	};


	-- draconium = {artificial=true;};




















	-- tyrannium = {artificial=true;};







};








>







 







>







 







>







 







>







 







|







 







|










>
>
>
>
>
>
>













>







 







>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
|
>
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
	copper = {
		dye = 'orange';
		ingot = 'default:copper_ingot';
		block = 'default:copperblock';
		tone = {255,176,61};
		no_tools = true; no_armor = true;
		hardness = 2;
		conduct = 0.1;
	};
	brass = {
		tone = {255,226,87};
		dye = 'bright_orange';
		artificial = true;
		no_tools = true; no_armor = true;
		hardness = 3;
................................................................................
		no_armor = true; no_tools = true;
		power = 1; cooktime = 8; hardness = 1;
	};
	electrum = {
		tone = {212, 255, 0}, alpha = 80;
		artificial = true;
		hardness = 1;
		conduct = 0.5;
		dye = 'bright_green';
		mix = {
			metals = {
				silver = 1;
				gold = 1;
			};
		};
................................................................................
		rarity = 17;
		durabilty = 900;
		hardness = 6;
		power = 3;
		speed = 3.5;
		cooktime = 30;
		maxenergy = 3500;
		conduct = 2.0;
		slots = {
			{
				affinity = {'counterpraxic'};
				confluence = 0.65;
				interference = {speed = 1};
			};
		}
................................................................................
		tone = {209,88,241}, alpha = 80;
		dye = 'purple';
		rarity = 18;
		meltpoint = 3;
		cooktime = 340;
		hardness = 7;
		maxenergy = 1800;
		conduct = 5;
		durability = 1900;
		speed = 3.2;
		img = {
			-- ingot = 'sorcery_iridium_ingot.png';
			-- block = 'sorcery_iridium_block.png';
		};
		slots = {
................................................................................
	};
	impervium = {
		tone = {226,255,107}, alpha = 90;
		cooktime = 260;
		meltpoint = 5;
		artificial = true;
		speed = 2.1;
		hardness = 15;
		durability = 5300;
		maxenergy = 2300;
		watercool = true;
		mix = {
			metals = {
				duranium = 4;
				iridium = 2;
................................................................................
	};
	eternium = {
		tone = {156,82,222}, alpha = 100;
		cooktime = 500;
		meltpoint = 6;
		artificial = true;
		speed = 2;
		hardness = 10;
		maxenergy = 1200;
		durability = 8100;
		watercool = true;
		mix = {
			metals = {
				iridium = 2;
				tungsten = 2;
				lithium = 1;
			};
		};
		sinter = {
			'sorcery:powder_iridium';
			'sorcery:powder_iridium';
			'sorcery:powder_tungsten';
			'sorcery:powder_tungsten';
			'sorcery:powder_lithium';
		};
		slots = {
			{affinity={'praxic','entropic'}, confluence = 1.4};
			{affinity={'praxic','syncretic'}, confluence = 1.1};
		}
	};
	unobtanium = {
		tone = {114,255,214}, alpha = 120;
		meltpoint = 3;
		cooktime = 330;
		artificial = true;
		maxenergy = 4000;
		hardness = 7;
		durability = 3300;
		conduct = 15;
		speed = 3.4;
		slots = {
			{affinity={'praxic'}, confluence = 0.7};
			{affinity={'counterpraxic'}, confluence = 1.2};
			{affinity={'cognic'}, confluence = 1.1};
		};
		mix = {
................................................................................
			metals = {
				impervium = 8;
				eternium = 4;
				cobalt = 1;
			};
		};
	};
	draconium = {
		tone = {255,20,80}, alpha = 110;
		artificial=true;
		meltpoint = 5;
		cooktime = 120;
		hardness = 8;
		maxconduct = 10;
		speed = 1.7;
		maxenergy = 2200;
		durability = 1500;
		slots = {
			{affinity={'praxic'},confluence=3};
			{affinity={'syncretic'},confluence=2};
		};
		sinter = {
			'sorcery:powder_lithium';
			'sorcery:powder_aluminum';
			'sorcery:powder_iridium';
			'sorcery:oil_flame';
		};
	};
	tyrannium = {
		tone = {46,255,245}, alpha = 80;
		artificial=true;
		hardness = 20;
		meltpoint = 6;
		cooktime = 240;
		maxenergy = 800;
		durability = 4000;
		speed = 3.0;
		slots = {};
	};
};

Modified data/spells.lua from [47b0c7f3e9] to [a738b55bd0].

384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
			local duration = (ctx.base.gem == 'amethyst' and 60) or 30
			if ctx.base.gem == 'diamond' then
				power = power * (math.random()*2)
				range = range * (math.random()*2)
				duration = duration * (math.random()*2)
			end
			local lum = math.ceil((power/maxpower) * minetest.LIGHT_MAX)
			print('setting lum',lum)
			for i=1,power do
				local pos = vector.add(center, {
					x = math.random(-range,range);
					z = math.random(-range,range);
					y = math.random(0,range/2);
				})
				local delta = vector.subtract(pos,center)







<







384
385
386
387
388
389
390

391
392
393
394
395
396
397
			local duration = (ctx.base.gem == 'amethyst' and 60) or 30
			if ctx.base.gem == 'diamond' then
				power = power * (math.random()*2)
				range = range * (math.random()*2)
				duration = duration * (math.random()*2)
			end
			local lum = math.ceil((power/maxpower) * minetest.LIGHT_MAX)

			for i=1,power do
				local pos = vector.add(center, {
					x = math.random(-range,range);
					z = math.random(-range,range);
					y = math.random(0,range/2);
				})
				local delta = vector.subtract(pos,center)

Modified enchanter.lua from [41724791de] to [15ff8624ed].

231
232
233
234
235
236
237

238
239
240
241
242
243
244
...
412
413
414
415
416
417
418










419
420
421
422
423
424
425
	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";
	};
................................................................................
	if ench.energy == 0 then
		ench.spells = {}
		sorcery.enchant.set(tool,ench)
	else
		sorcery.enchant.set(tool,ench,true)
	end
	puncher:set_wielded_item(tool)










end)

minetest.register_chatcommand('enchants', {
	description = 'Log information about the currently held object\'s enchantment';
	privs = { server = true };
	func = function(caller,params)
		local tool = minetest.get_player_by_name(caller):get_wielded_item()







>







 







>
>
>
>
>
>
>
>
>
>







231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
...
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
	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;
	after_dig_node = sorcery.lib.node.purge_container;
	tiles = {
		"default_obsidian.png";
		"default_steel_block.png";
		"default_bronze_block.png";
		"default_junglewood.png";
		"default_gold_block.png";
	};
................................................................................
	if ench.energy == 0 then
		ench.spells = {}
		sorcery.enchant.set(tool,ench)
	else
		sorcery.enchant.set(tool,ench,true)
	end
	puncher:set_wielded_item(tool)

	-- perform leyline checks and call notify if necessary
	if minetest.get_item_group(node.name, 'sorcery_ley_device') ~= 0 then
		for _,p in pairs(sorcery.ley.txofs) do
			local sum = vector.add(pos,p)
			if minetest.get_item_group(minetest.get_node(sum).name, 'sorcery_ley_device') ~= 0 then
				sorcery.ley.notify(sum)
			end
		end
	end
end)

minetest.register_chatcommand('enchants', {
	description = 'Log information about the currently held object\'s enchantment';
	privs = { server = true };
	func = function(caller,params)
		local tool = minetest.get_player_by_name(caller):get_wielded_item()

Added forcefield.lua version [4c792fc807].













































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
local constants = {
	cost_per_barrier = 0.1;
	-- 0.1 points of current will be required per second per
	-- unit of charge given to a barrier-block when ley-force
	-- of counterpraxic affinity is not available
}
local aimtbl = {
	[0] = {x =  0, y =  1, z =  0};
	[1] = {x =  0, y =  0, z =  1};
	[2] = {x =  0, y =  0, z = -1};
	[3] = {x =  1, y =  0, z =  0};
	[4] = {x = -1, y =  0, z =  0};
	[5] = {x =  0, y = -1, z =  0};
}
local pofstbl = {
	[0] = {x=0.5, y=0,   z=0.5};
	[1] = {x=0.5, y=0.5, z=0};
	[2] = {x=0.5, y=0.5, z=0};
	[3] = {x=0,   y=0.5, z=0.5};
	[4] = {x=0,   y=0.5, z=0.5};
	[5] = {x=0.5, y=0,   z=0.5};
}
local calc_cost = function(pos,time)
	local node = minetest.get_node(pos)
	local aim = aimtbl[math.floor(node.param2 / 4)]
	local tgts = {}
	for i=1,5 do
		local tpos = vector.add(pos, vector.multiply(aim,i))
		local n = minetest.get_node(tpos)
		if n.name == 'air' then tgts[#tgts + 1] = {tpos,0} else
			local f = minetest.get_item_group(n.name, 'sorcery_force_barrier')
			if f > 0 then
				tgts[#tgts + 1] = {tpos,f}
			else break end
		end
	end
	return {
		aim = aim;
		maxcost = #tgts * constants.cost_per_barrier * time;
		mincost = math.min(1,#tgts) * constants.cost_per_barrier * time;
		targets = tgts;
	}
end
for i=1,10 do
	minetest.register_node('sorcery:air_barrier_' .. tostring(i), {
		drawtype = 'airlike';
		walkable = true;
		pointable = false;
		sunlight_propagates = true;
		paramtype = 'light';
		light_source = i;
		groups = {
			sorcery_force_barrier = i;
		};
		-- _proto = {
		-- 	strength = i;
		-- };
		on_construct = function(pos)
			minetest.get_node_timer(pos):start(1)
		end;
		on_timer = function(pos,delta)
			local dec = 40*delta
			local node = minetest.get_node(pos)
			local newstr = math.ceil(10 * ((node.param2 - dec) / 0xFF))
			local newnode = 'sorcery:air_barrier_' .. tostring(newstr)
			if newstr <= 0 then
				minetest.remove_node(pos)
				return false
			else
				for _,c in pairs {{165,255,252}, {146,205,255}, {190,93,253}} do
					minetest.add_particlespawner {
						time = 1;
						amount = 15;
						minpos = vector.add(pos, -0.6);
						maxpos = vector.add(pos, 0.6);
						minvel = {x = 0, y = 0, z = 0};
						maxvel = {x = 0, y = 0, z = 0};
						minacc = {x = -0.1, y = -0.1, z = -0.1};
						maxacc = {x =  0.1, y =  0.1, z =  0.1};
						minexptime = 0.5;
						maxexptime = 1.0;
						minsize = 0.2;
						minsize = 0.7;
						texture = sorcery.lib.image('sorcery_spark.png'):multiply(sorcery.lib.color(c)):render();
						glow = 14;
						animation = {
							length = 1.1;
							type = 'vertical_frames';
							aspect_h = 16, aspect_w = 16;
						};
					}
				end
				minetest.swap_node(pos, {
					name = newnode;
					param1 = node.param1;
					param2 = newstr;
				})
				return true
			end
		end;
	})
end
minetest.register_node('sorcery:emitter_barrier', {
	description = "Barrier Screen Emitter";
	paramtype2 = 'facedir';
	groups = {
		cracky = 2;
		sorcery_ley_device = 1;
	};
	tiles = {
		'sorcery_emitter_barrier_top.png';
		'sorcery_emitter_barrier_bottom.png';
		'sorcery_emitter_barrier_front.png^[transformR270';
		'sorcery_emitter_barrier_front.png^[transformFXR90';
		'sorcery_emitter_barrier_side.png';
		'sorcery_emitter_barrier_side.png';
	};
	on_construct = function(pos)
		minetest.get_node_timer(pos):start(1)
	end;
	on_timer = function(pos,delta)
		local orientation = math.floor(minetest.get_node(pos).param2 / 4)
		local costs = calc_cost(pos,delta)
		local l = sorcery.ley.netcaps(pos,delta)
		if l.self.powerdraw >= costs.mincost then
			local dist = l.self.powerdraw / (constants.cost_per_barrier * delta)
			for i=1,math.floor(dist) do
				local t = costs.targets[i]
				local str = math.min(0xFF,t[2] + 50*delta);
				minetest.swap_node(t[1], {
					name = 'sorcery:air_barrier_' .. math.max(1, math.floor(10*(str/0xFF)));
					param2 = str;
				})
				minetest.get_node_timer(t[1]):start(1)
			end

			local pn = vector.add(pos, vector.divide(costs.aim,2));
			local pp = vector.add(pn, pofstbl[orientation])
			pn = vector.subtract(pn,  pofstbl[orientation])
			
			minetest.add_particlespawner {
				time = 1;
				amount = 20 * dist;
				minpos = pn;
				maxpos = pp;
				minvel = costs.aim;
				maxvel = vector.multiply(costs.aim,2);
				minsize = 0.3;
				maxsize = 0.5;
				minexptime = dist*0.5;
				maxexptime = dist*0.5;
				texture = sorcery.lib.image('sorcery_spark.png'):multiply(sorcery.lib.color(240,255,160)):render();
				glow = 14;
				animation = {
					length = dist + 0.1;
					type = 'vertical_frames';
					aspect_h = 16, aspect_w = 16;
				};
			}
			return true
		end
		return false
	end;
	after_place_node = function(pos, placer, stack, point)
		local vec = vector.subtract(point.under, pos)
		local n = minetest.get_node(pos)
		n.param2 = minetest.dir_to_facedir(vec)
		minetest.swap_node(pos,n)
	end;
	_sorcery = {
		ley = {
			mode='consume', affinity={'counterpraxic'},
			power = function(pos,time)
				local l = calc_cost(pos,time)
				return l.mincost, l.maxcost
			end;
		};
		on_leychange = function(pos)
			minetest.get_node_timer(pos):start(1)
		end;
	};
})

Modified harvester.lua from [8384a43ac0] to [97dec98beb].

15
16
17
18
19
20
21

22
23
24
25
26
27
28

minetest.register_node('sorcery:harvester', {
	description = 'Harvester';
	drawtype = 'mesh';
	paramtype = 'light';
	paramtype2 = 'facedir';
	mesh = 'sorcery-harvester.obj';

	groups = { cracky = 2; oddly_breakable_by_hand = 1; };
	sunlight_propagates = true;
	selection_box = hitbox;
	collision_box = hitbox;
	tiles = {
		amethyst:render();
		'default_copper_block.png';







>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

minetest.register_node('sorcery:harvester', {
	description = 'Harvester';
	drawtype = 'mesh';
	paramtype = 'light';
	paramtype2 = 'facedir';
	mesh = 'sorcery-harvester.obj';
	after_dig_node = sorcery.lib.node.purge_container;
	groups = { cracky = 2; oddly_breakable_by_hand = 1; };
	sunlight_propagates = true;
	selection_box = hitbox;
	collision_box = hitbox;
	tiles = {
		amethyst:render();
		'default_copper_block.png';

Modified infuser.lua from [c06011384f] to [3997cbe28f].

194
195
196
197
198
199
200

201
202
203
204
205
206
207
end

minetest.register_node("sorcery:infuser", {
	description = "Infuser";
	drawtype = "mesh";
	mesh = "infuser.obj";
	paramtype2 = "facedir";

	tiles = { -- FIXME
		"default_stone.png",
		"default_copper_block.png",
		"default_steel_block.png",
		"default_bronze_block.png",
		"default_tin_block.png",
	};







>







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
end

minetest.register_node("sorcery:infuser", {
	description = "Infuser";
	drawtype = "mesh";
	mesh = "infuser.obj";
	paramtype2 = "facedir";
	after_dig_node = sorcery.lib.node.purge_container;
	tiles = { -- FIXME
		"default_stone.png",
		"default_copper_block.png",
		"default_steel_block.png",
		"default_bronze_block.png",
		"default_tin_block.png",
	};

Modified init.lua from [8277bf1fce] to [752c791349].

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
..
47
48
49
50
51
52
53
54
55
56
57
58
59
60
-- and load them automatically, as interdependencies
-- exist (especially with /lib) and we need to be very
-- careful about the order they're loaded in

sorcery.unit('data') {'ui'}
sorcery.unit('lib') {
	-- convenience
	'str';
	-- serialization
	'marshal', 'json';
	-- data structures
	'tbl', 'class';
	-- wrappers
	'color', 'image', 'ui';
}
................................................................................
	'potions', 'oils', 'greases',
		'draughts', 'elixirs',
		'philters', 'extracts';
	'register';
}

for _,u in pairs {
	'leylines'; 'ores'; 'gems';
	'potions'; 'infuser'; 'altar'; 'wands';
	'tools'; 'enchanter'; 'harvester';
	'metallurgy-hot', 'metallurgy-cold';
	'entities'; 'recipes'; 'coins';
	'interop'; 'tnodes';
} do sorcery.load(u) end







|







 







|




|

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
..
47
48
49
50
51
52
53
54
55
56
57
58
59
60
-- and load them automatically, as interdependencies
-- exist (especially with /lib) and we need to be very
-- careful about the order they're loaded in

sorcery.unit('data') {'ui'}
sorcery.unit('lib') {
	-- convenience
	'str', 'node';
	-- serialization
	'marshal', 'json';
	-- data structures
	'tbl', 'class';
	-- wrappers
	'color', 'image', 'ui';
}
................................................................................
	'potions', 'oils', 'greases',
		'draughts', 'elixirs',
		'philters', 'extracts';
	'register';
}

for _,u in pairs {
	'ores'; 'gems';	'leylines';
	'potions'; 'infuser'; 'altar'; 'wands';
	'tools'; 'enchanter'; 'harvester';
	'metallurgy-hot', 'metallurgy-cold';
	'entities'; 'recipes'; 'coins';
	'interop'; 'tnodes'; 'forcefield';
} do sorcery.load(u) end

Modified leylines.lua from [112e3b2a50] to [8532b8e5fe].

71
72
73
74
75
76
77






























































































78























79
80


















































81
82
83




84
85


















86
87
88
89
90
91
92
...
102
103
104
105
106
107
108

109
110
111
112
113
114
115
...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147









148
149
150
151
152
153
154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

184




185
186
187
188
189
190
191
192
193
194

195
196
197
198
199
200
201
202
203
204
205
206
207





208
209
210
211
212
213
214
...
216
217
218
219
220
221
222

223
224
225
226
227
228
229
...
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257
258

259
260
261
262
263
264
265
266
267
268
269
270


271
272
273
274
275
276
277
278
279
280
281
282

283





284
285
286
287
288
289
290
291





292
293
294

295
296
297
298
299
300
301
302
303
304
305
306
307
308
309



310
311
312
313
314
315


316














317


















318




319
320
321
322
323
324
325
326
327
328
329
330


331

332
333


334


335
336
337
338
339
340
341

342
343



344







345
346
347
348
349




























350
351
352
353
354
355
356

357
358
















































































































































	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;
................................................................................
			-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';
................................................................................
			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
................................................................................
		[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























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
|
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







|
|
|
|
|
|
|
|











>
>
>
>
>
>
>
>
>



|










>

|










|
<
<
<
<
<
<
<
|

>

>
>
>
>


|







>













>
>
>
>
>







 







>







 







|
>









|
>











|
>
>









|


>

>
>
>
>
>

<
<
<
<
<
<
<
>
>
>
>
>


<
>


|
<
<
<



<
|
<
<
<

>
>
>
|
|
|
|
|
|
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
|
<
<
<
<







>
>

>


>
>

>
>







>

|
>
>
>
|
>
>
>
>
>
>
>





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373







374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
...
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498







499
500
501
502
503
504
505

506
507
508
509



510
511
512

513



514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565




566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
	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'};
	};
};

local makeswitch = function(switch, desc, tex, tiles, power)
	for _,active in pairs{true,false} do
		local turn = function(pos)
			local n = minetest.get_node(pos)
			minetest.sound_play('doors_steel_door_open', {
				gain = 0.7;
				pos = pos;
			}, true)
			local leymap = active and sorcery.ley.mapnet(pos) or nil
			minetest.swap_node(pos, {
				name = active and (switch .. '_off')
							   or  switch;
				param1 = n.param1;
				param2 = n.param2;
			})
			if active then
				-- if we're turning it off, use the old map,
				-- because post-swap the network will be
				-- broken and notify won't reach everyone
				leymap.map[leymap.startpos] = nil
				sorcery.ley.notifymap(leymap.map)
			else sorcery.ley.notify(pos) end
		end
		local tl = table.copy(tiles)
		tl[6] = tex .. '^sorcery_ley_switch_panel.png^sorcery_ley_switch_' .. (active and 'down' or 'up') .. '.png';
		minetest.register_node(switch .. (active and '' or '_off'), {
			description = desc;
			drop = switch;
			tiles = tl;
			paramtype2 = 'facedir';
			groups = {
				cracky = 2; choppy = 1;
				punch_operable = 1;
				sorcery_ley_device = active and 1 or 0;
			};
			_sorcery = {
				ley = active and {
					mode = 'signal'; power = power;
				} or nil;
			};
			on_punch = function(pos,node,puncher,point)
				if puncher ~= nil then
					if puncher:get_wielded_item():is_empty() then
						turn(pos)
					end
				end
				return minetest.node_punch(pos,node,puncher,point)
			end;
			on_rightclick = turn;
		})
	end
end

for _,b in pairs {
	{'Applewood', 'wood', 'default_wood.png'};
	{'Junglewood', 'junglewood', 'default_junglewood.png'};
	{'Pine', 'pine_wood', 'default_pine_wood.png'};
	{'Acacia', 'acacia_wood', 'default_pine_wood.png'};
	{'Aspen', 'aspen_wood', 'default_aspen_wood.png'};
	{'Stone', 'stone', 'default_stone.png'};
	{'Cobblestone', 'cobble', 'default_cobble.png'};
	{'Stone Brick', 'stonebrick', 'default_stone_brick.png'};
	{'Brick', 'brick', 'default_brick.png'};
} do
	local id = 'sorcery:conduit_half_' .. b[2]
	local switch = 'sorcery:conduit_switch_' .. b[2]
	local item = (b[4] or 'default') .. ':' .. b[2]
	local tex = b[3]
	local mod = '^[lowpart:50:'
	local sidemod = '^[transformR270^[lowpart:50:'
	local unflip = '^[transformR90'
	local tiles = {
		'sorcery_conduit_copper_top.png'..mod..tex; -- top
		tex..mod..'sorcery_conduit_copper_top.png';
		tex .. sidemod .. 'sorcery_conduit_copper_side.png' .. unflip; -- side
		'sorcery_conduit_copper_side.png' .. sidemod .. tex .. unflip; -- side
		'sorcery_conduit_copper_side.png'; -- back
		tex; -- front
	}
	minetest.register_node(id, {
		description = 'Half-' .. b[1] .. ' Conduit';
		paramtype2 = 'facedir';
		groups = {
			cracky = 2;
			choppy = 1;
			sorcery_ley_device = 1;
			sorcery_ley_conduit = 1;
		};
		_sorcery = {
			ley = { mode = 'signal'; power = 5; }
		};
		tiles = tiles;
	})
	minetest.register_craft {
		output = id .. ' 4';
		recipe = {
			{item, 'sorcery:conduit'};
			{item, 'sorcery:conduit'};
		};
	};
	makeswitch(switch, b[1] .. " Conduit Switch", tex, tiles, 5)
	minetest.register_craft {
		output = switch;
		recipe = {
			{'xdecor:lever_off',id};
		};
	}
end
makeswitch('sorcery:conduit_switch', "Conduit Switch", 'sorcery_conduit_copper_side.png', {
	'sorcery_conduit_copper_top.png';
	'sorcery_conduit_copper_top.png';
	'sorcery_conduit_copper_side.png';
	'sorcery_conduit_copper_side.png';
	'sorcery_conduit_copper_side.png';
	'sorcery_conduit_copper_side.png';
}, 10)
minetest.register_craft {
	output = 'sorcery:conduit_switch';
	recipe = {
		{'xdecor:lever_off','sorcery:conduit'};
	};
}

for name,metal in pairs(sorcery.data.metals) do
	if metal.conduct then
		local cable = 'sorcery:cable_' .. name
		minetest.register_node(cable, {
			description = sorcery.lib.str.capitalize(name) .. " Cable";
			drawtype = 'nodebox';
			groups = {
				sorcery_ley_device = 1; snappy = 3; attached = 1;
				sorcery_ley_cable = 1;
			};
			_sorcery = {
				ley = { mode = 'signal', power = metal.conduct };
			};
			sunlight_propagates = true;
			node_box = {
				type = 'connected';
				disconnected   = { -0.05, -0.35, -0.40; 0.05, -0.25, 0.40 };
				connect_front  = { -0.05, -0.35, -0.50; 0.05, -0.25, 0.05 };
				connect_back   = { -0.05, -0.35, -0.05; 0.05, -0.25, 0.50 };
				connect_right  = { -0.05, -0.35, -0.05; 0.50, -0.25, 0.05 };
				connect_left   = { -0.50, -0.35, -0.05; 0.05, -0.25, 0.05 };
				connect_top    = { -0.05, -0.25, -0.05; 0.05,  0.50, 0.05 };
				connect_bottom = { -0.05, -0.50, -0.05; 0.05, -0.35, 0.05 };
			};
			connects_to = { 'group:sorcery_ley_device', 'default:mese' };
			-- harcoding mese is kind of cheating -- figure out a
			-- better way to do this for the longterm
			paramtype = 'light';
			-- paramtype2 = 'facedir';
			after_place_node = function(pos, placer, stack, point)
				local vec = vector.subtract(point.under, pos)
				local n = minetest.get_node(pos)
				n.param2 = minetest.dir_to_facedir(vec)
				minetest.swap_node(pos,n)
			end;
			tiles = { 'sorcery_ley_plug.png' };
		})

		minetest.register_craft {
			output = cable .. ' 8';
			recipe = {
				{'basic_materials:copper_wire','basic_materials:copper_wire','basic_materials:copper_wire'};
				{ metal.parts.fragment, metal.parts.fragment, metal.parts.fragment };
				{'basic_materials:copper_wire','basic_materials:copper_wire','basic_materials:copper_wire'};
			};
			replacements = {
				{'basic_materials:copper_wire', 'basic_materials:empty_spool'};

				{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
				{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
				{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
				{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
				{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
			};
		};
	end
end

-- ley.notify will normally be called automatically, but if a
-- ley-producer or consume has fluctuating levels of energy
-- consumption, it should call this function when levels change
sorcery.ley.notifymap = function(map)
	for pos,name in pairs(map) do
		local props = minetest.registered_nodes[name]._sorcery
		if props and props.on_leychange then
			props.on_leychange(pos)
		end
	end
end
sorcery.ley.notify = function(pos)
	local n = sorcery.ley.mapnet(pos)
	sorcery.ley.notifymap(n.map)
end

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;
................................................................................
			-0.5, -0.5, -0.5;
			 0.5,  1.2,  0.5;
		};
	};
	minetest.register_node('sorcery:condenser', {
		description = 'Condenser';
		drawtype = 'mesh';
		paramtype2 = 'facedir';
		mesh = 'sorcery-condenser.obj';
		selection_box = box;
		collision_box = box;
		tiles = {
			amethyst:render();
			'sorcery_condenser.png';
			'default_tin_block.png';
................................................................................
			sorcery_ley_device = 1;
		};
		on_construct = function(pos)
			local meta = minetest.get_meta(pos)
			meta:set_string('infotext','Condenser')
		end;
		_sorcery = {
			ley = { mode = 'produce';
				power = function(pos,time)
					return sorcery.ley.field_to_current(sorcery.ley.estimate(pos).force, time);
				end;
				affinity = function(pos)
					return sorcery.ley.estimate(pos).aff
				end;
			};
		};
	})
end

minetest.register_craft {
	output = 'sorcery:condenser';
	recipe = {
		{'sorcery:accumulator'};
		{'sorcery:conduit'};
	};
}
sorcery.ley.txofs = {
	{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};
}
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,checked = {},{}
	power = power or 0
	
	local devices = {
		consume = {};
		produce = {};
		signal = {};
	}
	local numfound = 0
	local maxconduct = 0
	local minconduct
	local startkey
	local foundp = function(p)
		for _,k in pairs(checked) 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(sorcery.ley.txofs) do







				local sum = vector.add(pos,p)
				if not foundp(sum) then
					checked[#checked + 1] = sum
					local nodename = minetest.get_node(sum).name
					if nodename == 'ignore' then
						minetest.load_area(sum)
						nodename = minetest.get_node(sum).name
					end
					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,{query={mode=true}})
						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
							d.power = sorcery.ley.sample(pos,1,nodename,{query={power=true}}).power
							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;
						if not startkey then
							if vector.equals(startpos,sum) then
								startkey = sum
							end
						end
						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;
			startpos = startkey;
			conduct = {
				min = minconduct;
				max = maxconduct;
			};
		}
	else return nil end
end
................................................................................
		[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;
		minpower = m.t.u32; -- min power generated/consumed * 10,000
		maxpower = m.t.u32; -- max 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];
			minpower = l.minpower * 10000;
			maxpower = l.maxpower * 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];
			minpower = obj.minpower / 10000.0;
			maxpower = obj.maxpower / 10000.0;
			power = (obj.minpower == obj.maxpower) and obj.minpower or nil;
			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,flags)
	-- returns how much ley-force can be transmitted by a
	-- device over timespan
	local ret = {}
	name = name or minetest.get_node(pos).name
	flags = flags or {}
	flags.query = flags.query or {
		mode = true; power = true; affinity = true;
		minpower = true; maxpower = true;
	}
	local props = minetest.registered_nodes[name]._sorcery








	local evaluate = function(v)
		if type(v) == 'function' then
			return v(pos)
		else return v end
	end


	local leymeta do
		local nm = minetest.get_meta(pos)
		if nm:contains('sorcery:ley') then
			leymeta = sorcery.ley.decode(nm:get_string('sorcery:ley'))



		end
	end


	local compat = sorcery.data.compat.ley[name] 




	local lookup = function(k,default)
		if leymeta and leymeta[k] then return leymeta[k]
		elseif props and props.ley and props.ley[k] then return props.ley[k]
		elseif compat and compat[k] then return compat[k]
		else return default end 
	end
	if flags.query.mode     then ret.mode     = evaluate(lookup('mode','none')) end
	if flags.query.affinity then ret.affinity = evaluate(lookup('affinity',{})) end
	if flags.query.minpower or flags.query.maxpower or flags.query.power then
		local condset = function(name,var)
			if flags.query[name] then ret[name] = var end
		end
		local p = lookup('power')
		if p then
			if type(p) == 'function' then
			-- we have a single function to calculate power usage; we need to
			-- check whether it returns min,max or a constant
				local min, max = p(pos,timespan)
				if (not max) or min == max then
					ret.power = min
					condset('power',min)
					condset('minpower',min)
					condset('maxpower',min)
				else
					condset('minpower',min)
					condset('maxpower',max)
				end
			else -- power usage is simply a constant
				condset('power',p)
				condset('minpower',p)
				condset('maxpower',p)
			end
		else
			local feval = function(v)
				if type(v) == 'function' then
					return v(pos,timespan)
				else return v * timespan end
			end
			local min = feval(lookup('minpower'))
			local max = feval(lookup('maxpower'))
			condset('minpower',min)
			condset('maxpower',max)
			if min == max then condset('power',min) end
		end
	end

	if ret.power then
		if flags.query.minpower and not ret.minpower then ret.minpower = power end
		if flags.query.maxpower and not ret.maxpower then ret.maxpower = power end
	end
	return ret




end

sorcery.ley.netcaps = function(pos,timespan,exclude)
	local net = sorcery.ley.mapnet(pos)
	local maxpower = 0
	local freepower = 0
	local affs,usedaffs = {},{}
	local flexpowerdevs = {}
	local devself
	for _,n in pairs(net.devices.produce) do
		if vector.equals(pos,n.pos) then devself = n end
		if not exclude or not vector.equals(n.pos,exclude) then
			local ln = sorcery.ley.sample(n.pos,timespan,n.id)
			n.powersupply = ln.power
			n.affinity = ln.affinity
			maxpower = maxpower + ln.power
			-- production power does not vary, tho at some point it
			-- might be useful to enable some kind of power scaling
			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 vector.equals(pos,n.pos) then devself = n end
		if not exclude or not vector.equals(n.pos,exclude) then
			local ln = sorcery.ley.sample(n.pos,timespan,n.id, {
				query = { power = true; minpower = true; maxpower = true; affinity = true; };
			})
			n.powerdraw = (ln.minpower <= freepower) and ln.minpower or 0
			freepower = freepower - n.powerdraw
			-- merge in sample data and return it along with the map
			n.minpower = ln.minpower
			n.maxpower = ln.maxpower
			n.affinity = ln.affinity
			if ln.maxpower > ln.minpower then
				flexpowerdevs[#flexpowerdevs+1] = n
			end
			for _,a in pairs(ln.affinity) do
				usedaffs[a] = (usedaffs[a] or 0) + 1
			end
		end
	end

	-- now we know the following: all devices; if possible, have been
	-- given the minimum amount of power they need to run. if freepower
	-- < 0 then the network is overloaded and inoperable. if freepower>0,
	-- we now need to distribute the remaining power to devices that
	-- have a variable power consumption. there's no clean way of doing
	-- this, so we use the following algorithm:
	--   1. take a list of devices that want more power
	--   2. divide the amount of free power by the number of such devices
	--      to derive the maximum power that can be allocated to any device
	--   3. iterate through the devices. increase their power consumption by
	--      the maximum term. any device that is satiated can be removed from
	--      the list.
	--   4. if there is still power remaining, repeat until there is not.

	while freepower > 0 and #flexpowerdevs > 0 do
		local nextiter = {}
		local maxgive = freepower / #flexpowerdevs
		for _,d in pairs(flexpowerdevs) do
			local give = math.min(maxgive,d.maxpower - d.powerdraw)
			freepower = freepower - give
			d.powerdraw = d.powerdraw + give
			if d.powerdraw < d.maxpower then
				nextiter[#nextiter+1] = d
			end
		end
		flexpowerdevs = nextiter
	end
	
	return {
		net = net;
		freepower = freepower;
		maxpower = maxpower;
		affinity = affs;
		affinity_balance = usedaffs;
		self = devself;
	}
end

minetest.register_on_placenode(function(pos, node)
	if minetest.get_item_group(node.name, 'sorcery_ley_device') ~= 0 then
		sorcery.ley.notify(pos)
	end
end)

local constants = {
	generator_max_energy_output = 5;
	-- how much energy a generator makes after

	generator_time_to_max_energy = 150;
	-- seconds of activity
	
	generator_power_drain_speed = 0.1;
	-- points of energy output drained per second of no fuel
}
local update_generator = function(pos)
	minetest.get_node_timer(pos):start(1)
end
local generator_update_formspec = function(pos)
	local meta = minetest.get_meta(pos)
	local burnprog = math.min(1,meta:get_float('burnleft') / meta:get_float('burntime'))
	local power = meta:get_float('power')
	local inv = meta:get_inventory()
	local lamps = ''
	for i=0,4 do 
		local color
		if power - i >= 1 then
			color = 'red'
		elseif power - i > 0 then
			color = 'yellow'
		else
			color = 'off'
		end
		lamps = lamps .. string.format([[
			image[%f,0.5;1,1;sorcery_statlamp_%s.png]
		]], 2.5 + i, color)
	end
	meta:set_string('formspec', string.format([[
		size[8,5.8]
		list[context;fuel;0.5,0.5;1,1]
		list[current_player;main;0,2;8,4]
		image[1.5,0.5;1,1;default_furnace_fire_bg.png^[lowpart:%u%%:default_furnace_fire_fg.png]
	]], math.floor(burnprog * 100)) .. lamps)
end
for _,active in pairs{true,false} do
	local id = 'sorcery:generator' .. (active and '_active' or '')
	minetest.register_node(id, {
		description = 'Generator';
		paramtype2 = 'facedir';
		groups = { cracky = 2; sorcery_ley_device = 1; };
		drop = 'sorcery:generator';
		tiles = {
			'sorcery_ley_generator_top.png';
			'sorcery_ley_generator_bottom.png';
			'sorcery_ley_generator_side.png';
			'sorcery_ley_generator_side.png';
			'sorcery_ley_generator_back.png';
			'sorcery_ley_generator_front_' .. (active and 'on' or 'off') .. '.png';
		};
		on_construct = function(pos)
			local meta = minetest.get_meta(pos)
			local inv = meta:get_inventory()
			meta:set_string('infotext','Generator')
			meta:set_float('burntime',0)
			meta:set_float('burnleft',0)
			meta:set_float('power',0)
			generator_update_formspec(pos)
			inv:set_size('fuel',1)
		end;
		after_dig_node = sorcery.lib.node.purge_container;
		on_metadata_inventory_put = update_generator;
		on_metadata_inventory_take = update_generator;
		on_timer = function(pos,delta)
			local meta = minetest.get_meta(pos)
			local inv = meta:get_inventory()
			local self = minetest.get_node(pos)
			local timeleft = meta:get_float('burnleft') - delta
			local again = false
			local power = meta:get_float('power')
			local burning = active
			if timeleft < 0 then timeleft = 0 end
			if not active or timeleft == 0 then
				if inv:is_empty('fuel') then
					-- no fuel, can't start/keep going. drain power if
					-- necessary, otherwise bail
					burning = false
					if power > 0 then
						power = math.max(0, power - constants.generator_power_drain_speed)
						again = true
					end
				else
					-- fuel is present, let's burn it
					local res,decin = minetest.get_craft_result {
						method = 'fuel';
						items = {inv:get_stack('fuel',1)};
					}
					meta:set_float('burntime',res.time)
					timeleft = res.time
					inv:set_stack('fuel',1,decin.items[1])
					again = true
					burning = true
				end
			else
				local eps = constants.generator_max_energy_output / constants.generator_time_to_max_energy
				power = math.min(constants.generator_max_energy_output, power + eps*delta)
				again = true
			end
			::stop:: meta:set_float('power',power)
			         meta:set_float('burnleft',timeleft)
					 generator_update_formspec(pos)
					 if burning and not active then
						 minetest.swap_node(pos, {
							 name = 'sorcery:generator_active';
							 param1 = self.param1, param2 = self.param2;
						 })
					 elseif active and not burning then
						 minetest.swap_node(pos, {
							 name = 'sorcery:generator';
							 param1 = self.param1, param2 = self.param2;
						 })
					 end
					 return again
		end;
		allow_metadata_inventory_put = function(pos,listname,index,stack,user)
			local res = minetest.get_craft_result {
				method = 'fuel';
				items = {stack};
			}
			if res.time ~= 0 then return stack:get_count()
				else return 0 end
		end;
		_sorcery = {
			ley = {
				mode = 'produce', affinity = {'praxic'};
				power = function(pos,delta)
					local meta = minetest.get_meta(pos)
					return meta:get_float('power') * delta;
				end;
			};
		};
	})
end

Added lib/node.lua version [a0d38cfe10].











































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
return {
	purge_container = function(pos,node,meta,user)
		local offset = function(pos,range)
			local r = function(min,max)
				return (math.random() * (max - min)) + min
			end
			return {
				x = pos.x + r(0 - range, range);
				y = pos.y;
				z = pos.z + r(0 - range, range);
			}
		end
		for name, inv in pairs(meta.inventory) do
			for _, item in pairs(inv) do
				if not item:is_empty() then
					minetest.add_item(offset(pos,0.4), item)
				end
			end
		end
	end;
}

Modified metallurgy-cold.lua from [c21bb1a7ca] to [c1d9a4ce4b].

34
35
36
37
38
39
40




41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
..
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121



122
123
124
125
126
127
128
...
134
135
136
137
138
139
140

141
142
143
144
145
146
147
...
163
164
165
166
167
168
169

170
171
172
173
174
175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
...
345
346
347
348
349
350
351
352
353
354
355
356
	
	metal_grindvalue = 4;
	-- how many powders an ingot is worth

	default_grindvalue = 1;
	-- number of items produced when not otherwise
	-- specified




	
	default_hardness = 1;
	-- difficulty to grind an item when not otherwise
	-- specified

	metal_grindcost = 1;
	-- number of metal items needed to perform a grind
}
local mill_formspec_update = function(pos,pct,stat1,stat2)
	-- eventually we'll want to display available
	-- energy here, but for now we're just assuming
	-- max energy available

	-- local meta = minetest.get_meta(pos)
	-- local inv = meta:get_inventory()
	-- local torque = 20
	stat1 = stat1 or 'off'
	minetest.get_meta(pos):set_string('formspec', string.format([[
		size[8,7.2]
................................................................................
	if slot == 'grinder' then
		if minetest.get_item_group(item:get_name(), 'sorcery_mill_grindhead')~=0 
			then return 1 end
	elseif slot == 'input' then
		local metal = sorcery.data.metallookup[item:get_name()]
		local mat = sorcery.matreg.lookup[item:get_name()]
		local comp = sorcery.data.compat.grindables[item:get_name()]
		if metal or mat.metal or comp then
			return item:get_count()
		else
			local mat = item:get_definition()._matprop
			if mat and mat.grindvalue then
				return item:get_count() 
			end
		end
	end
	return 0
end
local matprops = function(item)
	local metal = sorcery.data.metallookup[item:get_name()]
	if not metal then
		-- allow grinding of armor and tools back to their
		-- original components
		local mat = sorcery.matreg.lookup[item:get_name()]
		if mat.metal then
			metal = mat
		else
			return nil
		end
	end
	local mp = item:get_definition()._matprop
		or sorcery.data.compat.grindables[item:get_name()]
		or {}

	again = true
	if metal then mp = {
		hardness = metal.data.hardness or mp.hardness or constants.default_hardness;
		grindvalue = metal.value or mp.grindvalue or (metal and constants.metal_grindvalue) or constants.default_grindvalue;
		powder = metal.data.parts.powder or mp.powder;
		grindcost = constants.metal_grindcost or mp.grindcost; -- invariant for metal
	} end

	mp.torque = constants.grind_torque_factor * mp.hardness




	if item:get_wear() ~= 0 then
		-- prevent cheating by recovering metal from items before they
		-- are destroyed
		local wearfac = (item:get_wear() / 65535)
		mp.grindvalue = math.max(1,math.ceil(mp.grindvalue * wearfac))
		mp.hardness = math.max(1,math.ceil(mp.grindcost * wearfac))
................................................................................
minetest.register_node('sorcery:mill',{
	description = 'Mill';
	groups = {
		cracky = 2;
		sorcery_ley_device = 1;
	};
	paramtype2 = 'facedir';

	on_construct = function(pos)
		local meta = minetest.get_meta(pos)
		local inv = meta:get_inventory()
		inv:set_size('input',1)
		inv:set_size('output',4)
		inv:set_size('grinder',2)
		meta:set_float('grindtime',0)
................................................................................
	on_metadata_inventory_move = function(pos,fl,fi,tl,ti)
		if fl == 'input' or tl == 'input' then
			minetest.get_meta(pos):set_float('grindtime',0)
		end
		mill_update(pos)
	end;
	_sorcery = {

		ley = { mode = 'consume'; affinity = {'praxic'}};
		on_leyconnect = mill_update;
		on_leycalc = function(pos,time)
			local meta = minetest.get_meta(pos)
			local active = meta:get_int('active') == 1
			if not active then return { power = 0; } end
			local inv = meta:get_inventory()
			local item = inv:get_stack('input',1)
			if item:is_empty() then
				meta:set_int('active', 0)
				return { power = 0; }
			end
			return {
				power = matprops(item).torque * time;

			}
		end;
	};
	on_timer = function(pos,delta)
		local meta = minetest.get_meta(pos)
		local inv = meta:get_inventory()
		local elapsed = meta:get_float('grindtime') + delta
		local ley = sorcery.ley.netcaps(pos,delta,pos)
		local again = false
		local active = false
		local reqtime -- sigh
		local statcolor = 'off'
		local grinders_on
		if inv:is_empty('input') or inv:is_empty('grinder') then
			print('empty')
			elapsed = 0
			mill_formspec_update(pos, 0)
		else
			local item = inv:get_stack('input',1)
			local mp = matprops(item)
			if mp.grindcost > item:get_count() then
				elapsed = 0
				mill_formspec_update(pos, 0)
				print('bad grindcost')
				goto stop
			end

			-- print('power supply',ley.maxpower)
			-- print('power available',ley.freepower)
			-- print('power needed',mp.torque*delta)
			if ley.maxpower < (mp.torque * delta) then
				-- not enough potential energy in the system to grind
				-- so don't bother
				print('not enough power')
				statcolor = 'red'
				elapsed = 0 goto stop
			elseif ley.freepower < (mp.torque * delta) then
				-- the net has enough potential energy to supply us,
				-- but too much of it is in use right now. give up
				-- on this round, but try again in a bit to see if
				-- more energy is available
				print('currently not enough power')
				statcolor = 'yellow'
				elapsed = 0 again = true goto stop
			end

			local grinders = 0
			local grindpower = 0
			local grind_wear = {}
................................................................................
					if dif == 0 then
						wear = constants.grind_wear
					elseif dif < 0 then
						wear = constants.grind_wear * ((dif * -1)/constants.grind_range)
					elseif dif > 0 then
						if dif > constants.grind_grace_range then
							wear = 0
							print('grinder reject')
							goto reject
						else
							wear = constants.grind_wear * (1 + (dif/constants.grind_range)) * constants.grind_grace_penalty
						end
					end
					::accept:: grinders = grinders + 1
					           grindpower = grindpower + hh
................................................................................
				if grindpower < mp.hardness then
					statcolor = 'yellow'
				else statcolor='green' end
				grindpower = grindpower / grinders
				-- if there is more power available than needed,
				-- and/or if the blades are stronger than needed,
				-- speed up the grind
				local speedboost = math.max(0.05,((grindpower - mp.hardness)/constants.grind_range) * grinders) * ((mp.torque * delta) / ley.freepower)
				reqtime = mp.grindvalue * mp.hardness * constants.grind_factor * (1-speedboost)
				if elapsed >= reqtime then
					item:take_item(mp.grindcost)
					inv:set_stack('input',1,item)
					local pw = ItemStack{
						name=mp.powder;
						count=mp.grindvalue;
................................................................................
			metal = name;
		};
	});
	minetest.register_craft {
		output = id;
		recipe = {
			{f,i,f};
			{i,i,i};
			{f,i,f};
		};
	}
end







>
>
>
>










|
<







 







|


|













|

<
<








|
|
|
|


|
>
>
>







 







>







 







>
|
<
|
|
|
|
|
|
|
|
|
|
<
|
>
|
|












<








<



<
<
<



<







<







 







<







 







|







 







|




34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
...
168
169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
184
185
186

187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210

211
212
213



214
215
216

217
218
219
220
221
222
223

224
225
226
227
228
229
230
...
240
241
242
243
244
245
246

247
248
249
250
251
252
253
...
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
...
342
343
344
345
346
347
348
349
350
351
352
353
	
	metal_grindvalue = 4;
	-- how many powders an ingot is worth

	default_grindvalue = 1;
	-- number of items produced when not otherwise
	-- specified

	default_grindcost = 1;
	-- number of items needed to perform a grind when
	-- not otherwise specified
	
	default_hardness = 1;
	-- difficulty to grind an item when not otherwise
	-- specified

	metal_grindcost = 1;
	-- number of metal items needed to perform a grind
}
local mill_formspec_update = function(pos,pct,stat1,stat2)
	-- eventually we'll want to display available
	-- energy here


	-- local meta = minetest.get_meta(pos)
	-- local inv = meta:get_inventory()
	-- local torque = 20
	stat1 = stat1 or 'off'
	minetest.get_meta(pos):set_string('formspec', string.format([[
		size[8,7.2]
................................................................................
	if slot == 'grinder' then
		if minetest.get_item_group(item:get_name(), 'sorcery_mill_grindhead')~=0 
			then return 1 end
	elseif slot == 'input' then
		local metal = sorcery.data.metallookup[item:get_name()]
		local mat = sorcery.matreg.lookup[item:get_name()]
		local comp = sorcery.data.compat.grindables[item:get_name()]
		if metal or (mat and mat.metal) or comp then
			return item:get_count()
		else
			mat = item:get_definition()._matprop
			if mat and mat.grindvalue then
				return item:get_count() 
			end
		end
	end
	return 0
end
local matprops = function(item)
	local metal = sorcery.data.metallookup[item:get_name()]
	if not metal then
		-- allow grinding of armor and tools back to their
		-- original components
		local mat = sorcery.matreg.lookup[item:get_name()]
		if mat and mat.metal then
			metal = mat


		end
	end
	local mp = item:get_definition()._matprop
		or sorcery.data.compat.grindables[item:get_name()]
		or {}

	again = true
	if metal then mp = {
		hardness = mp.hardness or metal.data.hardness;
		grindvalue = ((mp.grindvalue or metal.value) or (metal and constants.metal_grindvalue));
		powder = mp.powder or metal.data.parts.powder;
		grindcost = mp.grindcost or constants.metal_grindcost; -- invariant for metal
	} end

	mp.torque     = constants.grind_torque_factor * mp.hardness
	mp.grindvalue = mp.grindvalue or constants.default_grindvalue
	mp.grindcost  = mp.grindcost  or constants.default_grindcost
	mp.hardness   = mp.hardness   or constants.default_hardness;

	if item:get_wear() ~= 0 then
		-- prevent cheating by recovering metal from items before they
		-- are destroyed
		local wearfac = (item:get_wear() / 65535)
		mp.grindvalue = math.max(1,math.ceil(mp.grindvalue * wearfac))
		mp.hardness = math.max(1,math.ceil(mp.grindcost * wearfac))
................................................................................
minetest.register_node('sorcery:mill',{
	description = 'Mill';
	groups = {
		cracky = 2;
		sorcery_ley_device = 1;
	};
	paramtype2 = 'facedir';
	after_dig_node = sorcery.lib.node.purge_container;
	on_construct = function(pos)
		local meta = minetest.get_meta(pos)
		local inv = meta:get_inventory()
		inv:set_size('input',1)
		inv:set_size('output',4)
		inv:set_size('grinder',2)
		meta:set_float('grindtime',0)
................................................................................
	on_metadata_inventory_move = function(pos,fl,fi,tl,ti)
		if fl == 'input' or tl == 'input' then
			minetest.get_meta(pos):set_float('grindtime',0)
		end
		mill_update(pos)
	end;
	_sorcery = {
		ley = {
			mode = 'consume', affinity = {'praxic'};

			power = function(pos,time)
				local meta = minetest.get_meta(pos)
				local active = meta:get_int('active') == 1
				if not active then return 0 end
				local inv = meta:get_inventory()
				local item = inv:get_stack('input',1)
				if item:is_empty() then
					meta:set_int('active', 0)
					return 0
				end

				return matprops(item).torque * time;
			end;
		};
		on_leychange = mill_update;
	};
	on_timer = function(pos,delta)
		local meta = minetest.get_meta(pos)
		local inv = meta:get_inventory()
		local elapsed = meta:get_float('grindtime') + delta
		local ley = sorcery.ley.netcaps(pos,delta,pos)
		local again = false
		local active = false
		local reqtime -- sigh
		local statcolor = 'off'
		local grinders_on
		if inv:is_empty('input') or inv:is_empty('grinder') then

			elapsed = 0
			mill_formspec_update(pos, 0)
		else
			local item = inv:get_stack('input',1)
			local mp = matprops(item)
			if mp.grindcost > item:get_count() then
				elapsed = 0
				mill_formspec_update(pos, 0)

				goto stop
			end




			if ley.maxpower < (mp.torque * delta) then
				-- not enough potential energy in the system to grind
				-- so don't bother

				statcolor = 'red'
				elapsed = 0 goto stop
			elseif ley.freepower < (mp.torque * delta) then
				-- the net has enough potential energy to supply us,
				-- but too much of it is in use right now. give up
				-- on this round, but try again in a bit to see if
				-- more energy is available

				statcolor = 'yellow'
				elapsed = 0 again = true goto stop
			end

			local grinders = 0
			local grindpower = 0
			local grind_wear = {}
................................................................................
					if dif == 0 then
						wear = constants.grind_wear
					elseif dif < 0 then
						wear = constants.grind_wear * ((dif * -1)/constants.grind_range)
					elseif dif > 0 then
						if dif > constants.grind_grace_range then
							wear = 0

							goto reject
						else
							wear = constants.grind_wear * (1 + (dif/constants.grind_range)) * constants.grind_grace_penalty
						end
					end
					::accept:: grinders = grinders + 1
					           grindpower = grindpower + hh
................................................................................
				if grindpower < mp.hardness then
					statcolor = 'yellow'
				else statcolor='green' end
				grindpower = grindpower / grinders
				-- if there is more power available than needed,
				-- and/or if the blades are stronger than needed,
				-- speed up the grind
				local speedboost = math.max(0.05,((grindpower - mp.hardness)/constants.grind_range) * grinders)
				reqtime = mp.grindvalue * mp.hardness * constants.grind_factor * (1-speedboost)
				if elapsed >= reqtime then
					item:take_item(mp.grindcost)
					inv:set_stack('input',1,item)
					local pw = ItemStack{
						name=mp.powder;
						count=mp.grindvalue;
................................................................................
			metal = name;
		};
	});
	minetest.register_craft {
		output = id;
		recipe = {
			{f,i,f};
			{i,'',i};
			{f,i,f};
		};
	}
end

Modified metallurgy-hot.lua from [3c9b60f14d] to [45fd80ebe6].

294
295
296
297
298
299
300

301
302
303
304
305
306
307
...
376
377
378
379
380
381
382

383
384
385
386
387
388
389
	for _,state in pairs{'open','closed'} do
		local id_closed = id .. '_closed'
		local id_current = (state == 'closed' and id_closed) or id
		local desc = (kind.temp_name and sorcery.lib.str.capitalize(kind.temp_name) .. ' kiln') or 'Kiln'
		minetest.register_node(id_current, {
			description = desc;
			drawtype = "mesh";

			mesh = 'sorcery-kiln-' .. state .. '.obj';
			drop = id;
			groups = {
				cracky = (state == 'open' and 2) or nil;
			};
			sunlight_propagates = true;
			paramtype1 = 'light';
................................................................................
	local id = 'sorcery:smelter_' .. kind.material .. kind.size_name
	kind.id = id
	for _, active in pairs {false, true} do
		minetest.register_node((active and id .. '_active') or id, {
			_proto = kind;
			description = desc;
			drop = id;

			groups = {
				cracky = (active and 2) or nil;
			};
			paramtype2 = 'facedir';
			light_source = (active and 9) or 0;
			on_construct = function(pos)
				local meta = minetest.get_meta(pos)







>







 







>







294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
...
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
	for _,state in pairs{'open','closed'} do
		local id_closed = id .. '_closed'
		local id_current = (state == 'closed' and id_closed) or id
		local desc = (kind.temp_name and sorcery.lib.str.capitalize(kind.temp_name) .. ' kiln') or 'Kiln'
		minetest.register_node(id_current, {
			description = desc;
			drawtype = "mesh";
			after_dig_node = sorcery.lib.node.purge_container;
			mesh = 'sorcery-kiln-' .. state .. '.obj';
			drop = id;
			groups = {
				cracky = (state == 'open' and 2) or nil;
			};
			sunlight_propagates = true;
			paramtype1 = 'light';
................................................................................
	local id = 'sorcery:smelter_' .. kind.material .. kind.size_name
	kind.id = id
	for _, active in pairs {false, true} do
		minetest.register_node((active and id .. '_active') or id, {
			_proto = kind;
			description = desc;
			drop = id;
			after_dig_node = sorcery.lib.node.purge_container;
			groups = {
				cracky = (active and 2) or nil;
			};
			paramtype2 = 'facedir';
			light_source = (active and 9) or 0;
			on_construct = function(pos)
				local meta = minetest.get_meta(pos)

Modified ores.lua from [48691b218f] to [caa4bf5370].

125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
			no_armor = metal.no_armor;
			no_tools = metal.no_tools;
			durability = metal.durability;
			power = metal.power;
			speed = metal.speed;
			artificial = metal.artificial;
			cooktime = metal.cooktime;
			hardness = metal.hardness;

			ingot_image = (metal.image and metal.image.ingot) or nil;
			lump_image = (metal.image and metal.image.lump) or nil;
			armor_weight = metal.armor_weight;
			armor_protection = metal.armor_protection;
		}
	end
	minetest.register_craftitem(fragment, {







|
>







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
			no_armor = metal.no_armor;
			no_tools = metal.no_tools;
			durability = metal.durability;
			power = metal.power;
			speed = metal.speed;
			artificial = metal.artificial;
			cooktime = metal.cooktime;
			hardness = (metal.hardness/8) * 3; -- scaled wrt diamond
			level = math.ceil(((metal.hardness/8) * 3)) + 1;
			ingot_image = (metal.image and metal.image.ingot) or nil;
			lump_image = (metal.image and metal.image.lump) or nil;
			armor_weight = metal.armor_weight;
			armor_protection = metal.armor_protection;
		}
	end
	minetest.register_craftitem(fragment, {

Modified recipes.lua from [0b99f84f61] to [c451769631].

250
251
252
253
254
255
256


























































































































257
258
259
260
261
262
263
	};
	output = "sorcery:infuser";
}


---- altar
-- candles


























































































































minetest.register_craftitem('sorcery:candle', {
	-- TODO make candle node
	inventory_image = 'sorcery_candle.png';
	description = 'Votive Candle';
	groups = {
		candle = 1;
	};







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
	};
	output = "sorcery:infuser";
}


---- altar
-- candles
minetest.register_craftitem('sorcery:core_counterpraxic',{
	description = 'Counterpraxis Core';
	inventory_image = 'sorcery_core_counterpraxic.png';
});
--
minetest.register_craftitem('sorcery:suppression_matrix',{
	description = 'Suppression Matrix';
	inventory_image = 'sorcery_suppression_matrix.png';
});

minetest.register_craftitem('sorcery:inverter_coil',{
	description = 'Inverter Coil';
	inventory_image = 'sorcery_inverter_coil.png';
});

minetest.register_craftitem('sorcery:beam_generator',{
	description = 'Beam Generator';
	inventory_image = 'sorcery_beam_generator.png';
});

minetest.register_craftitem('sorcery:leyline_stabilizer',{
	description = 'Leyline Stabilizer';
	inventory_image = 'sorcery_leyline_stabilizer.png';
});

minetest.register_craftitem('sorcery:field_emitter',{
	description = 'Field Emitter';
	inventory_image = 'sorcery_field_emitter.png';
})

minetest.register_craft {
	output = 'sorcery:leyline_stabilizer';
	recipe = {
		{'basic_materials:copper_wire','group:sorcery_ley_cable','basic_materials:copper_wire'};
		{'sorcery:grease_neutralizing','group:sorcery_ley_cable','sorcery:grease_neutralizing'};
		{'basic_materials:copper_wire','group:sorcery_ley_cable','basic_materials:copper_wire'};
	};
	replacements = {
		{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
		{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
		{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
		{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
		{'sorcery:grease_neutralizing', 'xdecor:bowl'};
		{'sorcery:grease_neutralizing', 'xdecor:bowl'};
	};
}

minetest.register_craft {
	output = 'sorcery:beam_generator';
	recipe = {
		{'xpanes:bar_flat','xpanes:pane_flat','xpanes:bar_flat'};
		{'sorcery:screw_aluminum','sorcery:gem_sapphire','sorcery:screw_aluminum'};
		{'xpanes:bar_flat','sorcery:gem_luxite','xpanes:bar_flat'};
	};
}

minetest.register_craft {
	output = 'sorcery:suppression_matrix';
	recipe = {
		{'sorcery:fragment_electrum','basic_materials:steel_strip','sorcery:fragment_cobalt'};
		{'basic_materials:copper_strip','xpanes:bar_flat','basic_materials:copper_strip'};
		{'sorcery:fragment_cobalt','basic_materials:steel_strip','sorcery:fragment_electrum'};
	};
}

minetest.register_craft {
	output = 'sorcery:core_counterpraxic';
	recipe = {
		{'sorcery:gem_ruby_shard','sorcery:tungsten_ingot','sorcery:gem_ruby_shard'};
		{'sorcery:tungsten_ingot','sorcery:gem_emerald','sorcery:tungsten_ingot'};
		{'sorcery:gem_ruby_shard','sorcery:tungsten_ingot','sorcery:gem_ruby_shard'};
	};
}

minetest.register_craft {
	output = 'sorcery:inverter_coil';
	recipe = {
		{'sorcery:screw_platinum','basic_materials:steel_wire','sorcery:screw_platinum'};
		{'basic_materials:copper_wire','default:tin_ingot','basic_materials:copper_wire'};
		{'sorcery:screw_platinum','basic_materials:steel_wire','sorcery:screw_platinum'};
	};
	replacements = {
		{'basic_materials:steel_wire', 'basic_materials:empty_spool'};
		{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
		{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
		{'basic_materials:steel_wire', 'basic_materials:empty_spool'};
	};
}

minetest.register_craft {
	output = 'sorcery:field_emitter';
	recipe = {
		{'basic_materials:steel_bar','xpanes:pane_flat','basic_materials:steel_bar'};
		{'sorcery:beam_generator','sorcery:leyline_stabilizer','sorcery:beam_generator'};
		{'sorcery:beam_generator','basic_materials:copper_wire','sorcery:beam_generator'};
	};
	replacements = {
		{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
	};
}

minetest.register_craft {
	output = 'sorcery:emitter_barrier';
	recipe = {
		{'sorcery:suppression_matrix','sorcery:suppression_matrix','sorcery:suppression_matrix'};
		{'sorcery:field_emitter','sorcery:core_counterpraxic','sorcery:inverter_coil'};
		{'default:tin_ingot','default:tin_ingot','default:tin_ingot'};
	};
}

minetest.register_craft {
	output = 'sorcery:generator';
	recipe = {
		{'sorcery:aluminum_ingot','sorcery:aluminum_ingot','sorcery:aluminum_ingot'};
		{'default:bronze_ingot','basic_materials:copper_wire','default:bronze_ingot'};
		{'default:steel_ingot','default:furnace','default:steel_ingot'};
	};
	replacements = {
		{'basic_materials:copper_wire', 'basic_materials:empty_spool'};
	};
}

minetest.register_craftitem('sorcery:candle', {
	-- TODO make candle node
	inventory_image = 'sorcery_candle.png';
	description = 'Votive Candle';
	groups = {
		candle = 1;
	};

Added textures/sorcery_beam_generator.png version [58c2e4401c].

cannot compute difference between binary files

Added textures/sorcery_core_counterpraxic.png version [39479c1b28].

cannot compute difference between binary files

Added textures/sorcery_emitter_barrier_bottom.png version [a6363e789d].

cannot compute difference between binary files

Added textures/sorcery_emitter_barrier_front.png version [76e1531fe0].

cannot compute difference between binary files

Added textures/sorcery_emitter_barrier_side.png version [b566c1148a].

cannot compute difference between binary files

Added textures/sorcery_emitter_barrier_top.png version [2705ace703].

cannot compute difference between binary files

Added textures/sorcery_field_emitter.png version [fdceff2168].

cannot compute difference between binary files

Added textures/sorcery_inverter_coil.png version [a40914f554].

cannot compute difference between binary files

Added textures/sorcery_ley_generator_back.png version [aedb4f096c].

cannot compute difference between binary files

Added textures/sorcery_ley_generator_bottom.png version [f23afab857].

cannot compute difference between binary files

Added textures/sorcery_ley_generator_front_off.png version [811455addd].

cannot compute difference between binary files

Added textures/sorcery_ley_generator_front_on.png version [cf5b54dd34].

cannot compute difference between binary files

Added textures/sorcery_ley_generator_side.png version [07ad2d2926].

cannot compute difference between binary files

Added textures/sorcery_ley_generator_top.png version [7147d2203f].

cannot compute difference between binary files

Added textures/sorcery_ley_plug.png version [ac3ad24938].

cannot compute difference between binary files

Added textures/sorcery_ley_switch_base.png version [eca6656993].

cannot compute difference between binary files

Added textures/sorcery_ley_switch_down.png version [5722e6a77c].

cannot compute difference between binary files

Added textures/sorcery_ley_switch_panel.png version [3f4a278966].

cannot compute difference between binary files

Added textures/sorcery_ley_switch_up.png version [3e480e03fd].

cannot compute difference between binary files

Added textures/sorcery_leyline_stabilizer.png version [148e93c8c1].

cannot compute difference between binary files

Added textures/sorcery_suppression_matrix.png version [9c64f4969e].

cannot compute difference between binary files

Modified tnodes.lua from [74f7716464] to [27d6ea2995].

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
			minetest.get_node_timer(pos):start(1)
		end;
		on_timer = function(pos,dtime)
			local meta = minetest.get_meta(pos)
			local elapsed = dtime + meta:get_float('duration') - meta:get_float('timeleft')
			local level = 1 - (elapsed / meta:get_float('duration'))
			local lum = math.ceil(level*meta:get_int('power'))
			print('elapsed time',elapsed)
			print('light level',level)
			print('lum',lum)
			if lum ~= i then
				if lum <= 0 then
					minetest.remove_node(pos)
					return false
				else
					minetest.swap_node(pos,{name='sorcery:air_glimmer_'..tostring(lum)})
				end







<
<
<







15
16
17
18
19
20
21



22
23
24
25
26
27
28
			minetest.get_node_timer(pos):start(1)
		end;
		on_timer = function(pos,dtime)
			local meta = minetest.get_meta(pos)
			local elapsed = dtime + meta:get_float('duration') - meta:get_float('timeleft')
			local level = 1 - (elapsed / meta:get_float('duration'))
			local lum = math.ceil(level*meta:get_int('power'))



			if lum ~= i then
				if lum <= 0 then
					minetest.remove_node(pos)
					return false
				else
					minetest.swap_node(pos,{name='sorcery:air_glimmer_'..tostring(lum)})
				end

Modified wands.lua from [8ffba5fe57] to [12fce408ba].

687
688
689
690
691
692
693

694
695
696
697
698
699
700
			-- meta:set_string('formspec',wandwork_form(false))
		end;
		on_timer = function(...)
			if water then return wandwork_soak(...);
			         else return false end
		end;
		on_rightclick = wandwork_rightclick;

		allow_metadata_inventory_put = function(pos, list, index, stack, user)
			local meta = minetest.get_meta(pos)
			local wwi = meta:get_inventory()
			if list == 'preview' then return 0
			elseif list == 'wandparts' then
				if (not wwi:is_empty('preview')) and
					find_core(stack) then return 1







>







687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
			-- meta:set_string('formspec',wandwork_form(false))
		end;
		on_timer = function(...)
			if water then return wandwork_soak(...);
			         else return false end
		end;
		on_rightclick = wandwork_rightclick;
		after_dig_node = sorcery.lib.node.purge_container;
		allow_metadata_inventory_put = function(pos, list, index, stack, user)
			local meta = minetest.get_meta(pos)
			local wwi = meta:get_inventory()
			if list == 'preview' then return 0
			elseif list == 'wandparts' then
				if (not wwi:is_empty('preview')) and
					find_core(stack) then return 1