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
|