starlit  Diff

Differences From Artifact [bdd1907d1f]:

To Artifact [a2443e45a4]:


12
13
14
15
16
17
18


19
20
21
22
23
24
25
..
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
...
204
205
206
207
208
209
210

































































211
212
213
214
215
216
217
--    * used for determining quantities. that is,
--			f*x = spec to make x instances of f
--
--    new fab fields must be defined in starlit.type.fab.fields.
--    this maps a name to fn(a,b,n) -> quant, where a is the first
--    argument, b is a compounding amount, and n is a quantity of
--    items to produce. fields that are unnamed will be underwritten



local function fQuant(a,b,n) return ((a or 0)+(b or 0))*n end
local function fFac  (a,b,n)
	if a == nil and b == nil then return nil end
	local f if a == nil or b == nil then
		f = a or b
	else
................................................................................
end
local function fReq  (a,b,n) return a or b         end
local function fFlag (a,b,n) return a and b        end
local function fSize (a,b,n) return math.max(a,b)  end

local F = string.format
local lib = starlit.mod.lib






















































local fields = {
	-- fabrication eligibility will be determined by which kinds
	-- of input a particular fabricator can introduce. e.g. a
	-- printer with a  but no cache can only print items whose
	-- recipe only names elements as ingredients
	element = {
		name = {"element", "elements"};
		string = function(x, n, long)
			local el = starlit.world.material.element.db[x]
			return lib.math.si('g', n) .. ' ' .. ((not long and el.sym) or el.name)
		end;
		image = function(x, n)
			return string.format('starlit-element-%s.png', x)
		end;

		op = fQuant;
	};
	metal ={
		name = {"metal", "metals"};
		string = function(x, n)
			local met = starlit.world.material.metal.db[x]
			return lib.math.si('g', n) .. ' ' .. met.name
		end;
		image = function(x, n)
			local met = starlit.world.material.metal.db[x]
			return ItemStack(met.form.ingot):get_definition().inventory_image
		end;

		op = fQuant;
	};
	liquid = {
		name = {"liquid", "liquids"};
		string = function(x, n)
			local liq = starlit.world.material.liquid.db[x]
			return lib.math.si('L', n) .. ' ' .. liq.name
		end;

		op = fQuant;
	};
	gas = {
		name = {"gas", "gasses"};
		string = function(x, n)
			local gas = starlit.world.material.gas.db[x]
			return lib.math.si('g', n) .. ' ' .. gas.name
		end;

		op = fQuant;
	};
-- 	crystal = {
-- 		op = fQuant;
-- 	};
	item = {
		name = {"item", "items"};
		string = function(x, n)
			local i = minetest.registered_items[x]
			return tostring(n) .. 'x ' .. i.short_description
		end;









	};

	-- factors



	cost = {op=fFac}; -- units vary






















	time = {op=fFac}; -- (s)
		-- print: base printing time
	size = {op=fSize};
		-- printBay: size of the printer bay necessary to produce the item
	req  = {op=fReq};
	flag = {op=fFlag}; -- means that can be used to produce the item & misc flags
		-- print: allow production with a printer
		-- smelt: allow production with a smelter
	-- all else defaults to underwrite
}

local order = {
	'element', 'metal', 'liquid', 'gas', 'item'

}

local lib = starlit.mod.lib

