sorcery  Check-in [41fdb5b0b8]

Overview
Comment:ui tweaks, rework enchantment slightly
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 41fdb5b0b80615f7ccde49d5172525b73cd4c4511fe2a317b7c00523c4d65deb
User & Date: lexi on 2021-04-21 01:32:38
Other Links: manifest | tags
Context
2021-06-23
09:31
look i don't fucking remember okay check-in: a08f21c56c user: lexi tags: trunk
2021-04-21
01:32
ui tweaks, rework enchantment slightly check-in: 41fdb5b0b8 user: lexi tags: trunk
2020-10-31
19:49
add background noise for condensers (temporary hack, need to write a proper environment sound framework as the fucking env_sounds module is completely impossible to extend), fix a couple of really stupid bugs, make higher-quality phials increase the chance of getting good runes so it's not a complete waste to burn iridium or levitanium powder on making them, add targeted disjunction and some other amulet spells check-in: 15f176a7fe user: lexi tags: trunk
Changes

Modified cookbook.lua from [604c53eb6c] to [e643a56c3c].

29
30
31
32
33
34
35

36
37
38
39
40
41
42
...
152
153
154
155
156
157
158
159

160
161
162

163
164
165
166
167
168
169
170
171
172
173
174
...
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
...
398
399
400
401
402
403
404

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
		dye    = { caption = 'Any Dye',    cnitem = 'dye:black'      };
		bone   = { caption = 'Any Bone',   cnitem = 'bonemeal:bone'  };
		vessel = { caption = 'Any Bottle', cnitem = 'vessels:glass_bottle' };
		flower = { caption = 'Any Flower', cnitem = 'flowers:rose' };
		mushroom = { caption = 'Any Mushroom', cnitem = 'flowers:mushroom_brown' };
		water_bucket = { caption = 'Water Bucket', cnitem = 'bucket:bucket_water' };
		sorcery_ley_cable = { caption = 'Cable', cnitem = 'sorcery:cable_vidrium' };

	};
}
sorcery.cookbook.constants = constants

