starlit  Check-in [52a4f364ac]

Overview
Comment:compiler now draws power, better compile job progress bars, stats screen refreshes
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 52a4f364ac46708dc1cba01bec71657cb07700dee85ef13407c3fdbf093c504a
User & Date: lexi on 2024-05-07 03:45:58
Other Links: manifest | tags
Context
2024-05-07
23:16
fix some bugs, start work on actually using the tier system, add usage docs check-in: e905f495dc user: lexi tags: trunk
03:45
compiler now draws power, better compile job progress bars, stats screen refreshes check-in: 52a4f364ac user: lexi tags: trunk
00:27
add basic electrical parts, fix scrollbars to the greatest extent possible, fix error in marshal error msg, tweak inane compute stats check-in: 5267c0742d user: lexi tags: trunk
Changes

Modified mods/starlit-electronics/init.lua from [358af6b345] to [50792bb85d].

     1      1   local lib = starlit.mod.lib
     2      2   
     3      3   local E = {}
     4      4   starlit.mod.electronics = E
            5  +
            6  +--------------------------------
            7  +-- software context interface --
            8  +-------------------------------- ---------------------------------------
            9  +-- any time a program is run() or bgProc()eeded, a context argument MUST
           10  +-- be passed to the entry point. this argument must have at least the
           11  +-- following fields and functions:
           12  +
           13  +-- string   context ('suit', 'device', etc)
           14  +--          names the context in which the program is being run
           15  +-- swCap    program
           16  +--          a structure as returned from usableSoftware(), providing the
           17  +--          program with access to and information about its substrate
           18  +-- function verify() --> bool
           19  +--          return true if the chip the program was loaded from is still
           20  +--          accessible
           21  +-- function drawCurrent(power, time, whatFor, [min]) --> J
           22  +--          attempt to draw current from the power source of whatever core
           23  +--          the program is running on
           24  +-- function pullConf() --> ()
           25  +--          updates the conf structure provided to the program as part of
           26  +--          its initial context
           27  +-- function saveConf([conf]) --> ()
           28  +--          saves any changes made to the conf structure
           29  +-- function availableChips() --> inventory list
           30  +--          returns the list of chips that are available in the execution
           31  +--          context. needed to retrieve system files
           32  +
           33  +-- currently, this interface is implemented by
           34  +-- - starlit/interfaces.lua
           35  +-- - starlit/suit.lua
     5     36   
     6     37   ---------------------
     7     38   -- item registries --
     8     39   ---------------------
     9     40   
    10     41   -- a dynamo is any item that produces power and can be slotted into a power
    11     42   -- source slot. this includes batteries, but also things like radiothermal