local fab fab = lib.class {
	__name = 'starlit:fab';
	
	fields = fields;
	order = order;
	construct = function(q) return q end;
	__index = {
		elementalize = function(self)
			local e = fab {element = self.element or {}}
			for _, kind in pairs {'metal', 'gas', 'liquid'} do
				for m,mass in pairs(self[kind] or {}) do
					local mc = starlit.world.material[kind][m].composition
					e = e + mc:elementalize()*mass
				end
			end
			return e
		end;

		elementSeq = function(self)
................................................................................
				if next(t) then table.insert(all, {
					id=o, list=t;
					header=fields[o].name[t[2] and 2 or 1];
				}) end
			end
			return all
		end;

































































	};

	__tostring = function(self)
		local t = {}
		for i,o in ipairs(order) do
			if self[o] and fields[o].string then
				for mat,amt in pairs(self[o]) do







>
>







 







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










|




>






|



|

>






|

>






|

>











>
>
>
>
>
>
>
>
>




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












|
>




|










|







 







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







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
..
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
...
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
--    * used for determining quantities. that is,
--			f*x = spec to make x instances of f
--
--    new fab fields must be defined in starlit.type.fab.fields.
--    this maps a name to fn(a,b,n) -> quant, where a is the first
--    argument, b is a compounding amount, and n is a quantity of
--    items to produce. fields that are unnamed will be underwritten

local fab

local function fQuant(a,b,n) return ((a or 0)+(b or 0))*n end
local function fFac  (a,b,n)
	if a == nil and b == nil then return nil end
	local f if a == nil or b == nil then
		f = a or b
	else
................................................................................
end
local function fReq  (a,b,n) return a or b         end
local function fFlag (a,b,n) return a and b        end
local function fSize (a,b,n) return math.max(a,b)  end

local F = string.format
local lib = starlit.mod.lib

local function fRawMat(class)
	return function(x,n,stack)
		local def = stack:get_definition()._starlit
		if not def.material then return 0 end
		local mf = fab {[def.material.kind] = {[def.material[def.material.kind]] = def.mass}}

--		this is bugged: the same item can satisfy both e.g. metal.steel and element.fe
-- 		if not (mf[class] and mf[class][x]) then
-- 			mf = mf:elementalize()
			if not (mf[class] and mf[class][x]) then return 0 end
-- 		end

		local perItem = mf[class][x]
		local wholeStack = perItem * stack:get_count()

		local deduct = ItemStack()
		local taken = 0 repeat
			taken = taken + perItem
			deduct:add_item(stack:take_item(1))
		until taken >= n or stack:is_empty()
		return taken, deduct

		--[[  outsmarted myself with this one :/
		local fab = def.recover or def.fab
		-- we ignore recover_vary bc this needs to be deterministic
		local function tryFab(fab)
			if not fab then return 0 end
			if fab[class] and fab[class][x] then
				local perItem = fab[class][x]
				local wholeStack = perItem * stack:get_count()
				print('fab has substance', n, perItem, wholeStack)
				local deduct = ItemStack()
				local taken = 0 repeat
					taken = taken + perItem
					deduct:add_item(stack:take_item(1))
				until taken >= n
				return taken, deduct
			end
			return 0
		end
		local z,c = tryFab(fab)
		if z == 0 then -- does it work if we break down the constituent compounds?
			z,c = tryFab(fab:elementalize())
		end]]
	end
end
local function fCanister(class)
	return function(x, n, stack)
		local amt, deduct = 0
		return amt, deduct
	end
end

local fields = {
	-- fabrication eligibility will be determined by which kinds
	-- of input a particular fabricator can introduce. e.g. a
	-- printer with a  but no cache can only print items whose
	-- recipe only names elements as ingredients
	element = {
		name = {"element", "elements"};
		string = function(x, n, long)
			local el = starlit.world.material.element.db[x]
			return lib.math.siUI('g', n) .. ' ' .. ((not long and el.sym) or el.name)
		end;
		image = function(x, n)
			return string.format('starlit-element-%s.png', x)
		end;
		inventory = fRawMat 'element';
		op = fQuant;
	};
	metal ={
		name = {"metal", "metals"};
		string = function(x, n)
			local met = starlit.world.material.metal.db[x]
			return lib.math.siUI('g', n) .. ' ' .. met.name
		end;
		image = function(x, n)
			local met = starlit.world.material.metal.db[x]
			return ItemStack(met.form.brick):get_definition().inventory_image
		end;
		inventory = fRawMat 'metal';
		op = fQuant;
	};
	liquid = {
		name = {"liquid", "liquids"};
		string = function(x, n)
			local liq = starlit.world.material.liquid.db[x]
			return lib.math.siUI('L', n) .. ' ' .. liq.name
		end;
		inventory = fCanister 'liquid';
		op = fQuant;
	};
	gas = {
		name = {"gas", "gasses"};
		string = function(x, n)
			local gas = starlit.world.material.gas.db[x]
			return lib.math.siUI('g', n) .. ' ' .. gas.name
		end;
		inventory = fCanister 'gas';
		op = fQuant;
	};
-- 	crystal = {
-- 		op = fQuant;
-- 	};
	item = {
		name = {"item", "items"};
		string = function(x, n)
			local i = minetest.registered_items[x]
			return tostring(n) .. 'x ' .. i.short_description
		end;
		image = function(x, n)
			return ItemStack(x):get_definition().inventory_image
		end;
		inventory = function(x, n, stack)
			x = ItemStack(x)
			if not x:equals(stack) then return nil end
			local deduct = stack:take_item(x:get_count() * n)
			return deduct:get_count(), deduct
		end;
	};

	-- factors

	cost = {
		name = {"cost", "costs"};
		op=fFac; -- units vary
		string = function(x,n)
			local units = {
				power = 'J';
			}
			local s
			if units[x] then
				s = lib.math.siUI(units[x], n)
			elseif starlit.world.stats[x] then
				s = starlit.world.stats[x].desc(n)
			else
				s = tostring(n)
			end
			return string.format('%s: %s',x,s)
		end;
		image = function(x,n)
			local icons = {
				power = 'starlit-ui-icon-stat-power.png';
				numina = 'starlit-ui-icon-stat-numina.png'
			}
			return icons[x]
		end;
	};
	time = {op=fFac}; -- (s)
		-- print: base printing time
	size = {op=fSize};
		-- printBay: size of the printer bay necessary to produce the item
	req  = {op=fReq};
	flag = {op=fFlag}; -- means that can be used to produce the item & misc flags
		-- print: allow production with a printer
		-- smelt: allow production with a smelter
	-- all else defaults to underwrite
}

local order = {
	'element', 'metal', 'liquid', 'gas', 'item',
	'cost'
}

local lib = starlit.mod.lib

fab = lib.class {
	__name = 'starlit:fab';
	
	fields = fields;
	order = order;
	construct = function(q) return q end;
	__index = {
		elementalize = function(self)
			local e = fab {element = self.element or {}}
			for _, kind in pairs {'metal', 'gas', 'liquid'} do
				for m,mass in pairs(self[kind] or {}) do
					local mc = starlit.world.material[kind].db[m].composition
					e = e + mc:elementalize()*mass
				end
			end
			return e
		end;

		elementSeq = function(self)
................................................................................
				if next(t) then table.insert(all, {
					id=o, list=t;
					header=fields[o].name[t[2] and 2 or 1];
				}) end
			end
			return all
		end;
		seek = function(self, invs)
			local consumed = {}
			local spec = fab{item={}} -- used to generate a convenient visualization
			local unsatisfied = fab{}
			local cache = {}
			local leftover = fab{}
			local function alreadyGot(inv,slot)
				local already = cache[inv] and cache[inv][slot] and true
				if cache[inv] == nil then cache[inv] = {} end
				cache[inv][slot] = true
				return already
			end
			for ci, cat in ipairs(order) do
				local scan = fields[cat].inventory
				if scan and self[cat] then
					for substance, amt in pairs(self[cat]) do
-- 						print('check substance', substance, amt, dump(self[cat]))
						local amtFound = 0
						local stacks = {}
						for ii, inv in ipairs(invs) do
-- 							print('  - check inventory',ii,inv,'for',cat,substance,amt)
							for oi, o in ipairs(inv) do
-- 								print('    - check stack', oi, o)
								local st = ItemStack(o)
								if not st:is_empty() then
									local avail, deduct = scan(substance,amt,st)
									if avail > 0 then
										amtFound = amtFound + avail
-- 										print('       - found amt', amtFound,ii,oi)
										if not alreadyGot(ii,oi) then
											local sv = {
												inv=ii, slot=oi;
												consume=deduct, remain=st;
												satisfy=fab{[cat]={[substance]=avail}}
											}
											table.insert(stacks, sv)
										end
										if amtFound >= amt then goto suffice end
									end
								end
							end
						end

						::insufficient:: do -- record the failure and move on
							if unsatisfied[cat] == nil then unsatisfied[cat] = {} end
							unsatisfied[cat][substance] = amt-amtFound
						end

						::suffice:: -- commit the stack diff
						for si,sv in ipairs(stacks) do
-- 							table.insert(consumed, sv)
							local di = ItemStack(sv.consume)
							local din = ItemStack(sv.consume):get_name()
							if not spec.item[din] then spec.item[din] = 0 end
							spec.item[din] = spec.item[din] + di:get_count()
							local lo = amtFound-amt if lo > 0 then
								leftover = leftover + fab{[cat]={[substance]=lo}}
							end
						end

					end
				end
			end
			return (next(unsatisfied) == nil), consumed, unsatisfied, leftover, spec
		end;
	};

	__tostring = function(self)
		local t = {}
		for i,o in ipairs(order) do
			if self[o] and fields[o].string then
				for mat,amt in pairs(self[o]) do