Differences From
Artifact [bdd1907d1f]:
- File
mods/starlit/fab.lua
— part of check-in
[953151446f]
at
2024-05-05 19:31:39
on branch trunk
— better alarm LEDs, continue work on matter compiler UI, hack around gravitational horrorscape (i.e. stop shitting all over the server's `minetest.conf`), better stat interface, tweak some compute stats, be more generous with starting battery loadout, mercilessly squash numberless bugs beneath my jackbooted heel
(user:
lexi,
size: 11288)
[annotate]
[blame]
[check-ins using]
12 12 -- * used for determining quantities. that is,
13 13 -- f*x = spec to make x instances of f
14 14 --
15 15 -- new fab fields must be defined in starlit.type.fab.fields.
16 16 -- this maps a name to fn(a,b,n) -> quant, where a is the first
17 17 -- argument, b is a compounding amount, and n is a quantity of
18 18 -- items to produce. fields that are unnamed will be underwritten
19 +
20 +local fab
19 21
20 22 local function fQuant(a,b,n) return ((a or 0)+(b or 0))*n end
21 23 local function fFac (a,b,n)
22 24 if a == nil and b == nil then return nil end
23 25 local f if a == nil or b == nil then
24 26 f = a or b
25 27 else
................................................................................
29 31 end
30 32 local function fReq (a,b,n) return a or b end
31 33 local function fFlag (a,b,n) return a and b end
32 34 local function fSize (a,b,n) return math.max(a,b) end
33 35
34 36 local F = string.format
35 37 local lib = starlit.mod.lib
38 +
39 +local function fRawMat(class)
40 + return function(x,n,stack)
41 + local def = stack:get_definition()._starlit
42 + if not def.material then return 0 end
43 + local mf = fab {[def.material.kind] = {[def.material[def.material.kind]] = def.mass}}
44 +
45 +-- this is bugged: the same item can satisfy both e.g. metal.steel and element.fe
46 +-- if not (mf[class] and mf[class][x]) then
47 +-- mf = mf:elementalize()
48 + if not (mf[class] and mf[class][x]) then return 0 end
49 +-- end
50 +
51 + local perItem = mf[class][x]
52 + local wholeStack = perItem * stack:get_count()
53 +
54 + local deduct = ItemStack()
55 + local taken = 0 repeat
56 + taken = taken + perItem
57 + deduct:add_item(stack:take_item(1))
58 + until taken >= n or stack:is_empty()
59 + return taken, deduct
60 +
61 + --[[ outsmarted myself with this one :/
62 + local fab = def.recover or def.fab
63 + -- we ignore recover_vary bc this needs to be deterministic
64 + local function tryFab(fab)
65 + if not fab then return 0 end
66 + if fab[class] and fab[class][x] then
67 + local perItem = fab[class][x]
68 + local wholeStack = perItem * stack:get_count()
69 + print('fab has substance', n, perItem, wholeStack)
70 + local deduct = ItemStack()
71 + local taken = 0 repeat
72 + taken = taken + perItem
73 + deduct:add_item(stack:take_item(1))
74 + until taken >= n
75 + return taken, deduct
76 + end
77 + return 0
78 + end
79 + local z,c = tryFab(fab)
80 + if z == 0 then -- does it work if we break down the constituent compounds?
81 + z,c = tryFab(fab:elementalize())
82 + end]]
83 + end
84 +end
85 +local function fCanister(class)
86 + return function(x, n, stack)
87 + local amt, deduct = 0
88 + return amt, deduct
89 + end
90 +end
36 91
37 92 local fields = {
38 93 -- fabrication eligibility will be determined by which kinds
39 94 -- of input a particular fabricator can introduce. e.g. a
40 95 -- printer with a but no cache can only print items whose
41 96 -- recipe only names elements as ingredients
42 97 element = {
43 98 name = {"element", "elements"};
44 99 string = function(x, n, long)
45 100 local el = starlit.world.material.element.db[x]
46 - return lib.math.si('g', n) .. ' ' .. ((not long and el.sym) or el.name)
101 + return lib.math.siUI('g', n) .. ' ' .. ((not long and el.sym) or el.name)
47 102 end;
48 103 image = function(x, n)
49 104 return string.format('starlit-element-%s.png', x)
50 105 end;
106 + inventory = fRawMat 'element';
51 107 op = fQuant;
52 108 };
53 109 metal ={
54 110 name = {"metal", "metals"};
55 111 string = function(x, n)
56 112 local met = starlit.world.material.metal.db[x]
57 - return lib.math.si('g', n) .. ' ' .. met.name
113 + return lib.math.siUI('g', n) .. ' ' .. met.name
58 114 end;
59 115 image = function(x, n)
60 116 local met = starlit.world.material.metal.db[x]
61 - return ItemStack(met.form.ingot):get_definition().inventory_image
117 + return ItemStack(met.form.brick):get_definition().inventory_image
62 118 end;
119 + inventory = fRawMat 'metal';
63 120 op = fQuant;
64 121 };
65 122 liquid = {
66 123 name = {"liquid", "liquids"};
67 124 string = function(x, n)
68 125 local liq = starlit.world.material.liquid.db[x]
69 - return lib.math.si('L', n) .. ' ' .. liq.name
126 + return lib.math.siUI('L', n) .. ' ' .. liq.name
70 127 end;
128 + inventory = fCanister 'liquid';
71 129 op = fQuant;
72 130 };
73 131 gas = {
74 132 name = {"gas", "gasses"};
75 133 string = function(x, n)
76 134 local gas = starlit.world.material.gas.db[x]
77 - return lib.math.si('g', n) .. ' ' .. gas.name
135 + return lib.math.siUI('g', n) .. ' ' .. gas.name
78 136 end;
137 + inventory = fCanister 'gas';
79 138 op = fQuant;
80 139 };
81 140 -- crystal = {
82 141 -- op = fQuant;
83 142 -- };
84 143 item = {
85 144 name = {"item", "items"};
86 145 string = function(x, n)
87 146 local i = minetest.registered_items[x]
88 147 return tostring(n) .. 'x ' .. i.short_description
89 148 end;
149 + image = function(x, n)
150 + return ItemStack(x):get_definition().inventory_image
151 + end;
152 + inventory = function(x, n, stack)
153 + x = ItemStack(x)
154 + if not x:equals(stack) then return nil end
155 + local deduct = stack:take_item(x:get_count() * n)
156 + return deduct:get_count(), deduct
157 + end;
90 158 };
91 159
92 160 -- factors
93 161
94 - cost = {op=fFac}; -- units vary
162 + cost = {
163 + name = {"cost", "costs"};
164 + op=fFac; -- units vary
165 + string = function(x,n)
166 + local units = {
167 + power = 'J';
168 + }
169 + local s
170 + if units[x] then
171 + s = lib.math.siUI(units[x], n)
172 + elseif starlit.world.stats[x] then
173 + s = starlit.world.stats[x].desc(n)
174 + else
175 + s = tostring(n)
176 + end
177 + return string.format('%s: %s',x,s)
178 + end;
179 + image = function(x,n)
180 + local icons = {
181 + power = 'starlit-ui-icon-stat-power.png';
182 + numina = 'starlit-ui-icon-stat-numina.png'
183 + }
184 + return icons[x]
185 + end;
186 + };
95 187 time = {op=fFac}; -- (s)
96 188 -- print: base printing time
97 189 size = {op=fSize};
98 190 -- printBay: size of the printer bay necessary to produce the item
99 191 req = {op=fReq};
100 192 flag = {op=fFlag}; -- means that can be used to produce the item & misc flags
101 193 -- print: allow production with a printer
102 194 -- smelt: allow production with a smelter
103 195 -- all else defaults to underwrite
104 196 }
105 197
106 198 local order = {
107 - 'element', 'metal', 'liquid', 'gas', 'item'
199 + 'element', 'metal', 'liquid', 'gas', 'item',
200 + 'cost'
108 201 }
109 202
110 203 local lib = starlit.mod.lib
111 204
112 -local fab fab = lib.class {
205 +fab = lib.class {
113 206 __name = 'starlit:fab';
114 207
115 208 fields = fields;
116 209 order = order;
117 210 construct = function(q) return q end;
118 211 __index = {
119 212 elementalize = function(self)
120 213 local e = fab {element = self.element or {}}
121 214 for _, kind in pairs {'metal', 'gas', 'liquid'} do
122 215 for m,mass in pairs(self[kind] or {}) do
123 - local mc = starlit.world.material[kind][m].composition
216 + local mc = starlit.world.material[kind].db[m].composition
124 217 e = e + mc:elementalize()*mass
125 218 end
126 219 end
127 220 return e
128 221 end;
129 222
130 223 elementSeq = function(self)
................................................................................
204 297 if next(t) then table.insert(all, {
205 298 id=o, list=t;
206 299 header=fields[o].name[t[2] and 2 or 1];
207 300 }) end
208 301 end
209 302 return all
210 303 end;
304 + seek = function(self, invs)
305 + local consumed = {}
306 + local spec = fab{item={}} -- used to generate a convenient visualization
307 + local unsatisfied = fab{}
308 + local cache = {}
309 + local leftover = fab{}
310 + local function alreadyGot(inv,slot)
311 + local already = cache[inv] and cache[inv][slot] and true
312 + if cache[inv] == nil then cache[inv] = {} end
313 + cache[inv][slot] = true
314 + return already
315 + end
316 + for ci, cat in ipairs(order) do
317 + local scan = fields[cat].inventory
318 + if scan and self[cat] then
319 + for substance, amt in pairs(self[cat]) do
320 +-- print('check substance', substance, amt, dump(self[cat]))
321 + local amtFound = 0
322 + local stacks = {}
323 + for ii, inv in ipairs(invs) do
324 +-- print(' - check inventory',ii,inv,'for',cat,substance,amt)
325 + for oi, o in ipairs(inv) do
326 +-- print(' - check stack', oi, o)
327 + local st = ItemStack(o)
328 + if not st:is_empty() then
329 + local avail, deduct = scan(substance,amt,st)
330 + if avail > 0 then
331 + amtFound = amtFound + avail
332 +-- print(' - found amt', amtFound,ii,oi)
333 + if not alreadyGot(ii,oi) then
334 + local sv = {
335 + inv=ii, slot=oi;
336 + consume=deduct, remain=st;
337 + satisfy=fab{[cat]={[substance]=avail}}
338 + }
339 + table.insert(stacks, sv)
340 + end
341 + if amtFound >= amt then goto suffice end
342 + end
343 + end
344 + end
345 + end
346 +
347 + ::insufficient:: do -- record the failure and move on
348 + if unsatisfied[cat] == nil then unsatisfied[cat] = {} end
349 + unsatisfied[cat][substance] = amt-amtFound
350 + end
351 +
352 + ::suffice:: -- commit the stack diff
353 + for si,sv in ipairs(stacks) do
354 +-- table.insert(consumed, sv)
355 + local di = ItemStack(sv.consume)
356 + local din = ItemStack(sv.consume):get_name()
357 + if not spec.item[din] then spec.item[din] = 0 end
358 + spec.item[din] = spec.item[din] + di:get_count()
359 + local lo = amtFound-amt if lo > 0 then
360 + leftover = leftover + fab{[cat]={[substance]=lo}}
361 + end
362 + end
363 +
364 + end
365 + end
366 + end
367 + return (next(unsatisfied) == nil), consumed, unsatisfied, leftover, spec
368 + end;
211 369 };
212 370
213 371 __tostring = function(self)
214 372 local t = {}
215 373 for i,o in ipairs(order) do
216 374 if self[o] and fields[o].string then
217 375 for mat,amt in pairs(self[o]) do