Modified mods/starlit-electronics/sw.lua from [b2e1bf9d1b] to [1c1e9dd876].

   163    163   
   164    164   			::notfound:: do
   165    165   				return
   166    166   			end
   167    167   
   168    168   			::found::
   169    169   			local scm = starlit.item.sw.db[job.schematic]
   170         -			job.cyclesLeft = math.max(0, job.cyclesLeft - ctx.comp.cycles)
   171         -			if job.cyclesLeft == 0 then
   172         -				job.timeLeft = math.max(0, job.timeLeft - interval)
          170  +			local stages = {
          171  +				{id = 'cyclesLeft', decr=ctx.comp.cycles};
          172  +				{id = 'powerLeft', decr=function(current)
          173  +					return ctx.drawCurrent(current, interval, 'compile')
          174  +				end};
          175  +				{id = 'timeLeft', decr=interval};
          176  +				{id = 'numinaLeft', decr=0}; -- FIXME
          177  +			}
          178  +
          179  +			for i,v in ipairs(stages) do
          180  +				if job[v.id] > 0 then
          181  +					local decr
          182  +					if type(v.decr) == 'function'
          183  +						then decr = v.decr(job[v.id])
          184  +						else decr = v.decr
          185  +					end
          186  +					job[v.id] = math.max(0, job[v.id] - decr)
          187  +					goto incomplete
          188  +				end
   173    189   			end
   174         -			if job.timeLeft == 0 and job.cyclesLeft == 0 then
          190  +
          191  +			::complete:: do
   175    192   				table.remove(conf, jobSlot)
   176    193   				user:give(scm.output)
   177    194   				user:alarm(-2, 'item')
   178         -			else
          195  +				goto done
          196  +			end
          197  +
          198  +			::incomplete:: do
   179    199   				conf[jobSlot].value = job_t.enc(job)
   180    200   			end
   181    201   
          202  +			::done::
   182    203   			ctx.saveConf()
   183    204   		end;
   184    205   	}
   185    206   end
   186    207   
   187    208   starlit.item.sw.link('starlit_electronics:compile_commune', matterCompiler {
   188    209   	name = 'Compile Matter';

Modified mods/starlit-material/elements.lua from [0b9dddbb90] to [224229dfa8].

     4      4   
     5      5   M.element.meld {
     6      6   	hydrogen = {
     7      7   		name = 'hydrogen', sym = 'H', n = 1;  density = 8.988e-5;
     8      8   		gas = true;
     9      9   		color = lib.color(1,0.8,.3);
    10     10   	};
           11  +	lithium = {
           12  +		name = 'lithium', sym = 'Li', n = 3;  density = 0.534;
           13  +		-- i think lithium is considered a metal but we don't mark it as
           14  +		-- one here because making a 'lithium ingot' is insane (even possible?)
           15  +		color = lib.color(1,0.8,.3);
           16  +	};
    11     17   	beryllium = {
    12     18   		name = 'beryllium', sym = 'Be', n = 4;  density = 0;
    13     19   		metal = true; -- rare emerald-stuff
    14     20   		color = lib.color(0.2,1,0.2);
    15     21   	};
    16     22   	oxygen = {
    17     23   		name = 'oxygen', sym = 'O', n = 8;  density = 0.001429;
................................................................................
    18     24   		gas = true;
    19     25   		color = lib.color(.2,1,.2);
    20     26   	};
    21     27   	carbon = {
    22     28   		name = 'carbon', sym = 'C', n = 6, density = 2.266; -- g/cm³
    23     29   		color = lib.color(.7,.2,.1);
    24     30   	};
           31  +	magnesium = {
           32  +		name = 'magnesium', sym = 'Mg', n = 12, density = 1.738;
           33  +		metal = true;
           34  +		color = lib.color(0.7, 0.7, 0.7);
           35  +	};
           36  +	aluminum = {
           37  +		name = 'aluminum', sym = 'Al', n = 13;  density = 2.7;
           38  +		metal = true;
           39  +		color = lib.color(0.5,.55,.6);
           40  +	};
    25     41   	silicon = {
    26     42   		name = 'silicon', sym = 'Si', n = 14, density = 2.329;
    27     43   		metal = true; -- can be forged into an ingot
    28     44   		color = lib.color(.6,.6,.4);
    29     45   	};
    30         -	neodymium = {
    31         -		name = 'neodymium', sym = 'Nd', n= 60, density = 7.01;
    32         -		metal = true;
    33         -		color = lib.color(1,1,1);
           46  +	argon = {
           47  +		name = 'argon', sym = 'Ar', n = 18;  density = 0.001784;
           48  +		gas = true;
           49  +		color = lib.color(0,0.1,.9);
    34     50   	};
    35     51   	potassium = {
    36     52   		name = 'potassium', sym = 'K', n = 19, density = 0.862;
    37     53   		-- potassium is technically a metal but it's so soft
    38     54   		-- it can be easily nanoworked without high temps, so
    39     55   		-- ingots make no sense
    40     56   		color = lib.color(1,.8,0.1);
    41     57   	};
    42     58   	calcium = {
    43     59   		name = 'calcium', sym = 'Ca', n = 20; density = 1.55;
    44     60   		metal = true;
    45     61   		color = lib.color(1,1,0.7);
    46     62   	};
    47         -	magnesium = {
    48         -		name = 'magnesium', sym = 'Mg', n = 12, density = 1.738;
    49         -		metal = true;
    50         -		color = lib.color(0.7, 0.7, 0.7);
    51         -	};
    52         -	aluminum = {
    53         -		name = 'aluminum', sym = 'Al', n = 13;  density = 2.7;
    54         -		metal = true;
    55         -		color = lib.color(0.5,.55,.6);
    56         -	};
    57         -	iron = {
    58         -		name = 'iron', sym = 'Fe', n = 26;  density = 7.874;
    59         -		metal = true;
    60         -		color = lib.color(.3,.3,.3);
    61         -	};
    62         -	copper = {
    63         -		name = 'copper', sym = 'Cu', n = 29;  density = 8.96;
    64         -		metal = true;
    65         -		color = lib.color(.8,.4,.1);
    66         -	};
    67         -	lithium = {
    68         -		name = 'lithium', sym = 'Li', n = 3;  density = 0.534;
    69         -		-- i think lithium is considered a metal but we don't mark it as
    70         -		-- one here because making a 'lithium ingot' is insane (even possible?)
    71         -		color = lib.color(1,0.8,.3);
    72         -	};
    73     63   	titanium = {
    74     64   		name = 'titanium', sym = 'Ti', n = 22;  density = 4.506;
    75     65   		metal = true;
    76     66   		color = lib.color(.7,.7,.7);
    77     67   	};
    78     68   	vanadium = {
    79     69   		name = 'vanadium', sym = 'V', n = 23; density = 6;
    80     70   		metal = true;
    81     71   		color = lib.color(.3,0.5,.3);
    82     72   	};
           73  +	iron = {
           74  +		name = 'iron', sym = 'Fe', n = 26;  density = 7.874;
           75  +		metal = true;
           76  +		color = lib.color(.3,.3,.3);
           77  +	};
    83     78   	nickel = {
    84         -		name = 'nickel', sym = 'ni', n = 28, density = 8.908;
           79  +		name = 'nickel', sym = 'Ni', n = 28, density = 8.908;
    85     80   		metal = true;
    86     81   		color = lib.color(.7,.7,.6);
    87     82   	};
           83  +	copper = {
           84  +		name = 'copper', sym = 'Cu', n = 29;  density = 8.96;
           85  +		metal = true;
           86  +		color = lib.color(.8,.4,.1);
           87  +	};
           88  +	rubidium = {
           89  +		name = 'rubidium', sym = 'Rb', n = 37;  density = 1.532;
           90  +		color = lib.color(.8,.7,.7);
           91  +		-- this is a metal, but it's liquid at the temperatures we care about,
           92  +		-- so we don't set the metal flag
           93  +	};
           94  +	technetium = {
           95  +		name = 'technetium', sym = 'Tc', n = 43;  density = 11;
           96  +		desc = 'Prized by the higher Powers for subtle interactions that elude mere human scholars, technetium is of particular use in nuclear nanobatteries.';
           97  +		metal = true;
           98  +		color = lib.color(.2,0.2,1);
           99  +	};
          100  +	silver = {
          101  +		name = 'silver', sym = 'Ag', n = 47;  density = 10.49;
          102  +		metal = true;
          103  +		color = lib.color(.7,.7,.8);
          104  +	};
    88    105   	xenon = {
    89    106   		name = 'xenon', sym = 'Xe', n = 54;  density = 0.005894;
    90    107   		gas = true;
    91    108   		color = lib.color(.5,.1,1);
    92    109   	};
    93         -	argon = {
    94         -		name = 'argon', sym = 'Ar', n = 18;  density = 0.001784;
    95         -		gas = true;
    96         -		color = lib.color(0,0.1,.9);
          110  +	cesium = {
          111  +		name = 'cesium', sym = 'Cs', n = 55;  density = 1.93;
          112  +		color = lib.color(.5,.1,1);
          113  +		-- this is a metal, but it's liquid at the temperatures we care about,
          114  +		-- so we don't set the metal flag
          115  +	};
          116  +	neodymium = {
          117  +		name = 'neodymium', sym = 'Nd', n= 60, density = 7.01;
          118  +		metal = true;
          119  +		color = lib.color(1,1,1);
    97    120   	};
    98    121   	osmium = {
    99    122   		name = 'osmium', sym = 'Os', n = 76;  density = 22.59;
   100    123   		metal = true;
   101    124   		color = lib.color(.8,.1,1);
   102    125   	};
   103    126   	iridium = {
   104    127   		name = 'iridium', sym = 'Ir', n = 77; density = 22.56;
   105    128   		metal = true;
   106    129   		color = lib.color(.8,0,.5);
   107    130   	};
   108         -	technetium = {
   109         -		name = 'technetium', sym = 'Tc', n = 43;  density = 11;
   110         -		desc = 'Prized by the higher Powers for subtle interactions that elude mere human scholars, technetium is of particular use in nuclear nanobatteries.';
   111         -		metal = true;
   112         -		color = lib.color(.2,0.2,1);
   113         -	};
   114    131   	uranium = {
   115    132   		name = 'uranium', sym = 'U', n = 92;  density = 19.1;
   116    133   		desc = 'A weak but relatively plentiful nuclear fuel.';
   117    134   		metal = true;
   118    135   		color = lib.color(.2,.7,0);
   119    136   	};
   120    137   	thorium = {
   121    138   		name = 'thorium', sym = 'Th', n = 90;  density = 11.7;
   122    139   		desc = 'A frighteningly powerful nuclear fuel.';
   123    140   		metal = true;
   124    141   		color = lib.color(.7,.3,.1);
   125    142   	};
   126         -	silver = {
   127         -		name = 'silver', sym = 'Ag', n = 47;  density = 10.49;
   128         -		metal = true;
   129         -		color = lib.color(.7,.7,.8);
   130         -	};
   131    143   	gold = {
   132    144   		name = 'gold', sym = 'Au', n = 79;  density = 19.30;
   133    145   		metal = true;
   134    146   		color = lib.color(1,.8,0);
   135    147   	};
   136    148   }

Modified mods/starlit/compile.lua from [5db02bfe42] to [d92d0ca2de].

    27     27   	local job_t = starlit.store.compilerJob
    28     28   	local conf = state.pgm.file.body.conf
    29     29   	table.insert(conf, {
    30     30   		key='job', value=job_t.enc {
    31     31   			schematic  = scm.id;
    32     32   			cyclesLeft = scm.sw.cost and scm.sw.cost.cycles or 0;
    33     33   			timeLeft   = (fab.time and fab.time.print or 0);
           34  +			powerLeft  = cost.power;
           35  +			numinaLeft = cost.numina;
    34     36   		}
    35     37   	})
    36     38   end
    37     39   
    38     40   local function compilerCanPrint(user, cpl, scm)
    39     41   	local E = starlit.mod.electronics
    40     42   	local output = ItemStack(scm.sw.output):get_definition()
................................................................................
    44     46   		user.entity:get_inventory():get_list 'main';
    45     47   	}
    46     48   
    47     49   	local cost = {
    48     50   		fab = fab, output = output;
    49     51   		consume = consume, unsat = unsat, leftover = leftover, itemSpec = itemSpec;
    50     52   		runtimeEstimate = scm.speed + cpl.speed + (fab.time and fab.time.print or 0);
    51         -		power = cpl.powerCost + scm.powerCost;
           53  +		power = cpl.powerCost + scm.powerCost
           54  +		      + (sw.input.cost and sw.input.cost.power);
    52     55   		ram = (cpl.cost and cpl.cost.ram or 0)
    53     56   		    + (scm.cost and scm.cost.ram or 0);
    54     57   		cycles = (cpl.cost and cpl.cost.cycles or 0)
    55     58   		       + (scm.cost and scm.cost.cycles or 0);
           59  +		numina = fab.cost and fab.cost.numina or 0;
    56     60   	}
    57     61   
    58     62   	local userComp = E.chip.sumCompute(
    59     63   		user.entity:get_inventory():get_list 'starlit_suit_chips'
    60     64   	)
    61     65   
    62     66   	if ok and cost.power <= user:suitCharge() and cost.ram <= userComp.ram then
................................................................................
   265    269   						end
   266    270   					end
   267    271   				end
   268    272   
   269    273   			end;
   270    274   
   271    275   			render = function(state, user)
   272         -				local sel, pgmSelector = state.select, {}
          276  +				local sel, pgmSelector = state.select, {kind = 'vert'}
   273    277   				state.fetch()
   274    278   				state.ctx.pullConf()
   275    279   
   276         -				local function pushSelector(id, item, label, desc, req)
          280  +				local function pushSelector(tbl, id, item, label, desc, req, w)
          281  +					w = w or 12
   277    282   					local rh = .5
   278         -					local label = {kind = 'text', w = 10-1.5, h=1.5;
   279         -							text = '<global valign=middle>'..lib.str.htsan(label) }
          283  +					local label = {kind = 'text', w = w-1.5, h=1.6;
          284  +						text = '<global valign=middle>'..lib.str.htsan(label) }
   280    285   					if req then
   281    286   						label.h = label.h - rh - .2
   282    287   
   283    288   						local imgs = {}
   284    289   						for ci,c in ipairs(req) do
   285    290   							for ei, e in ipairs(c.list) do
   286    291   								table.insert(imgs, {kind = 'img', w=rh, h=rh,  img=e.img})
   287    292   							end
   288    293   						end
   289         -						label = {kind = 'vert', w = 10-1.5, h=1.5;
          294  +						label = {kind = 'vert', w = w-1.5, h=1.8;
   290    295   							label;
   291         -							{kind ='hztl', w=10-1.5, h=rh; unpack(imgs); }
          296  +							{kind ='hztl', w=w-1.5, h=rh; unpack(imgs); }
   292    297   						}
   293    298   					end
   294         -					table.insert(pgmSelector, {kind = 'hztl', w=10,h=1.5;
          299  +					table.insert(tbl, {kind = 'hztl', w=w,h=1.8;
   295    300   						{kind = 'contact', id=id, w=1.5, h=1.5;
   296    301   							item = item;
   297    302   							color = {hue=220, sat=0, lum=0};
   298    303   							desc = desc;
   299    304   						};
   300    305   						label;
   301    306   					})
   302    307   				end
   303    308   
   304         -				local back = {kind = 'button', id='back', label = '<- Back', w=10,h=1.2}
          309  +				local back = {kind = 'button', id='back', label = '<- Back', w=12,h=1.2}
   305    310   				if sel.chips == nil then
   306    311   					table.insert(pgmSelector, {kind = 'img', img = 'starlit-ui-alert.png', w=2, h=2})
   307    312   				elseif sel.chip == nil then
   308    313   					for i, c in ipairs(sel.chips.order) do
   309    314   					-- TODO filter out chips without schematics?
   310         -						pushSelector('chip_' .. c.data.uuid, c.stack, c.data.label)
          315  +						pushSelector(pgmSelector, 'chip_' .. c.data.uuid, c.stack, c.data.label)
   311    316   					end
   312    317   					if next(sel.chips.order) then
   313         -						table.insert(pgmSelector, {kind = 'hztl', w=10,h=1.5;
   314         -							{kind = 'button', w=5,h=1.5; id='showAll', label='Show All'};
   315         -							{kind = 'button', w=5,h=1.5; id='find', label='Find'};
          318  +						table.insert(pgmSelector, {kind = 'hztl', w=12,h=1.5;
          319  +							{kind = 'button', w=6,h=1.5; id='showAll', label='Show All'};
          320  +							{kind = 'button', w=6,h=1.5; id='find', label='Find'};
   316    321   						})
   317    322   					end
   318    323   				else
   319    324   					if sel.scm == nil then
          325  +						-- l m a o
          326  +-- 						pgmSelector.kind = 'hwrap'
          327  +-- 						pgmSelector.cols = 2
          328  +						local wrap = {kind='hwrap',cols=2,w=12}
   320    329   						for idx, ent in ipairs(sel.scms) do
   321    330   							local fab = ent.sw.input
   322    331   							if fab.flag and fab.flag.print then
   323    332   								local req = fab:visualize()
   324         -								pushSelector('scm_' .. idx, ent.sw.output, ent.sw.name, nil, req)
          333  +								pushSelector(wrap,'scm_' .. idx, ent.sw.output, ent.sw.name, nil, req,6)
   325    334   							end
   326    335   						end
          336  +						table.insert(pgmSelector, wrap)
   327    337   						table.insert(pgmSelector, back)
   328    338   					else
   329    339   						local scm = sel.scms[sel.scm]
   330    340   						local output = ItemStack(scm.sw.output):get_definition()
   331    341   						local fab = scm.sw.input
   332    342   						local sw = scm.sw
   333    343   						local function unmet(str)
   334    344   							return lib.color(1,.3,.3):fmt(str)
   335    345   						end
   336         -						table.insert(pgmSelector, {kind = 'hztl', w=10, h=1.2;
          346  +						table.insert(pgmSelector, {kind = 'hztl', w=12, h=1.2;
   337    347   							{kind = 'img', item = sw.output, w=1.2, h=1.2, desc=output.description};
   338         -							{kind = 'text', text = string.format('<global valign=middle><b>%s</b>', lib.str.htsan(sw.name)), w=10-1.2,h=1.2};
          348  +							{kind = 'text', text = string.format('<global valign=middle><b>%s</b>', lib.str.htsan(sw.name)), w=12-1.2,h=1.2};
   339    349   						})
   340         -						local inputTbl = {kind = 'vert', w=5,h=0;
   341         -							{kind = 'hbar', w=5, h=.5, text=sel.input and 'Input Plan' or 'Input'}};
   342         -						local costTbl = {kind = 'vert', w=5,h=0;
   343         -							{kind = 'hbar', w=5, h=.5, text=sel.input and 'Process Plan' or 'Process'}};
   344         -						local reqPane = {kind = 'pane', id='reqPane', w=10, h=7;
   345         -							{kind = 'hztl', w=10,h=0; inputTbl, costTbl}
          350  +						local inputTbl = {kind = 'vert', w=6,h=0;
          351  +							{kind = 'hbar', w=6, h=.5, text=sel.input and 'Input Plan' or 'Input'}};
          352  +						local costTbl = {kind = 'vert', w=6,h=0;
          353  +							{kind = 'hbar', w=6, h=.5, text=sel.input and 'Process Plan' or 'Process'}};
          354  +						local reqPane = {kind = 'pane', id='reqPane', w=12, h=8;
          355  +							{kind = 'hztl', w=12,h=0; inputTbl, costTbl}
   346    356   						}
   347    357   						local function pushCost(x, t, val)
   348         -							table.insert(costTbl, {kind='label', w=4.5,h=.5,x=x;
          358  +							table.insert(costTbl, {kind='label', w=5.5,h=.5,x=x;
   349    359   								text=string.format('%s: %s',t,val);
   350    360   							})
   351    361   						end
   352    362   						local function pushComputeCosts(header, p)
   353    363   							if p then
   354         -								table.insert(costTbl, {kind = 'label', w=5, h=.5, x=0; text=header});
          364  +								table.insert(costTbl, {kind = 'label', w=6, h=.5, x=0; text=header});
   355    365   								if p.cycles then
   356    366   									pushCost(.5, 'Compute', lib.math.siUI({'cycle','cycles'}, p.cycles, true))
   357    367   								end
   358    368   								if p.power then
   359    369   									local str = lib.math.siUI('J', p.power)
   360    370   									if p.power > user:suitCharge() then str = unmet(str) end
   361    371   									pushCost(.5, 'Power', str)
................................................................................
   368    378   									pushCost(.5, 'Memory', str)
   369    379   								end
   370    380   							end
   371    381   						end
   372    382   
   373    383   						local function fabToUI(x, inputTbl, req)
   374    384   							for ci,c in ipairs(req) do
   375         -								table.insert(inputTbl, {kind = 'label', w=5-x, h=.5, x=x;
          385  +								table.insert(inputTbl, {kind = 'label', w=6-x, h=.5, x=x;
   376    386   									text=lib.str.capitalize(c.header)});
   377    387   								for ei,e in ipairs(c.list) do
   378         -									table.insert(inputTbl, {kind = 'hztl', w=4.5-x, h=.5, x=x+.5;
          388  +									table.insert(inputTbl, {kind = 'hztl', w=5.5-x, h=.5, x=x+.5;
   379    389   										{kind='img',   w=.5,h=.5, img=e.img};
   380         -										{kind='label', w=3.3,h=.5,x=.2, text=lib.str.capitalize(e.label)};
          390  +										{kind='label', w=4.3,h=.5,x=.2, text=lib.str.capitalize(e.label)};
   381    391   									});
   382    392   								end
   383    393   							end
   384    394   						end
   385    395   
   386    396   						local commitHue, commitLabel = 120
   387    397   						if not sel.input then
................................................................................
   427    437   								end
   428    438   								fabToUI(0, inputTbl, vis)
   429    439   							end
   430    440   							if not ok then commitHue = 0 end
   431    441   						end
   432    442   						table.insert(pgmSelector, reqPane)
   433    443   						table.insert(pgmSelector, {kind = 'hztl', w=10,h=1.2;
   434         -							{kind = 'button', id='back', label = '← Back', w=5,h=1.2};
   435         -							{kind = 'button', id='commit', label = commitLabel .. ' →', w=5,h=1.2, color={hue=commitHue,sat=0,lum=0}};
          444  +							{kind = 'button', id='back', label = '← Back', w=6,h=1.2};
          445  +							{kind = 'button', id='commit', label = commitLabel .. ' →', w=6,h=1.2, color={hue=commitHue,sat=0,lum=0}};
   436    446   						})
   437    447   					end
   438    448   				end
   439    449   
   440    450   				local rec = getRecentJobs(state)
   441    451   				local recentList = {kind = 'hwrap', cols=3}
   442    452   				if rec then
   443    453   					for i,id in ipairs(rec) do
   444    454   						local out = ItemStack(starlit.item.sw.db[id].output)
   445    455   						table.insert(recentList, {kind='contact',w=1.5,h=1.5,id=string.format('recent_%s',i), item = out})
   446    456   					end
   447    457   				end
   448    458   				local queue = {kind='pane', w=5, h=9.5}
          459  +				local cst = state.ctx.availableChips()
   449    460   				for i,v in ipairs(state.pgm.file.body.conf) do
   450    461   					if v.key == 'job' then
   451    462   						local job = starlit.store.compilerJob.dec(v.value)
   452         -						local scm = starlit.item.sw.db[job.schematic]
   453         -						local cycTotal = (scm.cost and scm.cost.cycles or 0)
   454         -						local secTotal = (scm.input.time and scm.input.time.print or 0)
   455         -						local cycPct = cycTotal == 0 and 1 or (1 - job.cyclesLeft/cycTotal)
   456         -						local secPct = secTotal == 0 and 1 or (1 - job.timeLeft/secTotal)
   457         -						local out = ItemStack(scm.output)
   458         -						local cycBar = {kind = 'hbar', fac = cycPct, w=5-1.5, h = .5;
   459         -							color={hue=150,sat=0,lum=0}; text=string.format("Compute %s%%", math.floor(cycPct*100));
          463  +						local scm = starlit.mod.electronics.chip.usableSoftware(cst, {{
          464  +							id = job.schematic;
          465  +							sw = starlit.item.sw.db[job.schematic];
          466  +						}})[1]
          467  +						if scm == nil then goto badJob end
          468  +						local steps = {
          469  +							{label = 'Compute', n = job.cyclesLeft, color=lib.color(0,1,0);
          470  +								total = scm.sw.cost and scm.sw.cost.cycles or 0};
          471  +							{label = 'Energize', n = job.powerLeft, color=lib.color(0,.5,1);
          472  +								total = (scm.powerCost or 0)
          473  +								      + (state.pgm.powerCost or 0)
          474  +								      + (scm.sw.input.cost and scm.sw.input.cost.power or 0)};
          475  +							{label = 'Assemble', n = job.timeLeft, color=lib.color(1,.2,0);
          476  +								total = scm.sw.input.time and scm.sw.input.time.print or 0};
          477  +							{label = 'Numenize', n = job.numinaLeft, color=lib.color(1,0,1);
          478  +								total = scm.sw.cost and scm.sw.cost.numina or 0};
   460    479   						}
   461         -						local secBar = {kind = 'hbar', fac = secPct, w=5-1.5, h = .5;
   462         -							color={hue=50,sat=0,lum=0}; text=string.format("Assemble %s%%", math.floor(secPct*100));
   463         -						}
   464         -						table.insert(queue, {kind='hztl';  w=5, h=1.5;
          480  +						local bar = {kind = 'hbar', text = 'Complete', fac=1, w=5-1.5, h = .5}
          481  +						for i, s in ipairs(steps) do
          482  +							if s.n > 0 then
          483  +								local pct = 1 - s.n/s.total
          484  +								bar.color = s.color
          485  +								bar.fac = pct
          486  +								bar.text = string.format("%s %s%%", s.label, math.floor(pct*100))
          487  +								break
          488  +							end
          489  +						end
          490  +
          491  +						local out = ItemStack(scm.sw.output)
          492  +						table.insert(queue, {kind='hztl';  w=6, h=1.5;
   465    493   							{kind = 'contact', w=1.5,h=1.5, id=string.format('cancel_%s', i), item = out};
   466         -							{kind = 'vert', w=5-1.5, h=1.5;
   467         -								{kind = 'label', text=out:get_definition().short_description, w=5-1.5, h=.75};
   468         -								(cycPct < 1 and cycBar or secBar);
          494  +							{kind = 'vert', w=6-1.5, h=1.5;
          495  +								{kind = 'label', text=out:get_definition().short_description, w=6-1.5, h=.75};
          496  +								bar;
   469    497   							}
   470    498   						})
   471    499   					end
          500  +					::badJob::
   472    501   				end
   473    502   
   474    503   				return starlit.ui.build {
   475         -					kind = 'hztl', padding = 0.5; w = 20, h = 10, mode = 'sw';
   476         -					{kind = 'vert', w = 5, h = 5;
          504  +					kind = 'hztl', padding = 0.5; w = 22, h = 11, mode = 'sw';
          505  +					{kind = 'vert', w = 5, h = 15;
   477    506   						{kind = 'hbar', fac=0, w = 5, h = .5, text = '<b><left>Recent Prints</left></b>'};
   478    507   						recentList;
   479    508   					};
   480         -					{kind = 'vert', w = 10, h = 10;
   481         -						{kind = 'hbar', fac=0, w = 10, h = .5, text = '<b>Program Select</b>'};
   482         -						{kind = 'pane', w = 10, h = 9.5, id='pgmSelect';
   483         -							unpack(pgmSelector)
          509  +					{kind = 'vert', w = 12, h = 11;
          510  +						{kind = 'hbar', fac=0, w = 12, h = .5, text = '<b>Program Select</b>'};
          511  +						{kind = 'pane', w = 12, h = 10.5, id='pgmSelect';
          512  +							pgmSelector
   484    513   						};
   485    514   					};
   486         -					{kind = 'vert', w = 5, h = 10;
          515  +					{kind = 'vert', w = 5, h = 11;
   487    516   						{kind = 'hbar', fac=0, w = 5, h = .5, text = '<b><right>Print Queue</right></b>'};
   488    517   						queue;
   489    518   					};
   490    519   				}
   491    520   			end;
   492    521   		};
   493    522   	};
   494    523   })

Modified mods/starlit/interfaces.lua from [d00e83c8f9] to [461714c4c0].

   191    191   							user:reconfigureSuit()
   192    192   						end;
   193    193   						pullConf = function()
   194    194   							local stack = inv:get_stack('starlit_suit_chips', pgm.chipSlot)
   195    195   							pgm.fd.chip=stack
   196    196   							pgm.file = pgm.fd:read()
   197    197   						end;
          198  +						availableChips = function()
          199  +							return inv:get_list('starlit_suit_chips')
          200  +						end;
          201  +						drawCurrent = function(...)
          202  +							return user:suitDrawCurrent(...)
          203  +						end;
   198    204   					}
   199    205   				end
   200    206   
   201    207   				if pgm.sw.powerKind == 'active' then
   202    208   					if cfg then
   203    209   						user:openUI(pgm.sw.ui, 'index', {
   204    210   							context = 'suit';
................................................................................
   378    384   				return starlit.ui.build {
   379    385   					kind = 'vert', mode = 'sw';
   380    386   					padding = 0.5;
   381    387   				}
   382    388   			end;
   383    389   		};
   384    390   		body = {
          391  +			refresh = true;
   385    392   			render = function(state, user)
   386    393   				local barh = .75
   387    394   				local tb = {
   388    395   					kind = 'vert', mode = 'sw';
   389    396   					padding = 0.5, 
   390    397   					{kind = 'hztl', padding = 0.25;
   391    398   						{kind = 'label', text = 'Name', w = 2, h = barh};

Modified mods/starlit/store.lua from [ac25f2dec5] to [49ffbb2ad8].

    23     23   	)
    24     24   end
    25     25   
    26     26   starlit.store.compilerJob = G.struct {
    27     27   	schematic  = T.str;
    28     28   	cyclesLeft = T.u64;
    29     29   	timeLeft   = T.decimal;
           30  +	powerLeft  = T.decimal;
           31  +	numinaLeft  = T.decimal;
    30     32   }
    31     33   
    32     34   starlit.store.persona = G.struct {
    33     35   	name = T.str;
    34     36   	species = T.str;
    35     37   	speciesVariant = T.str;
    36     38   	background = T.str;

Modified mods/starlit/suit.lua from [f9e736d40a] to [963193f502].

   400    400   	elseif act == 'move' then
   401    401   		local item = inv:get_stack(p.to_list, p.to_index)
   402    402   		slotChange(p.from_list, 'take', item)
   403    403   		slotChange(p.to_list, 'put', item)
   404    404   	end
   405    405   end)
   406    406   
   407         -local suitInterval = 2.0
          407  +local suitInterval = 1.0
   408    408   starlit.startJob('starlit:suit-software', suitInterval, function(delta)
   409    409   	local runState = {
   410    410   		pgmsRun = {};
   411    411   		flags = {};
   412    412   	}
   413    413   	for id, u in pairs(starlit.activeUsers) do
   414    414   		if not u:naked() then
................................................................................
   437    437   						reconfSuit = true
   438    438   					end
   439    439   					function prop.pullConf()
   440    440   						local stack = inv:get_stack('starlit_suit_chips', suitprog.chipSlot)
   441    441   						prop.fd.chip=stack
   442    442   						prop.file = prop.fd:read()
   443    443   					end
          444  +					function prop.availableChips()
          445  +						return inv:get_list('starlit_suit_chips')
          446  +					end
   444    447   					function prop.giveItem(st)
   445    448   						u:thrustUpon(st)
          449  +					end
          450  +					function prop.drawCurrent(...)
          451  +						return u:suitDrawCurrent(...)
   446    452   					end
   447    453   					
   448    454   					if enabled and fn(u, prop, suitInterval, runState) then
   449    455   						runState.pgmsRun[s] = true
   450    456   					end
   451    457   				end
   452    458   			end

Modified mods/starlit/ui.lua from [60598b98d5] to [a436d8c893].

   161    161   		local cols = def.cols or math.huge
   162    162   		local startX = state.x
   163    163   		local maxX = startX
   164    164   		for _, w in ipairs(def) do idx = idx + 1
   165    165   			local src, st = starlit.ui.build(w, state)
   166    166   			-- TODO alignments
   167    167   			rowH = math.max(rowH, st.h)
   168         -			if idx > cols or def.w and (state.x + state.spacing + st.w > def.w) then
          168  +			if idx > cols or (def.w and (state.x + state.spacing + st.w > def.w)) then
   169    169   				totalH = totalH + rowH
   170    170   				state.x = startX
   171    171   				rowH,idx = 0,0
   172    172   				state.y = state.y + state.spacing + st.h
   173    173   			end
   174    174   			widget('container[%s,%s]%scontainer_end[]', state.x, state.y, src)
   175    175   			state.x=state.x + state.spacing + st.w
................................................................................
   289    289   		widget('label[%s,%s;%s]',
   290    290   			state.x, state.y + def.h*.5, txt)
   291    291   	elseif def.kind == 'text' then
   292    292   		-- TODO paragraph formatter
   293    293   		widget('hypertext[%s,%s;%s,%s;%s;%s]',
   294    294   			state.x, state.y, def.w, def.h, E(def.id), E(def.text))
   295    295   	elseif def.kind == 'hbar' or def.kind == 'vbar' then -- TODO fancy image bars
   296         -		local cl = lib.color(state.color)
          296  +		local cl = lib.color(def.color or state.color)
   297    297   		local fg = state.fg or cl:readable(.8,1)
   298    298   		local wfac, hfac = 1,1
   299    299   		local clamp = math.min(math.max(def.fac or 0, 0), 1)
   300    300   		if def.kind == 'hbar'
   301    301   			then wfac = wfac * clamp
   302    302   			else hfac = hfac * clamp
   303    303   		end

Modified mods/starlit/user.lua from [8fa84d176c] to [4cee014aa7].

   709    709   						minetest.item_drop(o, self.entity, where)
   710    710   					end
   711    711   				end
   712    712   				inv:set_list(lst, {})
   713    713   			end
   714    714   			dropInv 'main'
   715    715   			dropInv 'starlit_suit'
   716         -			self:statDelta('psi',          0, 'death', true)
   717         -			self:statDelta('nutrition', 1500, 'death', true)
   718         -			self:statDelta('hydration',    2, 'death', true)
   719         -			self:statDelta('fatigue',      0, 'death', true)
   720         -			self:statDelta('stamina',      0, 'death', true)
   721    716   			self:updateSuit()
   722    717   		end;
   723    718   		onRespawn = function(self)
   724    719   			local meta = self.entity:get_meta()
   725    720   			self.entity:set_pos(vector.from_string(meta:get_string'starlit_spawn'))
          721  +			self:statDelta('psi',          0, 'death', true)
          722  +			self:statDelta('nutrition', 1500, 'death', true)
          723  +			self:statDelta('hydration',    2, 'death', true)
          724  +			self:statDelta('fatigue',      0, 'death', true)
          725  +			self:statDelta('stamina',      0, 'death', true)
   726    726   			self:updateSuit()
   727    727   			return true
   728    728   		end;
   729    729   		onJoin = function(self)
   730    730   			local me = self.entity
   731    731   			local meta = me:get_meta()
   732    732   			self:pullPersona()