local slot3x3 = {
	{0,0}, {1,0}, {2,0};
	{0,1}, {1,1}, {2,1};
................................................................................
		desc = string.format("%s (%u)",desc,s:get_count())
	end
	return desc
end;

local bookadjs = { -- sets are in reverse order!
	{'Celestial', 'Divine', 'Inspired', 'Heavenly';
	 'Mystic', 'Diabolic', 'Luminous', 'Forsaken'};


	{'Dark', 'Perfected', 'Flawless', 'Unthinkable';
	 'Impossible', 'Worrisome', 'Unimpeachable'};


	{'Splendid', 'Magnificent', 'Sublime', 'Grand';
	 'Beneficent', 'Mysterious', 'Peculiar', 'Eerie';
	 'Fulsome', 'Fearsome', 'Curious', 'Fascinating';
     'Notorious', 'Infamous'};
}

local cache = {
	populate_grindables = function(cache)
		if not cache.grindables then
			cache.grindables = {}
			for k,v in pairs(minetest.registered_items) do
................................................................................
			local rec = {}
			local en = sorcery.data.enchants[name]
			if not en then return nil end
			en = en.recipe
			for i,e in pairs(en) do
				if e.lens then
					rec[i] = 'sorcery:lens_' .. e.lens .. '_' .. e.gem





				end
			end
			return rec
		end;
		props = function(name)
			return sorcery.data.enchants[name].info or {}













		end;
		slots = {
				{0.5,0};
			{0,1},   {1,1}
		};
		title = function(name) return sorcery.data.enchants[name].name end;
		outdesc = function(name,suffix)
			local e = sorcery.data.enchants[name]


			return sorcery.lib.ui.tooltip {
				title = e.name;

				desc = sorcery.lib.str.capitalize(e.desc);




				color = sorcery.lib.color(e.tone):readable();


			}
		end;
	};
	-- spells = {
	--  booksuf = 'Spellbook';
	--	slots = {
	--		{0,0}, {1,0};
................................................................................
			end
		end
	end
	local img, ot
	if props.note then
		local nx, ny, nw, nh
		if notes_right then

			nx = 5.25 ny = 0
			nw = 4 nh = 3
		else
			nx = 0 ny = 3
			nw = 4 nh = 1
		end
		t = t .. string.format([[
			hypertext[%f,%f;%f,%f;note;<global valign=middle halign=justify size=20>%s]
		]], nx,ny,nw,nh, minetest.formspec_escape(props.note))
	end
	if k.icon then img = k.icon(result) end
	if k.outdesc then ot = k.outdesc(result) else ot = desc_builtin(result) end
		-- image[%f,%f;1,1;gui_furnace_arrow_bg.png^[transformR270]
	return t .. string.format([[
		item_image[%f,%f;1,1;%s]tooltip[%f,%f;1,1;%s]
		]] --[[box[%f,%f;1,1;#850083A0]] .. [[
		%s[%f,%f;1,1;%s]
		tooltip[%f,%f;1,1;%s]
	]], k.w, k.h/2 - 0.5, k.node,
		k.w, k.h/2 - 0.5, minetest.formspec_escape(minetest.registered_nodes[k.node].description),
			-- k.w+1, k.h/2 - 0.5,
		img and 'image' or 'item_image',
			k.w+1.1, k.h/2 - 0.5, minetest.formspec_escape(img or result),
			k.w+1.1, k.h/2 - 0.5, minetest.formspec_escape(ot))
end;

local retrieve_recipe = function(kind,out,notes_right)
	local rec = recipe_kinds[kind]







>







 







|
>


|
>




|







 







>
>
>
>
>





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








>
>


>
|
>
>
>
>
|
>
>







 







>
|
|


|










|




|







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
...
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
...
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
...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
		dye    = { caption = 'Any Dye',    cnitem = 'dye:black'      };
		bone   = { caption = 'Any Bone',   cnitem = 'bonemeal:bone'  };
		vessel = { caption = 'Any Bottle', cnitem = 'vessels:glass_bottle' };
		flower = { caption = 'Any Flower', cnitem = 'flowers:rose' };
		mushroom = { caption = 'Any Mushroom', cnitem = 'flowers:mushroom_brown' };
		water_bucket = { caption = 'Water Bucket', cnitem = 'bucket:bucket_water' };
		sorcery_ley_cable = { caption = 'Cable', cnitem = 'sorcery:cable_vidrium' };
		scissors = { caption = 'Scissors', cnitem = 'sorcery:scissors_steel' };
	};
}
sorcery.cookbook.constants = constants

local slot3x3 = {
	{0,0}, {1,0}, {2,0};
	{0,1}, {1,1}, {2,1};
................................................................................
		desc = string.format("%s (%u)",desc,s:get_count())
	end
	return desc
end;

local bookadjs = { -- sets are in reverse order!
	{'Celestial', 'Divine', 'Inspired', 'Heavenly';
	 'Mystic', 'Diabolic', 'Luminous', 'Forsaken',
	 'Ethereal'};

	{'Dark', 'Perfected', 'Flawless', 'Unthinkable';
	 'Impossible', 'Worrisome', 'Unimpeachable', 'Fulsome',
	 'Wise'};

	{'Splendid', 'Magnificent', 'Sublime', 'Grand';
	 'Beneficent', 'Mysterious', 'Peculiar', 'Eerie';
	 'Fulsome', 'Fearsome', 'Curious', 'Fascinating';
     'Notorious', 'Infamous', 'Wondrous'};
}

local cache = {
	populate_grindables = function(cache)
		if not cache.grindables then
			cache.grindables = {}
			for k,v in pairs(minetest.registered_items) do
................................................................................
			local rec = {}
			local en = sorcery.data.enchants[name]
			if not en then return nil end
			en = en.recipe
			for i,e in pairs(en) do
				if e.lens then
					rec[i] = 'sorcery:lens_' .. e.lens .. '_' .. e.gem
				elseif e.item then
					rec[i] = e.item
				end
				if e.consume or (e.item and not e.dmg) then
					rec[i] = rec[i] .. ' ' .. tostring(e.consume or 1) -- :/
				end
			end
			return rec
		end;
		props = function(name)
			local ench = sorcery.data.enchants[name]
			local p = ench.info
			local desc = ''
			if ench.cost ~= 0 then
				desc = string.format('%s <b>%i</b> thaum-second%s of charge when tool is used',
					ench.cost > 0 and 'Consumes' or 'Generates',
					math.abs(ench.cost),
					ench.cost ~= 1 and 's' or ''
				)
			end
				
			if p == nil then return {note = desc} end
			if p.note   then return p end
			return sorcery.lib.tbl.proto({note = desc},p)	
		end;
		slots = {
				{0.5,0};
			{0,1},   {1,1}
		};
		title = function(name) return sorcery.data.enchants[name].name end;
		outdesc = function(name,suffix)
			local e = sorcery.data.enchants[name]
			local cap = sorcery.lib.str.capitalize
			local aff = sorcery.data.affinities[e.affinity]
			return sorcery.lib.ui.tooltip {
				title = e.name;
				desc = cap(e.desc);
				color = sorcery.lib.color(e.tone);
				props = {
					{
						title = string.format('%s affinity', cap(e.affinity));
						desc = aff.desc;
						color = sorcery.lib.color(aff.color);
					};
				};
			}
		end;
	};
	-- spells = {
	--  booksuf = 'Spellbook';
	--	slots = {
	--		{0,0}, {1,0};
................................................................................
			end
		end
	end
	local img, ot
	if props.note then
		local nx, ny, nw, nh
		if notes_right then
			nx = 5.25 - (3 - k.w) -- :/
			ny = 0
			nw = 4 nh = k.h
		else
			nx = 0 ny = 3
			nw = 4 nh = k,h
		end
		t = t .. string.format([[
			hypertext[%f,%f;%f,%f;note;<global valign=middle halign=justify size=20>%s]
		]], nx,ny,nw,nh, minetest.formspec_escape(props.note))
	end
	if k.icon then img = k.icon(result) end
	if k.outdesc then ot = k.outdesc(result) else ot = desc_builtin(result) end
		-- image[%f,%f;1,1;gui_furnace_arrow_bg.png^[transformR270]
	return t .. string.format([[
		item_image[%f,%f;1,1;%s]tooltip[%f,%f;1,1;%s]
		box[%f,%f;1.1,1.1;#1a001650]
		%s[%f,%f;1,1;%s]
		tooltip[%f,%f;1,1;%s]
	]], k.w, k.h/2 - 0.5, k.node,
		k.w, k.h/2 - 0.5, minetest.formspec_escape(minetest.registered_nodes[k.node].description),
			 k.w+1.05, k.h/2 - 0.55,
		img and 'image' or 'item_image',
			k.w+1.1, k.h/2 - 0.5, minetest.formspec_escape(img or result),
			k.w+1.1, k.h/2 - 0.5, minetest.formspec_escape(ot))
end;

local retrieve_recipe = function(kind,out,notes_right)
	local rec = recipe_kinds[kind]

Modified data/enchants.lua from [5670d710ce] to [f66e8dbb2f].

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
69
70
71
72
73
74
75
76
77
78
79
80



81
82
83
84
85
86
87
..
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
...
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
...
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202
203
204
...
217
218
219
220
221
222
223
224
225
226
227
228
229
		cost = 1;
		tone = {232,102,255};
		desc = 'tools last longer before wearing out';
		affinity = 'counterpraxic';
		groups = allgroups;
		recipe = {
			{lens = 'convex',    gem = 'amethyst', dmg = 2};
			{lens = 'rectifier', gem = 'emerald',  dmg = 4};
			{lens = 'convex',    gem = 'emerald',  dmg = 2};
		};
		apply = function(stack,power,base)
			local caps = table.copy(stack:get_definition().tool_capabilities)
			for g,v in pairs(caps.groupcaps) do
				local unit = base.groupcaps[g].uses * 0.6
				caps.groupcaps[g].uses = v.uses + unit*power
................................................................................
		groups = digtools;
		affinity = 'cognic';
		cost = 1;
		tone = {255,235,195};
		desc = 'Leave a trail of light hanging in the air as you dig';
		recipe = {
			{lens = 'convex',    gem = 'sapphire', dmg = 2};
			{lens = 'concave',   gem = 'ruby',     dmg = 1};
			{lens = 'concave',   gem = 'sapphire', dmg = 1};
		};
		on_dig = function(ctx)
			local chance = 10 -- make dependent on power somehow?
			if math.random(chance) == 1 then
				local lightlevel = math.floor(math.min(minetest.LIGHT_MAX,4*ctx.power))
				-- spawn a light block
................................................................................
		cost = 0; -- energy is only depleted when repair takes place
		tone = {255,84,187};
		affinity = 'syncretic';
		groups = {
			'pick'; 'pickaxe'; 'sword';
		};
		recipe = {
			{lens = 'amplifier', gem = 'ruby',     dmg = 5};
			{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 sorcery.itemclass.get(ctx.node.name,'ore') then
				ctx.tool:add_wear(-(sorcery.enchant.strength(ctx.tool,'harvest') * 2000))
				ctx.cost = 3
................................................................................
		name = 'Conserve';
		tone = {84,255,144};
		cost = 0;
		desc = 'enchantments last longer before running out of power to sustain them';
		groups = allgroups;
		affinity = 'syncretic';
		recipe = {
			{lens = 'rectifier', gem = 'mese',     dmg = 7};
			{lens = 'rectifier', gem = 'sapphire', dmg = 2};
			{lens = 'rectifier', gem = 'amethyst', dmg = 2};
		};
		-- implemented in sorcery/enchanter.lua:register_on_dig
	};
	dowse = { -- send up flare when valuable ores are nearby
		name = 'Dowse';
		tone = {241,251,113};
		cost = 1;
		desc = 'strike colored sparks when used to dig near valuable ore.';
		groups = {'pick','pickaxe'};
		affinity = 'cognic';
		recipe = {
			{lens = 'concave', gem = 'ruby',     dmg = 3};
			{lens = 'concave', gem = 'emerald',  dmg = 3};
			{lens = 'concave', gem = 'sapphire', dmg = 3};
		};
		on_dig = function(ctx)
			local range = 4*sorcery.enchant.strength(ctx.tool,'dowse')
			local colors = {
				['default:stone_with_gold'    ] = {255,234,182};
................................................................................
		name = 'Glitter';
		cost = 10;
		tone = {255,50,60};
		desc = 'dramatically improve your chances of finding gems while mining veins';
		groups = {'pick','pickaxe'};
		affinity = 'entropic';
		recipe = {

			{lens = 'amplifier', gem = 'diamond',  dmg = 12};
			{lens = 'rectifier', gem = 'sapphire', dmg = 9};
			{lens = 'convex',    gem = 'ruby',     dmg = 7};
		};
	};
	pierce = { -- faster mining speed
		name = 'Pierce';
		cost = 3;
		tone = {113,240,251};
		groups = digtools;
		{
			'pick';'pickaxe';'axe';'shovel';'sickle';
		};
		desc = 'rip through solid stone or wood like a hot knife through butter';
		recipe = {
			{lens = 'amplifier', gem = 'diamond',  dmg = 4};
			{lens = 'amplifier', gem = 'ruby',     dmg = 4};
			{lens = 'rectifier', gem = 'diamond',  dmg = 2};
		};
		affinity = 'praxic';
		apply = function(stack,power,base)
			local caps = table.copy(stack:get_definition().tool_capabilities)
			for g,v in pairs(caps.groupcaps) do
				for i,t in pairs(v.times) do
					local unit = base.groupcaps[g].times[i] * 0.15
................................................................................
	};
	rend = { -- more damage / mine higher level blocks
		name = 'Rend';
		affinity = 'praxic';
		tone = {251,203,113};
		groups = {'sword';'pick';'pickaxe';};
		recipe = {
			{lens = 'convex',    gem = 'mese',    dmg = 3};
			{lens = 'amplifier', gem = 'emerald', dmg = 7};

			{lens = 'amplifier', gem = 'diamond', dmg = 7};
		};
		cost = 5;
		desc = 'cleave through sturdy ores and tear mortal flesh with fearsome ease';
		apply = function(stack,power,base)
			local caps = table.copy(stack:get_definition().tool_capabilities)
			for g,v in pairs(caps.groupcaps) do
				local unit = 2
................................................................................
	sanctify = {
		desc = 'prolong the blessings of the heavens';
		groups = {'sorcery_sanctify'};
		affinity = 'entropic';
		tone = {255,255,255};
		cost = 7;
		recipe = {
			{lens = 'amplifier', gem = 'ruby', dmg = 13};
			{lens = 'amplifier', gem = 'ruby', dmg = 15};
			{lens = 'amplifier', gem = 'ruby', dmg = 18};
		};
	};
}







|







 







|







 







|
|
|


>
>
>







 







|













|







 







>


<







<
<
<




|







 







<

>
|







 







|





16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
..
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
...
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
...
188
189
190
191
192
193
194

195
196
197
198
199
200
201
202
203
204
...
217
218
219
220
221
222
223
224
225
226
227
228
229
		cost = 1;
		tone = {232,102,255};
		desc = 'tools last longer before wearing out';
		affinity = 'counterpraxic';
		groups = allgroups;
		recipe = {
			{lens = 'convex',    gem = 'amethyst', dmg = 2};
			{item = 'default:obsidian_shard'};
			{lens = 'convex',    gem = 'emerald',  dmg = 2};
		};
		apply = function(stack,power,base)
			local caps = table.copy(stack:get_definition().tool_capabilities)
			for g,v in pairs(caps.groupcaps) do
				local unit = base.groupcaps[g].uses * 0.6
				caps.groupcaps[g].uses = v.uses + unit*power
................................................................................
		groups = digtools;
		affinity = 'cognic';
		cost = 1;
		tone = {255,235,195};
		desc = 'Leave a trail of light hanging in the air as you dig';
		recipe = {
			{lens = 'convex',    gem = 'sapphire', dmg = 2};
			{item = 'sorcery:gem_luxite_shard'};
			{lens = 'concave',   gem = 'sapphire', dmg = 1};
		};
		on_dig = function(ctx)
			local chance = 10 -- make dependent on power somehow?
			if math.random(chance) == 1 then
				local lightlevel = math.floor(math.min(minetest.LIGHT_MAX,4*ctx.power))
				-- spawn a light block
................................................................................
		cost = 0; -- energy is only depleted when repair takes place
		tone = {255,84,187};
		affinity = 'syncretic';
		groups = {
			'pick'; 'pickaxe'; 'sword';
		};
		recipe = {
			{lens = 'amplifier', gem = 'ruby', dmg = 5};
			{item = 'sorcery:powder_tungsten'};
			{item = 'sorcery:extract_rye'};
		};
		desc = 'some damage is repaired when used to mine ore or kill an attacker';
		info = {
			note = 'Consumes <b>3</b> thaum-seconds of charge when repair takes place';
		};
		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 sorcery.itemclass.get(ctx.node.name,'ore') then
				ctx.tool:add_wear(-(sorcery.enchant.strength(ctx.tool,'harvest') * 2000))
				ctx.cost = 3
................................................................................
		name = 'Conserve';
		tone = {84,255,144};
		cost = 0;
		desc = 'enchantments last longer before running out of power to sustain them';
		groups = allgroups;
		affinity = 'syncretic';
		recipe = {
			{item = 'default:mese_crystal_fragment'};
			{lens = 'rectifier', gem = 'sapphire', dmg = 2};
			{lens = 'rectifier', gem = 'amethyst', dmg = 2};
		};
		-- implemented in sorcery/enchanter.lua:register_on_dig
	};
	dowse = { -- send up flare when valuable ores are nearby
		name = 'Dowse';
		tone = {241,251,113};
		cost = 1;
		desc = 'strike colored sparks when used to dig near valuable ore.';
		groups = {'pick','pickaxe'};
		affinity = 'cognic';
		recipe = {
			{item = 'sorcery:gem_luxite'};
			{lens = 'concave', gem = 'emerald',  dmg = 3};
			{lens = 'concave', gem = 'sapphire', dmg = 3};
		};
		on_dig = function(ctx)
			local range = 4*sorcery.enchant.strength(ctx.tool,'dowse')
			local colors = {
				['default:stone_with_gold'    ] = {255,234,182};
................................................................................
		name = 'Glitter';
		cost = 10;
		tone = {255,50,60};
		desc = 'dramatically improve your chances of finding gems while mining veins';
		groups = {'pick','pickaxe'};
		affinity = 'entropic';
		recipe = {
			{item = 'sorcery:oil_luck'};
			{lens = 'amplifier', gem = 'diamond',  dmg = 12};
			{lens = 'rectifier', gem = 'sapphire', dmg = 9};

		};
	};
	pierce = { -- faster mining speed
		name = 'Pierce';
		cost = 3;
		tone = {113,240,251};
		groups = digtools;



		desc = 'rip through solid stone or wood like a hot knife through butter';
		recipe = {
			{lens = 'amplifier', gem = 'diamond',  dmg = 4};
			{lens = 'amplifier', gem = 'ruby',     dmg = 4};
			{item = 'default:flint'};
		};
		affinity = 'praxic';
		apply = function(stack,power,base)
			local caps = table.copy(stack:get_definition().tool_capabilities)
			for g,v in pairs(caps.groupcaps) do
				for i,t in pairs(v.times) do
					local unit = base.groupcaps[g].times[i] * 0.15
................................................................................
	};
	rend = { -- more damage / mine higher level blocks
		name = 'Rend';
		affinity = 'praxic';
		tone = {251,203,113};
		groups = {'sword';'pick';'pickaxe';};
		recipe = {

			{lens = 'amplifier', gem = 'emerald', dmg = 7};
			{item = 'flowers:flower_rose'};
			{item = 'sorcery:powder_silver'};
		};
		cost = 5;
		desc = 'cleave through sturdy ores and tear mortal flesh with fearsome ease';
		apply = function(stack,power,base)
			local caps = table.copy(stack:get_definition().tool_capabilities)
			for g,v in pairs(caps.groupcaps) do
				local unit = 2
................................................................................
	sanctify = {
		desc = 'prolong the blessings of the heavens';
		groups = {'sorcery_sanctify'};
		affinity = 'entropic';
		tone = {255,255,255};
		cost = 7;
		recipe = {
			{item = 'sorcery:holy_water'};
			{lens = 'amplifier', gem = 'ruby', dmg = 15};
			{lens = 'amplifier', gem = 'ruby', dmg = 18};
		};
	};
}

Modified data/metals.lua from [3560b06dd9] to [81e12129e0].

392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
		artificial=true;
		meltpoint = 5;
		cooktime = 120;
		hardness = 8;
		maxconduct = 15;
		sharpness = 5;
		level = 2;
		speed = 1.7;
		maxenergy = 2200;
		durability = 1500;
		slots = {
			{affinity={'praxic'},confluence=3};
			{affinity={'syncretic'},confluence=2};
		};
		sinter = {







|







392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
		artificial=true;
		meltpoint = 5;
		cooktime = 120;
		hardness = 8;
		maxconduct = 15;
		sharpness = 5;
		level = 2;
		speed = 2.5;
		maxenergy = 2200;
		durability = 1500;
		slots = {
			{affinity={'praxic'},confluence=3};
			{affinity={'syncretic'},confluence=2};
		};
		sinter = {

Modified data/oils.lua from [6619d22587] to [1e67dfd074].

105
106
107
108
109
110
111
112










113


		mix = {
			'sorcery:extract_greengrass';
			'sorcery:extract_grape';
			'farming:cocoa_beans';
			'farming:sugar';
			'farming:sugar';
		};
	};










}










>
>
>
>
>
>
>
>
>
>
|
>
>
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
		mix = {
			'sorcery:extract_greengrass';
			'sorcery:extract_grape';
			'farming:cocoa_beans';
			'farming:sugar';
			'farming:sugar';
		};
	};
	luck = {
		color = {156,54,255};
		style = 'sparkle';
		mix = {
			'sorcery:extract_marram';
			'farming:hemp_leaf';
			'farming:hemp_oil';
			'xdecor:honey';
			'farming:salt';
			'farming:salt';
		};
	};
}

Modified data/runes.lua from [56e196edd6] to [92db18c27d].

140
141
142
143
144
145
146





147
148
149
150
151
152
153
...
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
				cast = function(ctx)
					local target = minetest.get_player_by_name(ctx.meta:get_string('rune_join_target'))
					if not target then return false end

					local subjects if ctx.amulet.frame == 'cobalt' then
						if ctx.target.type ~= 'object' then return false end
						subjects = {{ref=ctx.target.ref}}





					else subjects = {{ref=ctx.caster}} end

					local delay = math.max(5,11 - ctx.stats.power) + 2.3*(math.random()*2-1)
					local color = sorcery.lib.color(117,38,237)
					teleport(ctx,subjects,delay,target:get_pos(),color)
					if ctx.amulet.frame == 'gold' then
						teleport(ctx,{{ref=target}},delay,ctx.caster:get_pos())
................................................................................
				mingrade = 4;
				name = 'Duplication';
				desc = 'Bring an exact twin of any object or item into existence, no matter how common or rare it might be';
				cast = function(ctx)
					local color = sorcery.lib.color(255,61,205)
					local dup, sndpos, anchor, sbj, ty
					if ctx.target.type == 'object' and ctx.target.ref:get_luaentity().name == '__builtin:item' then
						-- sorcery.vfx.imbue(color, ctx.target.ref) -- causes graphics card problems???
						sndpos = 'subjects'
						sbj = {{player = ctx.target.ref}}
						local item = ItemStack(ctx.target.ref:get_luaentity().itemstring)
						local r = function() return math.random() * 2 - 1 end
						local putpos = vector.offset(ctx.target.ref:get_pos(), r(), 1, r())
						dup = function()
							item:set_count(1) -- nice try bouge-san







>
>
>
>
>







 







|







140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
...
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
				cast = function(ctx)
					local target = minetest.get_player_by_name(ctx.meta:get_string('rune_join_target'))
					if not target then return false end

					local subjects if ctx.amulet.frame == 'cobalt' then
						if ctx.target.type ~= 'object' then return false end
						subjects = {{ref=ctx.target.ref}}
					elseif ctx.amulet.frame == 'iridium' then
						subjects = {}
						for _,o in pairs(minetest.get_objects_inside_radius(ctx.caster:get_pos(), ctx.stats.power)) do
							subjects[#subjects+1] = {player = o}
						end
					else subjects = {{ref=ctx.caster}} end

					local delay = math.max(5,11 - ctx.stats.power) + 2.3*(math.random()*2-1)
					local color = sorcery.lib.color(117,38,237)
					teleport(ctx,subjects,delay,target:get_pos(),color)
					if ctx.amulet.frame == 'gold' then
						teleport(ctx,{{ref=target}},delay,ctx.caster:get_pos())
................................................................................
				mingrade = 4;
				name = 'Duplication';
				desc = 'Bring an exact twin of any object or item into existence, no matter how common or rare it might be';
				cast = function(ctx)
					local color = sorcery.lib.color(255,61,205)
					local dup, sndpos, anchor, sbj, ty
					if ctx.target.type == 'object' and ctx.target.ref:get_luaentity().name == '__builtin:item' then
						sorcery.vfx.imbue(color, ctx.target.ref) -- causes graphics card problems???
						sndpos = 'subjects'
						sbj = {{player = ctx.target.ref}}
						local item = ItemStack(ctx.target.ref:get_luaentity().itemstring)
						local r = function() return math.random() * 2 - 1 end
						local putpos = vector.offset(ctx.target.ref:get_pos(), r(), 1, r())
						dup = function()
							item:set_count(1) -- nice try bouge-san

Modified data/spells.lua from [dffc43152b] to [2d770c6227].

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
...
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
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
...
583
584
585
586
587
588
589

590
591
592
593
594
595
596
...
624
625
626
627
628
629
630

631
632
633
634
635
636
637
	end
	return r[math.random(#r)]
end
local anchorwand = function(aff,uses,recipe)
	local affcolor = sorcery.lib.color(sorcery.data.affinities[aff].color)
	return {
		name = aff .. ' anchor';
		desc = 'With an enchanter, anchor ' .. aff .. ' spells into an object to enable it to produce preternatural effects';
		uses = uses;
		affinity = recipe;
		color = affcolor;
		sound = 'xdecor_enchanting'; -- FIXME make own
		cast = function(ctx)
			local node = target_node(ctx, 'sorcery:enchanter')
			if not node then return false end
................................................................................
					local proto = stack:get_definition()._proto
					if proto.kind ~= spec.lens or proto.gem ~= spec.gem
						then return false end
				elseif spec.item then
					default_mode = 'consume'
					if stack:get_name() ~= spec.item then
						return false end


				else
					return false
				end

				local mode
				if spec.dmg then mode = 'dmg'
				elseif spec.consume then mode = 'consume'
				else mode = default_mode end

				if mode == 'dmg' then
					stack:add_wear((spec.dmg or 1) * 1000)
					return stack
				elseif mode == 'consume' then

					stack:take_item(spec.consume or 1)








					return stack
				end
			end
			for ench,data in pairs(sorcery.data.enchants) do
				if data.affinity ~= aff or data.recipe == nil then goto skip end
				local newinv = {}
				for s,v in pairs(data.recipe) do
................................................................................
			bolt:get_luaentity()._blastradius = radius
			bolt:set_velocity(vel)
		end;
	};
	seal = {
		name = 'sealing';
		color = {255,238,16};
		uses = 32;
		desc = 'Bind an object to your spirit such that it will be rendered impregnable to others, or break a sealing created with this same wand';
		leytype = 'imperic';
		affinity = {'pine','dark'};
		cast = function(ctx)
			if ctx.target == nil or ctx.target.type ~= 'node' then return false end
			local meta = minetest.get_meta(ctx.target.under)
			-- first we need to check if the wand has an identifying 'key' yet,
................................................................................
		uses = 128;
		desc = 'Reveal the strength and affinities of the local leyline';
		cast = function(ctx)
			local color = ctx.base.gem == 'sapphire';
			local duration = (ctx.base.gem == 'amethyst' and 4) or 2;
			local ley = sorcery.ley.estimate(ctx.caster:get_pos())

			local strength = ley.force
			if color then
				strength = strength / #ley.aff
				for _,a in pairs(ley.aff) do
					cast_sparkle(ctx,sorcery.lib.color(sorcery.data.affinities[a].color):brighten(1.3), strength, duration * strength)
				end
			else
				cast_sparkle(ctx,sorcery.lib.color(250,255,185), strength, duration*strength)
			end

		end;
	};
................................................................................
			end
			newenergy = math.min(maxcharge, newenergy * (ctx.stats.power or 1))

			sorcery.ley.setcharge(rechargee,charge + newenergy)
			e:set_stack('item',1,rechargee)

			enchantment_sparkle(ctx, sorcery.lib.color(212,6,63))

		end;
	};
	transfer = {
		name = 'transfer';
		uses = 65;
		color = {6,212,121};
		leytype = 'syncretic';
................................................................................
				if ctx.base.gem == 'sapphire'
					then e.spells = {} e.energy = 0
					else table.remove(e.spells, math.random(#e.spells))
				end
			end
			sorcery.enchant.set(item,e)
			ei:set_stack('item',1,item)

			enchantment_sparkle(ctx,sorcery.lib.color(255,154,44))
			enchantment_sparkle(ctx,sorcery.lib.color(226,44,255))
		end;
	};
	divine = {
		name = 'divining';
		desc = 'Steal away the secrets of the cosmos';







|







 







>
>













>

>
>
>
>
>
>
>
>







 







|







 







|



|







 







>







 







>







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
...
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
...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
...
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
...
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
	end
	return r[math.random(#r)]
end
local anchorwand = function(aff,uses,recipe)
	local affcolor = sorcery.lib.color(sorcery.data.affinities[aff].color)
	return {
		name = aff .. ' anchor';
		desc = 'Destroy items on an enchanter and channel their essence with enchanting lenses to anchor ' .. aff .. ' spells into an object, enabling it to produce preternatural effects';
		uses = uses;
		affinity = recipe;
		color = affcolor;
		sound = 'xdecor_enchanting'; -- FIXME make own
		cast = function(ctx)
			local node = target_node(ctx, 'sorcery:enchanter')
			if not node then return false end
................................................................................
					local proto = stack:get_definition()._proto
					if proto.kind ~= spec.lens or proto.gem ~= spec.gem
						then return false end
				elseif spec.item then
					default_mode = 'consume'
					if stack:get_name() ~= spec.item then
						return false end
					if spec.consume and stack:get_count() < spec.consume then
						return false end
				else
					return false
				end

				local mode
				if spec.dmg then mode = 'dmg'
				elseif spec.consume then mode = 'consume'
				else mode = default_mode end

				if mode == 'dmg' then
					stack:add_wear((spec.dmg or 1) * 1000)
					return stack
				elseif mode == 'consume' then
					local r = sorcery.register.residue.db[stack:get_name()]
					stack:take_item(spec.consume or 1)
					if r then
						local rs = ItemStack(r) 
						rs:set_count(rs:get_count() * (spec.consume or 1))
						if stack:is_empty()
							then stack = rs
							else minetest.add_item(ctx.target.above, rs)
						end
					end
					return stack
				end
			end
			for ench,data in pairs(sorcery.data.enchants) do
				if data.affinity ~= aff or data.recipe == nil then goto skip end
				local newinv = {}
				for s,v in pairs(data.recipe) do
................................................................................
			bolt:get_luaentity()._blastradius = radius
			bolt:set_velocity(vel)
		end;
	};
	seal = {
		name = 'sealing';
		color = {255,238,16};
		uses = 128;
		desc = 'Bind an object to your spirit such that it will be rendered impregnable to others, or break a sealing created with this same wand';
		leytype = 'imperic';
		affinity = {'pine','dark'};
		cast = function(ctx)
			if ctx.target == nil or ctx.target.type ~= 'node' then return false end
			local meta = minetest.get_meta(ctx.target.under)
			-- first we need to check if the wand has an identifying 'key' yet,
................................................................................
		uses = 128;
		desc = 'Reveal the strength and affinities of the local leyline';
		cast = function(ctx)
			local color = ctx.base.gem == 'sapphire';
			local duration = (ctx.base.gem == 'amethyst' and 4) or 2;
			local ley = sorcery.ley.estimate(ctx.caster:get_pos())

			local strength = ley.force * 4 * ley.force
			if color then
				strength = strength / #ley.aff
				for _,a in pairs(ley.aff) do
					cast_sparkle(ctx,sorcery.lib.color(sorcery.data.affinities[a].color):brighten(1.3), strength, duration * (strength*0.5))
				end
			else
				cast_sparkle(ctx,sorcery.lib.color(250,255,185), strength, duration*strength)
			end

		end;
	};
................................................................................
			end
			newenergy = math.min(maxcharge, newenergy * (ctx.stats.power or 1))

			sorcery.ley.setcharge(rechargee,charge + newenergy)
			e:set_stack('item',1,rechargee)

			enchantment_sparkle(ctx, sorcery.lib.color(212,6,63))
			sorcery.enchant.update_enchanter(ctx.target.under)
		end;
	};
	transfer = {
		name = 'transfer';
		uses = 65;
		color = {6,212,121};
		leytype = 'syncretic';
................................................................................
				if ctx.base.gem == 'sapphire'
					then e.spells = {} e.energy = 0
					else table.remove(e.spells, math.random(#e.spells))
				end
			end
			sorcery.enchant.set(item,e)
			ei:set_stack('item',1,item)
			sorcery.enchant.update_enchanter(ctx.target.under)
			enchantment_sparkle(ctx,sorcery.lib.color(255,154,44))
			enchantment_sparkle(ctx,sorcery.lib.color(226,44,255))
		end;
	};
	divine = {
		name = 'divining';
		desc = 'Steal away the secrets of the cosmos';

Modified entities.lua from [9e257faffc] to [5b6d6366ce].

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
	age = u.marshal.t.u32;
	lastemit = u.marshal.t.u32;
}

minetest.register_entity('sorcery:spell_projectile_flamebolt',{
	initial_properties = {
		visual = "sprite";
        use_texture_alpha = true;
		textures = {'sorcery_fireball.png'};
		visual_size = { x = 2, y = 2, z = 2 };
		physical = true;
		collide_with_objects = true;
		pointable = false;
		glow = 14;
		static_save = false;







|







3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
	age = u.marshal.t.u32;
	lastemit = u.marshal.t.u32;
}

minetest.register_entity('sorcery:spell_projectile_flamebolt',{
	initial_properties = {
		visual = "sprite";
        use_texture_alpha = 'blend';
		textures = {'sorcery_fireball.png'};
		visual_size = { x = 2, y = 2, z = 2 };
		physical = true;
		collide_with_objects = true;
		pointable = false;
		glow = 14;
		static_save = false;

Deleted hotmetallurgy.lua version [ac0bc31ba3].

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
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
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
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
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
-- alloying furnace
--
-- there are several kinds of alloy furnace, with varying
-- capabilities. the simplest can alloy two simple metals;
-- the most complex can alloy four high-grade metals such
-- as titanium, platinum, iridium, or levitanium.
--
-- furnace recipes follow a pattern: the number of crucibles
-- determines the input slots, the type of crucible determines
-- how hot the furnace can get, and various other components
-- (like a coolant circulator) can be added to allow the
-- creation of exotic metals.
--
-- alloy furnaces produce ingots that can later be melted down
-- again and cast into a mold to produce items of particular
-- shapes.

-- there are five kinds of crucibles: clay, aluminum, platinum,
-- duridium, and impervium. clay crucibles are made by molding
-- clay into the proper shape and then firing it in a furnace.
-- others are made by casting.

local fragments_per_ingot = 4 

for _, c in pairs { 'clay', 'aluminum', 'platinum', 'duranium' } do
	minetest.register_craftitem('sorcery:crucible_' .. c, {
		description = sorcery.lib.str.capitalize(c .. ' crucible');
		inventory_image = 'sorcery_crucible_' .. c .. '.png';
	})
end

minetest.register_craftitem('sorcery:crucible_clay_molding', {
	description = sorcery.lib.str.capitalize('Crucible molding');
	inventory_image = 'sorcery_crucible_clay_molding.png';
})

minetest.register_craft {
	recipe = {
		{ 'default:clay_lump', '', 'default:clay_lump'};
		{ 'default:clay_lump', '', 'default:clay_lump'};
		{ 'default:clay_lump', 'default:clay_lump', 'default:clay_lump'};
	};
	output = 'sorcery:crucible_clay_molding';
}
minetest.register_craft {
	type = 'shapeless';
	recipe = { 'sorcery:crucible_clay_molding' };
	output = 'default:clay_lump 7';
}
minetest.register_craft {
	type = 'cooking';
	recipe = 'sorcery:crucible_clay_molding';
	cooktime = 40;
	output = 'sorcery:crucible_clay';
}

local burn_layout = function(v)
	local layouts = {
		[1] = {w = 1, h = 1}; [2] = {w = 2, h = 1}; [3] = {w = 3, h = 1};
		[4] = {w = 2, h = 2}; [5] = {w = 3, h = 2}; [6] = {w = 3, h = 2};
		[7] = {w = 4, h = 2}; [8] = {w = 4, h = 2}; [9] = {w = 3, h = 3};
	}
	local inpos  = { x = 2.8, y = 1 }
	local insize = layouts[v.in]
	local outpos = { x = 5.2, y = 2.4 }
	local outsize = layouts[v.out]
	local fuelpos = { x = 2.8, y = 3.4 }
	local fuelsize = layouts[v.fuel]
	return string.format([[
		list[context;input;%f,%f;%f,%f;]
		list[context;output;%f,%f;%f,%f;]
		list[context;fuel;%f,%f;%f,%f;]
		
		image[2.3,1.9;1,1;default_furnace_fire_bg.png^[lowpart:%u%%:default_furnace_fire_fg.png]
		image[3.2,1.9;1,1;gui_furnace_arrow_bg.png^[lowpart:%u%%:gui_furnace_arrow_fg.png^[transformR270]

		listring[context;output] listring[current_player;main]
		listring[context;input]  listring[current_player;main]
		listring[context;fuel]   listring[current_player;main]
	]], 

	--input
		inpos.x - insize.w/2, -- pos
		inpos.y - insize.h/2, 
		insize.w, insize.h, -- size
	--output
		outpos.x - outsize.w/2, -- pos
		outpos.y - outsize.h/2, 
		outsize.w, outsize.h, -- size
	--fuel
		fuelpos.x - fuelsize.w/2, -- pos
		fuelpos.y - fuelsize.h/2, 
		fuelsize.w, fuelsize.h, -- size

		v.burn, v.prog
	)
end

local kiln_formspec = function(kind, fuel_progress, cook_progress)
	return [[
		size[8,8]
		list[current_player;main;0,4.2;8,4]
		list[context;wax;0,2;1,1]
	]] .. burn_layout{
		in = kind.size^2;
		out = kind.outsize;
		fuel = kind.fuelsize;
		burn = fuel_progress;
		prog = cook_progress;
	}
end

local smelter_formspec = function(kind, fuel_progress, smelt_progress)
	return [[
		size[8,8]
		list[current_player;main;0,4.2;8,4]
	]] .. burn_layout {
		in = kind.size;
		out = kind.outsize;
		fuel = kind.fuelsize;
		burn = fuel_progress;
		prog = smelt_progress;
	}
end

local find_recipe = function(inv)
	local mix = {}
	local count = 0
	for i=1,inv:get_size('input') do
		local m = inv:get_stack('input',i)
		if m:is_empty() then goto skip end
		local l = sorcery.data.metallookup[m:get_name()]
		if not l then return false end
		mix[l.id] = (mix[l.id] or 0) + l.value
		count = count + l.value
	::skip::end
	-- everything is metal, we've finished summing it up.
	-- let's see if the assembled items match the ratio
	-- specified in any of the smelting recipes.
	local matches = 0
	for _,rec in pairs(sorcery.data.alloys) do
		local fac = nil
		local meltpoint = 1
		if rec.metals == nil then goto skip_recipe end
		for metal, ratio in pairs(rec.metals) do
			if mix[metal] and mix[metal] % ratio == 0 then
				if fac then
					if mix[metal] / ratio ~= fac then goto skip_recipe end
				else fac = math.floor(mix[metal] / ratio) end
				local m = sorcery.data.metals[metal]
				if m.meltpoint then
					meltpoint = math.max(meltpoint, m.meltpoint) end
			else goto skip_recipe end
		end
		do return rec, count, fac, meltpoint end
	::skip_recipe::end
	return false
end

local update_smelter = function(pos)
	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()
	local proto = minetest.registered_nodes[minetest.get_node(pos).name]._proto
	local recipe, count, factor, meltpoint = find_recipe(inv)
	if recipe --[[and proto.temp >= meltpoint]] then
		minetest.get_node_timer(pos):start(1)
	else
		meta:set_float('burntime',0)
	end
end

local smelter_step = function(kind,active,pos,delta)
	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()
	local recipe, count, factor = find_recipe(inv)
	local cooktime
	local elapsed = meta:get_float('burntime') + delta
	meta:set_float('burnleft',meta:get_float('burnleft') - delta)
	if (not active) and (not recipe) then return false end
	if meta:get_float('burnleft') <= 0 or not active then
		if recipe then
			local burn, frep = minetest.get_craft_result {
				method = 'fuel', width = 1;
				items = { inv:get_stack('fuel',1) };
			}
			if burn.time == 0 then goto nofuel end
			inv:set_stack('fuel', 1, frep.items[1])
			meta:set_float('burnleft',burn.time)
			meta:set_float('burnmax',burn.time)
			if not active then
				minetest.swap_node(pos,
					sorcery.lib.tbl.merge(minetest.get_node(pos), {
						name = kind.id .. '_active'
					}))
				active = true
			end
		else goto nofuel end
	end

	if not recipe then goto update end
	
	cooktime = ((recipe.cooktime / fragments_per_ingot) * count) / factor
	if elapsed >= cooktime then
		elapsed = 0
		-- remove used items
		for i=1,inv:get_size('input') do
			local s = inv:get_stack('input',i)
			if s:is_empty() then goto skip end
			s:take_item(1) inv:set_stack('input',i,s)
		::skip::end

		local outstack
		if count % fragments_per_ingot == 0 then
			outstack = ItemStack {
				name = sorcery.data.metals[recipe.output].ingot or 'sorcery:' .. recipe.output .. '_ingot';
				count = count / fragments_per_ingot;
			}
		else
			outstack = ItemStack {
				name = 'sorcery:fragment_' .. recipe.output;
				count = count;
			}
		end

		local leftover = inv:add_item('output',outstack)
		if not leftover:is_empty() then
			minetest.add_item(pos, leftover)
		end
	end

	::update::
		meta:set_float('burntime',elapsed)
		meta:set_string('formspec', smelter_formspec(kind,
			math.min(1, meta:get_float('burnleft') /
						meta:get_float('burnmax')
					) * 100, -- fuel
			(cooktime and math.min(1, elapsed / cooktime) * 100) or 0 -- smelt
		))
		do return active end

	::nofuel::
		if active then
			minetest.swap_node(pos,
				sorcery.lib.tbl.merge(minetest.get_node(pos), { name = kind.id }))
		end
		meta:set_float('burnleft',0) -- just in case
	::noburn::
		meta:set_float('burntime',0) -- just in case
		meta:set_string('formspec', smelter_formspec(kind, 0, 0))
		return false
end
local register_kiln = function(kind)
	local box = {
		open = {
			type = 'fixed';
			fixed = {
				-0.5,-0.5,-0.5,
				 0.5, 1.6, 0.5
			};
		};
		closed = {
			type = 'fixed';
			fixed = {
				-0.5,-0.5,-0.5,
				 0.5, 0.7, 0.5
			};
		};
	}
	local id = 'sorcery:kiln_' .. kind.material
	local metal = sorcery.data.metals[kind.material]
	-- use some iffy heuristics to guess the texture
	local metaltex
	if kind.material == 'clay' then
		metaltex = 'default_brick.png';
	else
		metaltex = (metal.img and metal.img.block) or
			(metal.ingot and 'default_' .. kind.material .. '_block.png') or
			sorcery.lib.image('default_steel_block.png'):multiply(sorcery.lib.color(metal.tone)):render();
	end
	local tex = {
		open = {
			'default_furnace_front.png';
			metaltex;
			'default_copper_block.png';
			'default_stone.png';
		};
		closed = {
			'default_furnace_front_active.png';
			'default_copper_block.png';
			metaltex;
			'default_stone.png';
		};
	};
	for _,state in pairs{'open','closed'} do
		local id_closed = id .. '_closed'
		local id_current = (state == 'closed' and id_closed) or id
		local desc = sorcery.lib.str.capitalize(kind.temp_name) .. ' kiln';
		minetest.register_node(id_current, {
			_active = (state == 'closed');
			_proto = kind;
			description = desc;
			drawtype = "mesh";
			mesh = 'sorcery-kiln-' .. state .. '.obj';
			drop = id;
			groups = { cracky = 2; sorcery_device_kiln = (state == 'closed') and 1 or 2; }
			sunlight_propagates = true;
			paramtype1 = 'light';
			paramtype2 = 'facedir';
			selection_box = box[state];
			collision_box = box[state];
			tiles = tex[state];
			light_source = (state == 'closed' and 7) or 0;
			on_construct = function(pos)
				local meta = minetest.get_meta(pos)
				local inv = meta:get_inventory()
				inv:set_size('input', kind.size^2)
				inv:set_size('output', kind.outsize)
				inv:set_size('fuel', kind.fuelsize)
				inv:set_size('wax', 1)

				meta:set_float('burnleft',0)
				meta:set_float('burnmax',0)
				meta:set_float('burntime',0)

				meta:set_string('infotext',desc)
				meta:set_string('formspec',kiln_formspec(kind,0,0))
			end;
			on_timer = function(pos,delta)
			end;

			on_metadata_inventory_put = function(pos)
				minetest.get_node_timer(pos):start(1) end;
			on_metadata_inventory_move = function(pos)
				minetest.get_node_timer(pos):start(1) end;
			on_metadata_inventory_take = function(pos)
				minetest.get_node_timer(pos):start(1) end;
		})
	end
	local ingot
	if kind.material == 'clay' then
		ingot = 'default:clay_brick';
	else
		ingot = metal.ingot or 'sorcery:' .. kind.material .. '_ingot'
	end
	minetest.register_craft = {
		output = id_open;
		recipe = {
			{'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'};
			{ingot,'',ingot};
			{ingot,'default:furnace',ingot};
		};
	}
end

local register_smelter = function(kind)
	local recipe = {{},{};
		{'default:stone','default:furnace','default:stone'};
	} do
		local on = kind.crucible
		local ti = 'default:tin_ingot'
		local cu = 'default:copper_ingot'
		local crucmap = {
			[2] = { {cu,cu,cu}, {on,ti,on} };
			[3] = { {cu,on,cu}, {on,ti,on} };
			[4] = { {on,cu,on}, {on,ti,on} };
			[5] = { {on,cu,on}, {on,on,on} };
			[6] = { {on,on,on}, {on,on,on} };
		};
		for y=1,2 do recipe[y] = crucmap[kind.size][y] end
	end
	
	local desc = 'smelter';
	if kind.temp_name then desc = kind.temp_name .. ' ' .. desc end
	if kind.size_name then desc = kind.size_name .. ' ' .. desc end
	desc = sorcery.lib.str.capitalize(desc);
	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 = 2;
				sorcery_device_smelter = active and 1 or 2;
			};
			paramtype2 = 'facedir';
			light_source = (active and 9) or 0;
			on_construct = function(pos)
				local meta = minetest.get_meta(pos)
				local inv = meta:get_inventory()
				inv:set_size('input',kind.size)
				inv:set_size('output',kind.outsize)
				inv:set_size('fuel',kind.fuelsize)
				meta:set_string('infotext', desc)
				meta:set_string('formspec', smelter_formspec(kind, 0, 0))
				meta:set_float('burnleft',0)
				meta:set_float('burnmax',0)
				meta:set_float('burntime',0)
			end;
			on_metadata_inventory_put = update_smelter;
			on_metadata_inventory_move = update_smelter;
			on_metadata_inventory_take = update_smelter;
			on_timer = function(pos,delta) return smelter_step(kind, active, pos, delta) end;
			-- allow_metadata_inventory_put = function(pos, listname, index, stack, player)
			-- end;
			tiles = {
				'sorcery_smelter_top_' .. tostring(kind.size) .. '.png';
				'sorcery_smelter_bottom.png';
				'sorcery_smelter_side.png';
				'sorcery_smelter_side.png';
				'sorcery_smelter_side.png';
				'sorcery_smelter_front' .. ((active and '_hot') or '') .. '.png';
			};
		})
	end
	minetest.register_craft {
		recipe = recipe;
		output = id;
	}
end

for _, t in pairs {
	{1, nil, 'clay'};
	-- {2, 'hot','aluminum'};
	-- {3, 'volcanic','platinum'};
	-- {4, 'stellar','duridium'};
	-- {5, 'nova','impervium'};
} do register_kiln {
		temp = t[1], temp_name = t[2];
		material = t[3];
		size = 3, outsize = 4, fuelsize = 1; -- future-proofing
	}

	for _, s in pairs {
		{2, 'small'};
		{3, 'large'};
		{4, 'grand'};
	} do register_smelter {
		size = s[1], size_name = s[2];
		temp = t[1], temp_name = t[2];
		material = t[3];
		crucible = 'sorcery:crucible_' .. t[3];
		outsize = 4, fuelsize = 1; -- future-proofing
	} end
end
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































































































































































































































































































































































































































































































































































































































































































































































































































































Modified init.lua from [98e23bb4d1] to [b6f042d41b].

99
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
root {'compat','matreg'}
if not sorcery.stage('loadlore', data, root) then
	data {
		'compat';
		'affinities'; 'gods';
		'calendar', 'signs';
		'resonance';
		'enchants', 'spells', 'runes';
		'gems', 'metals';

		'potions', 'oils', 'greases',
			'draughts', 'elixirs',
			'philters', 'extracts';
	}
end

sorcery.load('registration') do







<

>







99
100
101
102
103
104
105

106
107
108
109
110
111
112
113
114
root {'compat','matreg'}
if not sorcery.stage('loadlore', data, root) then
	data {
		'compat';
		'affinities'; 'gods';
		'calendar', 'signs';
		'resonance';

		'gems', 'metals';
		'enchants', 'spells', 'runes';
		'potions', 'oils', 'greases',
			'draughts', 'elixirs',
			'philters', 'extracts';
	}
end

sorcery.load('registration') do

Modified leylines.lua from [a8ee2829a5] to [3f1f974e2b].

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
		name = 'Condenser sound effects';
		nodenames = {'sorcery:condenser'};
		neighbors = {'group:sorcery_ley_device'};
		interval = 5.6, chance = 1, catch_up = false;
		action = function(pos)
			local force = sorcery.ley.estimate(pos).force
			minetest.sound_play('sorcery_condenser_bg', {
				pos = pos, max_hear_distance = 5 + 8*force, gain = force*0.3;
			})
		end;
	}
end

minetest.register_craft {
	output = 'sorcery:condenser';







|







376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
		name = 'Condenser sound effects';
		nodenames = {'sorcery:condenser'};
		neighbors = {'group:sorcery_ley_device'};
		interval = 5.6, chance = 1, catch_up = false;
		action = function(pos)
			local force = sorcery.ley.estimate(pos).force
			minetest.sound_play('sorcery_condenser_bg', {
				pos = pos, max_hear_distance = 5 + 4*force, gain = force*0.3;
			})
		end;
	}
end

minetest.register_craft {
	output = 'sorcery:condenser';

Modified lib/color.lua from [818d3ded97] to [9a1b399926].

160
161
162
163
164
165
166
167
168
169







170
171
172
173
174
175
176
...
181
182
183
184
185
186
187
188

189
190
191
192
193
194
195
				-- print("r"..self.red.."g"..self.green.."b"..self.blue.." is h"..hue.."s"..saturation.."l"..luminosity)
				local temp = from_hsl({hue=hue,saturation=saturation,luminosity=luminosity},self.alpha)
				-- print("back is r"..temp.red.."g"..temp.green.."b"..temp.blue)
				return { hue = hue, saturation = saturation, luminosity = luminosity }
			end;

			readable = function(self, target)
				target = target or 0.5
				local hsl = self:to_hsl()
				hsl.luminosity = target







				return from_hsl(hsl, self.alpha)
			end;

			bg = function(self, text) return
				text .. minetest.get_background_escape_sequence(self:hex())
			end;

................................................................................
			brighten = function(self, fac)
				-- Use HSL to brighten
				-- To HSL
				local hsl = self:to_hsl()
				-- Do the calculation, clamp to 0-1 instead of the clamp fn
				hsl.luminosity = math.min(math.max(hsl.luminosity * fac, 0), 1)
				-- Turn back into RGB color
				local t = from_hsl(hsl, self.alpha)

				-- print("brighten is r"..t.red.."g"..t.green.."b"..t.blue)
				return from_hsl(hsl, self.alpha)
			end;

			darken = warp(function(new, fac)
				-- TODO: is there any point to this being different than brighten? Probably especially not now.
				new.red = clip(new.red - (new.red * fac))







|


>
>
>
>
>
>
>







 







|
>







160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
				-- print("r"..self.red.."g"..self.green.."b"..self.blue.." is h"..hue.."s"..saturation.."l"..luminosity)
				local temp = from_hsl({hue=hue,saturation=saturation,luminosity=luminosity},self.alpha)
				-- print("back is r"..temp.red.."g"..temp.green.."b"..temp.blue)
				return { hue = hue, saturation = saturation, luminosity = luminosity }
			end;

			readable = function(self, target)
				target = target or 0.6
				local hsl = self:to_hsl()
				hsl.luminosity = target
				local worstHue = 230
				local nearness = math.abs(worstHue - hsl.hue)
				if nearness <= 70 then
					local boost = 1.0 - (nearness / 70)
					hsl.luminosity = math.min(1, hsl.luminosity * (1 + (boost*0.4)))
				end

				return from_hsl(hsl, self.alpha)
			end;

			bg = function(self, text) return
				text .. minetest.get_background_escape_sequence(self:hex())
			end;

................................................................................
			brighten = function(self, fac)
				-- Use HSL to brighten
				-- To HSL
				local hsl = self:to_hsl()
				-- Do the calculation, clamp to 0-1 instead of the clamp fn
				hsl.luminosity = math.min(math.max(hsl.luminosity * fac, 0), 1)
				-- Turn back into RGB color
				-- local t = from_hsl(hsl, self.alpha)
				-- print("darker is r"..hsl.red.."g"..hsl.green.."b"..hsl.blue)
				-- print("brighten is r"..t.red.."g"..t.green.."b"..t.blue)
				return from_hsl(hsl, self.alpha)
			end;

			darken = warp(function(new, fac)
				-- TODO: is there any point to this being different than brighten? Probably especially not now.
				new.red = clip(new.red - (new.red * fac))

Added lib/hud.lua version [a485497e38].

























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
-- the HUD library provides an easy-to-use interface for create custom
-- item UIs. the idea is that an item <id> will define one or more UI layouts
-- in its _sorcery.hud property, then call lib.hud.open(id, layout, player, ...)
-- when it wishes to open <layout>, usually from its on_use handler. the library
-- will then iterate through the elements in its definition, transmit them to
-- the user, and store the resulting handles in a user context. it will also
-- register any timer callbacks needed to ensure a timely update of the item.
-- if the item is removed from the hotbar, any UI associated with it will be
-- closed.
--
-- each UI element can have a function that is called to set its value. if
-- this is present, it will be called at initialization and at update.
--
-- example:
--	_sorcery = {
--		hud = {
--			powerlevel = {
--				period = 0.1;
--				elements = {
--					{kind = 'text',
--						text = function(ctx)
--							return ctx.stack:get_meta():get_int("power")
--						end;
--					};
--				};
--			};
--		};
--	};

Modified lib/tbl.lua from [6f943d189b] to [d4f49ab351].

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
			new[k] = fn.deepcopy(v)
		else
			new[k] = v
		end
	end
	return new
end









fn.merge = function(base,override)
	local new = fn.copy(base)
	for k,v in pairs(override) do
		new[k] = v
	end
	return new
end

fn.deepmerge = function(base,override,func)
	local new = {}
	local keys = fn.merge(fn.keys(base),fn.keys(override))
	for _,k in pairs(keys) do
		if type(base[k]) == 'table' and
		   type(override[k]) == 'table' then
			new[k] = fn.deepmerge(base[k], override[k], func)
		elseif func and override[k] and base[k] then
			new[k] = func(base[k],override[k], k)
		elseif override[k] then
			new[k] = override[k]
		else
			new[k] = base[k]
		end
	end
	return new
end

fn.append = function(r1, r2)
	local new = fn.copy(r1)
	for i=1,#r2 do
		new[#new + 1] = r2[i]
	end
	return new
end

fn.has = function(tbl,value,eqfn)
	for k,v in pairs(tbl) do
		if eqfn then
			if eqfn(v,value,tbl) then return true, k end







>
>
>
>
>
>
>
>











|












<
<
<
<
<
<
<
<







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
			new[k] = fn.deepcopy(v)
		else
			new[k] = v
		end
	end
	return new
end

fn.append = function(r1, r2)
	local new = fn.copy(r1)
	for i=1,#r2 do
		new[#new + 1] = r2[i]
	end
	return new
end

fn.merge = function(base,override)
	local new = fn.copy(base)
	for k,v in pairs(override) do
		new[k] = v
	end
	return new
end

fn.deepmerge = function(base,override,func)
	local new = {}
	local keys = fn.append(fn.keys(base),fn.keys(override))
	for _,k in pairs(keys) do
		if type(base[k]) == 'table' and
		   type(override[k]) == 'table' then
			new[k] = fn.deepmerge(base[k], override[k], func)
		elseif func and override[k] and base[k] then
			new[k] = func(base[k],override[k], k)
		elseif override[k] then
			new[k] = override[k]
		else
			new[k] = base[k]
		end
	end








	return new
end

fn.has = function(tbl,value,eqfn)
	for k,v in pairs(tbl) do
		if eqfn then
			if eqfn(v,value,tbl) then return true, k end

Modified lib/ui.lua from [a001c55363] to [787cef01b1].

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
local l = sorcery.lib
local dui = sorcery.data.ui

return {
	tooltip = function(a)
		local color = a.color
		if color == nil then color = l.color(136,158,177) end
		local str = a.title
		if a.desc then
			str = str .. '\n' .. color:fmt(minetest.wrap_text(a.desc,60))
		end
		if a.props then
			-- str = str .. '\n'
			for _,prop in pairs(a.props) do
				local c
				if prop.color and l.color.id(prop.color) then
					c = prop.color
				elseif dui.colors[prop.affinity] then
					c = l.color(dui.colors[prop.affinity])
				else
					c = l.color(dui.colors.neutral)
				end

				str = str .. '\n ' .. c:fmt('* ')

				if prop.title then
					str = str .. c:brighten(1.3):fmt(prop.title) .. ': '
				end

				local lines = minetest.wrap_text(prop.desc, 50, true)
				str = str .. c:fmt(lines[1])
				for i=2,#lines do
					str = str .. '\n' .. string.rep(' ',5) .. c:fmt(lines[i])
				end
			end
		end
		return str
	end;
}





|










|












|






|


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
local l = sorcery.lib
local dui = sorcery.data.ui

return {
	tooltip = function(a)
		local color = a.color and a.color:readable()
		if color == nil then color = l.color(136,158,177) end
		local str = a.title
		if a.desc then
			str = str .. '\n' .. color:fmt(minetest.wrap_text(a.desc,60))
		end
		if a.props then
			-- str = str .. '\n'
			for _,prop in pairs(a.props) do
				local c
				if prop.color and l.color.id(prop.color) then
					c = prop.color:readable()
				elseif dui.colors[prop.affinity] then
					c = l.color(dui.colors[prop.affinity])
				else
					c = l.color(dui.colors.neutral)
				end

				str = str .. '\n ' .. c:fmt('* ')

				if prop.title then
					str = str .. c:brighten(1.3):fmt(prop.title) .. ': '
				end

				local lines = minetest.wrap_text(prop.desc, 55, true)
				str = str .. c:fmt(lines[1])
				for i=2,#lines do
					str = str .. '\n' .. string.rep(' ',5) .. c:fmt(lines[i])
				end
			end
		end
		return color:darken(0.8):bg(str)
	end;
}

Modified metallurgy-cold.lua from [0187673a26] to [3417cf14d8].

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
	mp.torque     = constants.grind_torque_factor * mp.hardness
	mp.grindvalue = mp.grindvalue or constants.default_grindvalue
	mp.grindcost  = mp.grindcost  or constants.default_grindcost

	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))
		mp.torque = math.max(1,math.ceil(mp.torque * wearfac))
	end

	return mp
end







|







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
	mp.torque     = constants.grind_torque_factor * mp.hardness
	mp.grindvalue = mp.grindvalue or constants.default_grindvalue
	mp.grindcost  = mp.grindcost  or constants.default_grindcost

	if item:get_wear() ~= 0 then
		-- prevent cheating by recovering metal from items before they
		-- are destroyed
		local wearfac = 1-(item:get_wear() / 65535)
		mp.grindvalue = math.max(1,math.ceil(mp.grindvalue * wearfac))
		mp.hardness = math.max(1,math.ceil(mp.grindcost * wearfac))
		mp.torque = math.max(1,math.ceil(mp.torque * wearfac))
	end

	return mp
end

Modified portal.lua from [12b64fac56] to [149a85d413].

358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
							time = 2;
							amount = 500 * fac;
							minpos = { x = -0.3, y =    0, z = -0.3 };
							maxpos = { x =  0.3, y =  1.5, z =  0.3 };
							minvel = { x = -0.3, y =  0.4, z = -0.3 };
							maxvel = { x =  0.3, y =  0.6, z =  0.3 };
							maxacc = { x =    0, y =  0.5, z =    0 };
							texture = sorcery.lib.image('sorcery_spark.png'):multiply(sorcery.lib.color(255,144,226)):render();
							minexptime = 1.5;
							maxexptime = 2;
							minsize = 0.4;
							maxsize = 1.6 * fac;
							glow = 14;
							attached = u.object;
							animation = {







|







358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
							time = 2;
							amount = 500 * fac;
							minpos = { x = -0.3, y =    0, z = -0.3 };
							maxpos = { x =  0.3, y =  1.5, z =  0.3 };
							minvel = { x = -0.3, y =  0.4, z = -0.3 };
							maxvel = { x =  0.3, y =  0.6, z =  0.3 };
							maxacc = { x =    0, y =  0.5, z =    0 };
							texture = sorcery.lib.image('sorcery_sputter.png'):glow(sorcery.lib.color(255,144,226)):render();
							minexptime = 1.5;
							maxexptime = 2;
							minsize = 0.4;
							maxsize = 1.6 * fac;
							glow = 14;
							attached = u.object;
							animation = {

Modified potions.lua from [f87e1ae861] to [2e6379fa76].

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
sorcery.register_potion = function(name,label,desc,color,imgvariant,glow,extra)
	local image = 'sorcery_liquid_'..(imgvariant or 'dull')..'.png' .. 
		'^[multiply:'..tostring(color)..
		'^vessels_glass_bottle.png'

	sorcery.register.residue.link('sorcery:' .. name, 'vessels:glass_bottle')
	local node = {
		description = color:darken(0.8):bg(
			sorcery.lib.ui.tooltip {
				title = label;
				desc = desc;
				color = color:readable();
			}
			-- label .. (desc and ("\n" .. color:readable():fmt(desc)) or '')
		);
		short_description = label;
		drawtype = "plantlike";
		tiles = {image};
		inventory_image = image;
		paramtype = "light";
		is_ground_content = false;
		light_source = glow and math.min(minetest.LIGHT_MAX,glow) or 0;







|




|

|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
sorcery.register_potion = function(name,label,desc,color,imgvariant,glow,extra)
	local image = 'sorcery_liquid_'..(imgvariant or 'dull')..'.png' .. 
		'^[multiply:'..tostring(color)..
		'^vessels_glass_bottle.png'

	sorcery.register.residue.link('sorcery:' .. name, 'vessels:glass_bottle')
	local node = {
		description = --color:darken(0.8):bg(
			sorcery.lib.ui.tooltip {
				title = label;
				desc = desc;
				color = color:readable();
			};
			-- label .. (desc and ("\n" .. color:readable():fmt(desc)) or '')
		--);
		short_description = label;
		drawtype = "plantlike";
		tiles = {image};
		inventory_image = image;
		paramtype = "light";
		is_ground_content = false;
		light_source = glow and math.min(minetest.LIGHT_MAX,glow) or 0;

Modified runeforge.lua from [4e5c236dfa] to [6c2e235473].

1
2
3
4
5

6
7
8
9
10
11
12
..
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58
59
...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
...
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
-- TODO make some kind of disposable "filter" tool that runeforges require 
-- to generate runes and that wears down over time, to make amulets more
-- expensive than they currently are? the existing system is neat but
-- i think amulets are a little overpowered for something that just
-- passively consumes ley-current


local constants = {
	rune_mine_interval = 240;
	-- how often a powered forge rolls for new runes

	rune_cache_max = 4;
	-- how many runes a runeforge can hold at a time
................................................................................
	local fac = (g-1) / 5
	return i - ((i*0.5) * fac), 0.5 * fac
end
sorcery.register.runes.foreach('sorcery:generate',{},function(name,rune)
	local id = 'sorcery:rune_' .. name
	rune.image = rune.image or string.format('sorcery_rune_%s.png',name)
	rune.item = id

	minetest.register_craftitem(id, {
		description = sorcery.lib.color(rune.tone):readable():fmt(rune.name .. ' Rune');
		short_description = rune.name .. ' Rune';
		inventory_image = rune.image;
		stack_max = 1;
		groups = {
			sorcery_rune = 1;
			not_in_creative_inventory = 1;
		};
................................................................................
		local rp = rune:get_definition()._proto
		local rg = rune:get_meta():get_int('rune_grade')
		m:set_string('amulet_rune', rp.id)
		m:set_int('amulet_rune_grade', rg)
		local spell = sorcery.amulet.getspell(stack)
		if not spell then return nil end
		local name
		if spell.minrune then -- indicating quality makes less sense if it's restricted
			name = string.format('Amulet of %s', spell.name)
		else
			name = string.format('Amulet of %s %s', constants.amulet_grades[rg], spell.name)
		end
		m:set_string('description', sorcery.lib.ui.tooltip {
			title = name;
			color = spell.tone;
................................................................................
		local i = m:get_inventory()
		i:set_size('cache',constants.rune_cache_max)
		i:set_size('wrench',1) i:set_size('phial',1) i:set_size('refuse',1)
		i:set_size('amulet',1) i:set_size('active',1)
		m:set_string('infotext','Rune Forge')
		runeforge_update(pos)
	end;
	after_dig_node = sorcery.lib.node.purge_only {'amulet'};
	on_timer = runeforge_update;
	on_metadata_inventory_move = function(pos, fl,fi, tl,ti, count, user)
		local inv = minetest.get_meta(pos):get_inventory()
		local wrench if not inv:is_empty('wrench') then
			wrench = inv:get_stack('wrench',1):get_definition()._proto
		end
		local wwear = function(cap)





>







 







>

|







 







|







 







|







1
2
3
4
5
6
7
8
9
10
11
12
13
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
...
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
-- TODO make some kind of disposable "filter" tool that runeforges require 
-- to generate runes and that wears down over time, to make amulets more
-- expensive than they currently are? the existing system is neat but
-- i think amulets are a little overpowered for something that just
-- passively consumes ley-current
--  -- are phials & rune-wrenches enough for this now?

local constants = {
	rune_mine_interval = 240;
	-- how often a powered forge rolls for new runes

	rune_cache_max = 4;
	-- how many runes a runeforge can hold at a time
................................................................................
	local fac = (g-1) / 5
	return i - ((i*0.5) * fac), 0.5 * fac
end
sorcery.register.runes.foreach('sorcery:generate',{},function(name,rune)
	local id = 'sorcery:rune_' .. name
	rune.image = rune.image or string.format('sorcery_rune_%s.png',name)
	rune.item = id
	local c = sorcery.lib.color(rune.tone)
	minetest.register_craftitem(id, {
		description = c:darken(0.7):bg(c:readable():fmt(rune.name .. ' Rune'));
		short_description = rune.name .. ' Rune';
		inventory_image = rune.image;
		stack_max = 1;
		groups = {
			sorcery_rune = 1;
			not_in_creative_inventory = 1;
		};
................................................................................
		local rp = rune:get_definition()._proto
		local rg = rune:get_meta():get_int('rune_grade')
		m:set_string('amulet_rune', rp.id)
		m:set_int('amulet_rune_grade', rg)
		local spell = sorcery.amulet.getspell(stack)
		if not spell then return nil end
		local name
		if spell.mingrade and spell.mingrade > 0 then -- indicating quality makes less sense if it's restricted
			name = string.format('Amulet of %s', spell.name)
		else
			name = string.format('Amulet of %s %s', constants.amulet_grades[rg], spell.name)
		end
		m:set_string('description', sorcery.lib.ui.tooltip {
			title = name;
			color = spell.tone;
................................................................................
		local i = m:get_inventory()
		i:set_size('cache',constants.rune_cache_max)
		i:set_size('wrench',1) i:set_size('phial',1) i:set_size('refuse',1)
		i:set_size('amulet',1) i:set_size('active',1)
		m:set_string('infotext','Rune Forge')
		runeforge_update(pos)
	end;
	after_dig_node = sorcery.lib.node.purge_only {'amulet','wrench'};
	on_timer = runeforge_update;
	on_metadata_inventory_move = function(pos, fl,fi, tl,ti, count, user)
		local inv = minetest.get_meta(pos):get_inventory()
		local wrench if not inv:is_empty('wrench') then
			wrench = inv:get_stack('wrench',1):get_definition()._proto
		end
		local wwear = function(cap)

Modified vfx.lua from [423ee7c3cf] to [d0158e43bb].

10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
..
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
		and function(x) return vector.add(pos,x) end
		or  function(x) return x end
	local height = caster:get_properties().eye_height
	minetest.add_particlespawner {
		amount = 70 * strength;
		time = duration or 1.5;
		attached = caster;
		texture = sorcery.lib.image('sorcery_spark.png'):multiply(color):render();

		minpos = ofs({ x =  0.0, z =  0.6, y =  height*0.7});
		maxpos = ofs({ x =  0.4, z =  0.2, y =  height*1.1});
		minvel = { x = -0.5, z = -0.5, y = -0.5};
		maxvel = { x =  0.5, z =  0.5, y =  0.5};
		minacc = { x =  0.0, z =  0.0, y =  0.5};
		maxacc = { x =  0.0, z =  0.0, y =  0.5};
		minsize = 0.4, maxsize = 0.8;
................................................................................
end

sorcery.vfx.enchantment_sparkle = function(tgt,color)
	local minvel, maxvel
	if minetest.get_node(vector.add(tgt.under,{y=1,z=0,x=0})).name == 'air' then
		minvel = {x=0,z=0,y= 0.3}  maxvel = {x=0,z=0,y= 1.5};
	else
		local dir = vector.subtract(tgt.under,tgt.above)
		minvel = vector.multiply(dir, 0.3)
		maxvel = vector.multiply(dir, 1.2)
	end
	return minetest.add_particlespawner {
		amount = 50;
		time = 0.5;
		minpos = vector.subtract(tgt.under, 0.5);







|
>







 







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
		and function(x) return vector.add(pos,x) end
		or  function(x) return x end
	local height = caster:get_properties().eye_height
	minetest.add_particlespawner {
		amount = 70 * strength;
		time = duration or 1.5;
		attached = caster;
		-- texture = sorcery.lib.image('sorcery_spark.png'):multiply(color):render();
		texture = sorcery.vfx.glowspark(color):render();
		minpos = ofs({ x =  0.0, z =  0.6, y =  height*0.7});
		maxpos = ofs({ x =  0.4, z =  0.2, y =  height*1.1});
		minvel = { x = -0.5, z = -0.5, y = -0.5};
		maxvel = { x =  0.5, z =  0.5, y =  0.5};
		minacc = { x =  0.0, z =  0.0, y =  0.5};
		maxacc = { x =  0.0, z =  0.0, y =  0.5};
		minsize = 0.4, maxsize = 0.8;
................................................................................
end

sorcery.vfx.enchantment_sparkle = function(tgt,color)
	local minvel, maxvel
	if minetest.get_node(vector.add(tgt.under,{y=1,z=0,x=0})).name == 'air' then
		minvel = {x=0,z=0,y= 0.3}  maxvel = {x=0,z=0,y= 1.5};
	else
		local dir = vector.subtract(tgt.above,tgt.under)
		minvel = vector.multiply(dir, 0.3)
		maxvel = vector.multiply(dir, 1.2)
	end
	return minetest.add_particlespawner {
		amount = 50;
		time = 0.5;
		minpos = vector.subtract(tgt.under, 0.5);

Modified wands.lua from [e661ef77a3] to [3776b1d392].

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
...
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
...
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
			end
			return proto
		end;
		matprops = function(proto)
			local matprops = {}
			for k,v in pairs(proto) do
				if sorcery.wands.materials[k] then
					local mp = sorcery.wands.materials[k].wandprops
					if mp then
						matprops = sorcery.lib.tbl.deepmerge(matprops, mp,
							function(a,b,k)
								if key == 'bond'
									then return a+b
									else return a*b
								end
							end)
					end
				end
................................................................................
			angle = user:get_look_horizontal();
			eyeheight = uprops.eye_height;
		};
		wearmult = 1;
	}
	local result = castfn(context)
	if result ~= false then
		minetest.sound_play(sorcery.data.spells[spell].sound or "default_item_smoke", { --FIXME make own sounds
			pos = user:get_pos();
			gain = 0.8;
		})
		-- minetest.add_particle {
		-- 	pos = vector.add(vector.add(user:get_pos(), vector.multiply(user:get_look_dir(),1.1)), {y=1.6,z=0,x=0});
		-- 	velocity = user:get_velocity();
		-- 	expirationtime = 0.5;
................................................................................
		sunlight_propagates = true;
		paramtype = 'light';
		paramtype2 = 'facedir';
		tiles = images;
		selection_box = hitbox;
		collision_box = hitbox;
		after_dig_node = sorcery.lib.node.purge_container;
		use_texture_alpha = true;
		on_construct = function(pos)
			local meta = minetest.get_meta(pos)
			local inv = meta:get_inventory()
			inv:set_size('wand', 1)
			update_stand_info(pos)
		end;
		_proto = {







|


|







 







|







 







|







159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
...
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
...
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
			end
			return proto
		end;
		matprops = function(proto)
			local matprops = {}
			for k,v in pairs(proto) do
				if sorcery.wands.materials[k] then
					local mp = sorcery.wands.materials[k][v].wandprops
					if mp then
						matprops = sorcery.lib.tbl.deepmerge(matprops, mp,
							function(a,b,key)
								if key == 'bond'
									then return a+b
									else return a*b
								end
							end)
					end
				end
................................................................................
			angle = user:get_look_horizontal();
			eyeheight = uprops.eye_height;
		};
		wearmult = 1;
	}
	local result = castfn(context)
	if result ~= false then
		minetest.sound_play(sorcery.data.spells[spell].sound or "sorcery_chime", { --FIXME make better sound
			pos = user:get_pos();
			gain = 0.8;
		})
		-- minetest.add_particle {
		-- 	pos = vector.add(vector.add(user:get_pos(), vector.multiply(user:get_look_dir(),1.1)), {y=1.6,z=0,x=0});
		-- 	velocity = user:get_velocity();
		-- 	expirationtime = 0.5;
................................................................................
		sunlight_propagates = true;
		paramtype = 'light';
		paramtype2 = 'facedir';
		tiles = images;
		selection_box = hitbox;
		collision_box = hitbox;
		after_dig_node = sorcery.lib.node.purge_container;
		use_texture_alpha = 'blend';
		on_construct = function(pos)
			local meta = minetest.get_meta(pos)
			local inv = meta:get_inventory()
			inv:set_size('wand', 1)
			update_stand_info(pos)
		end;
		_proto = {