starlit  Diff

Differences From Artifact [bdd1907d1f]:

To Artifact [a2443e45a4]:


    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