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
2
3
4































5
6
7
8
9
10
11
local lib = starlit.mod.lib

local E = {}
starlit.mod.electronics = E
































---------------------
-- item registries --
---------------------

-- a dynamo is any item that produces power and can be slotted into a power
-- source slot. this includes batteries, but also things like radiothermal




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







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
local lib = starlit.mod.lib

local E = {}
starlit.mod.electronics = E

--------------------------------
-- software context interface --
-------------------------------- ---------------------------------------
-- any time a program is run() or bgProc()eeded, a context argument MUST
-- be passed to the entry point. this argument must have at least the
-- following fields and functions:

-- string   context ('suit', 'device', etc)
--          names the context in which the program is being run
-- swCap    program
--          a structure as returned from usableSoftware(), providing the
--          program with access to and information about its substrate
-- function verify() --> bool
--          return true if the chip the program was loaded from is still
--          accessible
-- function drawCurrent(power, time, whatFor, [min]) --> J
--          attempt to draw current from the power source of whatever core
--          the program is running on
-- function pullConf() --> ()
--          updates the conf structure provided to the program as part of
--          its initial context
-- function saveConf([conf]) --> ()
--          saves any changes made to the conf structure
-- function availableChips() --> inventory list
--          returns the list of chips that are available in the execution
--          context. needed to retrieve system files

-- currently, this interface is implemented by
-- - starlit/interfaces.lua
-- - starlit/suit.lua

---------------------
-- item registries --
---------------------

-- a dynamo is any item that produces power and can be slotted into a power
-- source slot. this includes batteries, but also things like radiothermal

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

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

			::notfound:: do
				return
			end

			::found::
			local scm = starlit.item.sw.db[job.schematic]
			job.cyclesLeft = math.max(0, job.cyclesLeft - ctx.comp.cycles)
			if job.cyclesLeft == 0 then














				job.timeLeft = math.max(0, job.timeLeft - interval)

			end
			if job.timeLeft == 0 and job.cyclesLeft == 0 then



				table.remove(conf, jobSlot)
				user:give(scm.output)
				user:alarm(-2, 'item')
			else




				conf[jobSlot].value = job_t.enc(job)
			end


			ctx.saveConf()
		end;
	}
end

starlit.item.sw.link('starlit_electronics:compile_commune', matterCompiler {
	name = 'Compile Matter';







|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
<
>
>
>



<
>
>
>
>



>







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

			::notfound:: do
				return
			end

			::found::
			local scm = starlit.item.sw.db[job.schematic]
			local stages = {
				{id = 'cyclesLeft', decr=ctx.comp.cycles};
				{id = 'powerLeft', decr=function(current)
					return ctx.drawCurrent(current, interval, 'compile')
				end};
				{id = 'timeLeft', decr=interval};
				{id = 'numinaLeft', decr=0}; -- FIXME
			}

			for i,v in ipairs(stages) do
				if job[v.id] > 0 then
					local decr
					if type(v.decr) == 'function'
						then decr = v.decr(job[v.id])
						else decr = v.decr
					end
					job[v.id] = math.max(0, job[v.id] - decr)
					goto incomplete
				end

			end

			::complete:: do
				table.remove(conf, jobSlot)
				user:give(scm.output)
				user:alarm(-2, 'item')

				goto done
			end

			::incomplete:: do
				conf[jobSlot].value = job_t.enc(job)
			end

			::done::
			ctx.saveConf()
		end;
	}
end

starlit.item.sw.link('starlit_electronics:compile_commune', matterCompiler {
	name = 'Compile Matter';

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

4
5
6
7
8
9
10






11
12
13
14
15
16
17
..
18
19
20
21
22
23
24










25
26
27
28
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
131
132
133
134
135
136

M.element.meld {
	hydrogen = {
		name = 'hydrogen', sym = 'H', n = 1;  density = 8.988e-5;
		gas = true;
		color = lib.color(1,0.8,.3);
	};






	beryllium = {
		name = 'beryllium', sym = 'Be', n = 4;  density = 0;
		metal = true; -- rare emerald-stuff
		color = lib.color(0.2,1,0.2);
	};
	oxygen = {
		name = 'oxygen', sym = 'O', n = 8;  density = 0.001429;
................................................................................
		gas = true;
		color = lib.color(.2,1,.2);
	};
	carbon = {
		name = 'carbon', sym = 'C', n = 6, density = 2.266; -- g/cm³
		color = lib.color(.7,.2,.1);
	};










	silicon = {
		name = 'silicon', sym = 'Si', n = 14, density = 2.329;
		metal = true; -- can be forged into an ingot
		color = lib.color(.6,.6,.4);
	};
	neodymium = {
		name = 'neodymium', sym = 'Nd', n= 60, density = 7.01;
		metal = true;
		color = lib.color(1,1,1);
	};
	potassium = {
		name = 'potassium', sym = 'K', n = 19, density = 0.862;
		-- potassium is technically a metal but it's so soft
		-- it can be easily nanoworked without high temps, so
		-- ingots make no sense
		color = lib.color(1,.8,0.1);
	};
	calcium = {
		name = 'calcium', sym = 'Ca', n = 20; density = 1.55;
		metal = true;
		color = lib.color(1,1,0.7);
	};
	magnesium = {
		name = 'magnesium', sym = 'Mg', n = 12, density = 1.738;
		metal = true;
		color = lib.color(0.7, 0.7, 0.7);
	};
	aluminum = {
		name = 'aluminum', sym = 'Al', n = 13;  density = 2.7;
		metal = true;
		color = lib.color(0.5,.55,.6);
	};
	iron = {
		name = 'iron', sym = 'Fe', n = 26;  density = 7.874;
		metal = true;
		color = lib.color(.3,.3,.3);
	};
	copper = {
		name = 'copper', sym = 'Cu', n = 29;  density = 8.96;
		metal = true;
		color = lib.color(.8,.4,.1);
	};
	lithium = {
		name = 'lithium', sym = 'Li', n = 3;  density = 0.534;
		-- i think lithium is considered a metal but we don't mark it as
		-- one here because making a 'lithium ingot' is insane (even possible?)
		color = lib.color(1,0.8,.3);
	};
	titanium = {
		name = 'titanium', sym = 'Ti', n = 22;  density = 4.506;
		metal = true;
		color = lib.color(.7,.7,.7);
	};
	vanadium = {
		name = 'vanadium', sym = 'V', n = 23; density = 6;
		metal = true;
		color = lib.color(.3,0.5,.3);
	};





	nickel = {
		name = 'nickel', sym = 'ni', n = 28, density = 8.908;
		metal = true;
		color = lib.color(.7,.7,.6);
	};






















	xenon = {
		name = 'xenon', sym = 'Xe', n = 54;  density = 0.005894;
		gas = true;
		color = lib.color(.5,.1,1);
	};
	argon = {
		name = 'argon', sym = 'Ar', n = 18;  density = 0.001784;






		gas = true;
		color = lib.color(0,0.1,.9);
	};
	osmium = {
		name = 'osmium', sym = 'Os', n = 76;  density = 22.59;
		metal = true;
		color = lib.color(.8,.1,1);
	};
	iridium = {
		name = 'iridium', sym = 'Ir', n = 77; density = 22.56;
		metal = true;
		color = lib.color(.8,0,.5);
	};
	technetium = {
		name = 'technetium', sym = 'Tc', n = 43;  density = 11;
		desc = 'Prized by the higher Powers for subtle interactions that elude mere human scholars, technetium is of particular use in nuclear nanobatteries.';
		metal = true;
		color = lib.color(.2,0.2,1);
	};
	uranium = {
		name = 'uranium', sym = 'U', n = 92;  density = 19.1;
		desc = 'A weak but relatively plentiful nuclear fuel.';
		metal = true;
		color = lib.color(.2,.7,0);
	};
	thorium = {
		name = 'thorium', sym = 'Th', n = 90;  density = 11.7;
		desc = 'A frighteningly powerful nuclear fuel.';
		metal = true;
		color = lib.color(.7,.3,.1);
	};
	silver = {
		name = 'silver', sym = 'Ag', n = 47;  density = 10.49;
		metal = true;
		color = lib.color(.7,.7,.8);
	};
	gold = {
		name = 'gold', sym = 'Au', n = 79;  density = 19.30;
		metal = true;
		color = lib.color(1,.8,0);
	};
}







>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>





|
|
|
|













<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










>
>
>
>
>

|



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





|
|
>
>
>
>
>
>
|
|











<
<
<
<
<
<












<
<
<
<
<






4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
24
25
26
27
28
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






131
132
133
134
135
136
137
138
139
140
141
142





143
144
145
146
147
148

M.element.meld {
	hydrogen = {
		name = 'hydrogen', sym = 'H', n = 1;  density = 8.988e-5;
		gas = true;
		color = lib.color(1,0.8,.3);
	};
	lithium = {
		name = 'lithium', sym = 'Li', n = 3;  density = 0.534;
		-- i think lithium is considered a metal but we don't mark it as
		-- one here because making a 'lithium ingot' is insane (even possible?)
		color = lib.color(1,0.8,.3);
	};
	beryllium = {
		name = 'beryllium', sym = 'Be', n = 4;  density = 0;
		metal = true; -- rare emerald-stuff
		color = lib.color(0.2,1,0.2);
	};
	oxygen = {
		name = 'oxygen', sym = 'O', n = 8;  density = 0.001429;
................................................................................
		gas = true;
		color = lib.color(.2,1,.2);
	};
	carbon = {
		name = 'carbon', sym = 'C', n = 6, density = 2.266; -- g/cm³
		color = lib.color(.7,.2,.1);
	};
	magnesium = {
		name = 'magnesium', sym = 'Mg', n = 12, density = 1.738;
		metal = true;
		color = lib.color(0.7, 0.7, 0.7);
	};
	aluminum = {
		name = 'aluminum', sym = 'Al', n = 13;  density = 2.7;
		metal = true;
		color = lib.color(0.5,.55,.6);
	};
	silicon = {
		name = 'silicon', sym = 'Si', n = 14, density = 2.329;
		metal = true; -- can be forged into an ingot
		color = lib.color(.6,.6,.4);
	};
	argon = {
		name = 'argon', sym = 'Ar', n = 18;  density = 0.001784;
		gas = true;
		color = lib.color(0,0.1,.9);
	};
	potassium = {
		name = 'potassium', sym = 'K', n = 19, density = 0.862;
		-- potassium is technically a metal but it's so soft
		-- it can be easily nanoworked without high temps, so
		-- ingots make no sense
		color = lib.color(1,.8,0.1);
	};
	calcium = {
		name = 'calcium', sym = 'Ca', n = 20; density = 1.55;
		metal = true;
		color = lib.color(1,1,0.7);
	};


























	titanium = {
		name = 'titanium', sym = 'Ti', n = 22;  density = 4.506;
		metal = true;
		color = lib.color(.7,.7,.7);
	};
	vanadium = {
		name = 'vanadium', sym = 'V', n = 23; density = 6;
		metal = true;
		color = lib.color(.3,0.5,.3);
	};
	iron = {
		name = 'iron', sym = 'Fe', n = 26;  density = 7.874;
		metal = true;
		color = lib.color(.3,.3,.3);
	};
	nickel = {
		name = 'nickel', sym = 'Ni', n = 28, density = 8.908;
		metal = true;
		color = lib.color(.7,.7,.6);
	};
	copper = {
		name = 'copper', sym = 'Cu', n = 29;  density = 8.96;
		metal = true;
		color = lib.color(.8,.4,.1);
	};
	rubidium = {
		name = 'rubidium', sym = 'Rb', n = 37;  density = 1.532;
		color = lib.color(.8,.7,.7);
		-- this is a metal, but it's liquid at the temperatures we care about,
		-- so we don't set the metal flag
	};
	technetium = {
		name = 'technetium', sym = 'Tc', n = 43;  density = 11;
		desc = 'Prized by the higher Powers for subtle interactions that elude mere human scholars, technetium is of particular use in nuclear nanobatteries.';
		metal = true;
		color = lib.color(.2,0.2,1);
	};
	silver = {
		name = 'silver', sym = 'Ag', n = 47;  density = 10.49;
		metal = true;
		color = lib.color(.7,.7,.8);
	};
	xenon = {
		name = 'xenon', sym = 'Xe', n = 54;  density = 0.005894;
		gas = true;
		color = lib.color(.5,.1,1);
	};
	cesium = {
		name = 'cesium', sym = 'Cs', n = 55;  density = 1.93;
		color = lib.color(.5,.1,1);
		-- this is a metal, but it's liquid at the temperatures we care about,
		-- so we don't set the metal flag
	};
	neodymium = {
		name = 'neodymium', sym = 'Nd', n= 60, density = 7.01;
		metal = true;
		color = lib.color(1,1,1);
	};
	osmium = {
		name = 'osmium', sym = 'Os', n = 76;  density = 22.59;
		metal = true;
		color = lib.color(.8,.1,1);
	};
	iridium = {
		name = 'iridium', sym = 'Ir', n = 77; density = 22.56;
		metal = true;
		color = lib.color(.8,0,.5);
	};






	uranium = {
		name = 'uranium', sym = 'U', n = 92;  density = 19.1;
		desc = 'A weak but relatively plentiful nuclear fuel.';
		metal = true;
		color = lib.color(.2,.7,0);
	};
	thorium = {
		name = 'thorium', sym = 'Th', n = 90;  density = 11.7;
		desc = 'A frighteningly powerful nuclear fuel.';
		metal = true;
		color = lib.color(.7,.3,.1);
	};





	gold = {
		name = 'gold', sym = 'Au', n = 79;  density = 19.30;
		metal = true;
		color = lib.color(1,.8,0);
	};
}

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

27
28
29
30
31
32
33


34
35
36
37
38
39
40
..
44
45
46
47
48
49
50
51

52
53
54
55

56
57
58
59
60
61
62
...
265
266
267
268
269
270
271
272
273
274
275
276

277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
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
...
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
...
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448

449
450
451


452
453
454
455
456
457
458
459









460
461
462










463

464
465
466
467
468

469
470
471

472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
	local job_t = starlit.store.compilerJob
	local conf = state.pgm.file.body.conf
	table.insert(conf, {
		key='job', value=job_t.enc {
			schematic  = scm.id;
			cyclesLeft = scm.sw.cost and scm.sw.cost.cycles or 0;
			timeLeft   = (fab.time and fab.time.print or 0);


		}
	})
end

local function compilerCanPrint(user, cpl, scm)
	local E = starlit.mod.electronics
	local output = ItemStack(scm.sw.output):get_definition()
................................................................................
		user.entity:get_inventory():get_list 'main';
	}

	local cost = {
		fab = fab, output = output;
		consume = consume, unsat = unsat, leftover = leftover, itemSpec = itemSpec;
		runtimeEstimate = scm.speed + cpl.speed + (fab.time and fab.time.print or 0);
		power = cpl.powerCost + scm.powerCost;

		ram = (cpl.cost and cpl.cost.ram or 0)
		    + (scm.cost and scm.cost.ram or 0);
		cycles = (cpl.cost and cpl.cost.cycles or 0)
		       + (scm.cost and scm.cost.cycles or 0);

	}

	local userComp = E.chip.sumCompute(
		user.entity:get_inventory():get_list 'starlit_suit_chips'
	)

	if ok and cost.power <= user:suitCharge() and cost.ram <= userComp.ram then
................................................................................
						end
					end
				end

			end;

			render = function(state, user)
				local sel, pgmSelector = state.select, {}
				state.fetch()
				state.ctx.pullConf()

				local function pushSelector(id, item, label, desc, req)

					local rh = .5
					local label = {kind = 'text', w = 10-1.5, h=1.5;
							text = '<global valign=middle>'..lib.str.htsan(label) }
					if req then
						label.h = label.h - rh - .2

						local imgs = {}
						for ci,c in ipairs(req) do
							for ei, e in ipairs(c.list) do
								table.insert(imgs, {kind = 'img', w=rh, h=rh,  img=e.img})
							end
						end
						label = {kind = 'vert', w = 10-1.5, h=1.5;
							label;
							{kind ='hztl', w=10-1.5, h=rh; unpack(imgs); }
						}
					end
					table.insert(pgmSelector, {kind = 'hztl', w=10,h=1.5;
						{kind = 'contact', id=id, w=1.5, h=1.5;
							item = item;
							color = {hue=220, sat=0, lum=0};
							desc = desc;
						};
						label;
					})
				end

				local back = {kind = 'button', id='back', label = '<- Back', w=10,h=1.2}
				if sel.chips == nil then
					table.insert(pgmSelector, {kind = 'img', img = 'starlit-ui-alert.png', w=2, h=2})
				elseif sel.chip == nil then
					for i, c in ipairs(sel.chips.order) do
					-- TODO filter out chips without schematics?
						pushSelector('chip_' .. c.data.uuid, c.stack, c.data.label)
					end
					if next(sel.chips.order) then
						table.insert(pgmSelector, {kind = 'hztl', w=10,h=1.5;
							{kind = 'button', w=5,h=1.5; id='showAll', label='Show All'};
							{kind = 'button', w=5,h=1.5; id='find', label='Find'};
						})
					end
				else
					if sel.scm == nil then




						for idx, ent in ipairs(sel.scms) do
							local fab = ent.sw.input
							if fab.flag and fab.flag.print then
								local req = fab:visualize()
								pushSelector('scm_' .. idx, ent.sw.output, ent.sw.name, nil, req)
							end
						end

						table.insert(pgmSelector, back)
					else
						local scm = sel.scms[sel.scm]
						local output = ItemStack(scm.sw.output):get_definition()
						local fab = scm.sw.input
						local sw = scm.sw
						local function unmet(str)
							return lib.color(1,.3,.3):fmt(str)
						end
						table.insert(pgmSelector, {kind = 'hztl', w=10, h=1.2;
							{kind = 'img', item = sw.output, w=1.2, h=1.2, desc=output.description};
							{kind = 'text', text = string.format('<global valign=middle><b>%s</b>', lib.str.htsan(sw.name)), w=10-1.2,h=1.2};
						})
						local inputTbl = {kind = 'vert', w=5,h=0;
							{kind = 'hbar', w=5, h=.5, text=sel.input and 'Input Plan' or 'Input'}};
						local costTbl = {kind = 'vert', w=5,h=0;
							{kind = 'hbar', w=5, h=.5, text=sel.input and 'Process Plan' or 'Process'}};
						local reqPane = {kind = 'pane', id='reqPane', w=10, h=7;
							{kind = 'hztl', w=10,h=0; inputTbl, costTbl}
						}
						local function pushCost(x, t, val)
							table.insert(costTbl, {kind='label', w=4.5,h=.5,x=x;
								text=string.format('%s: %s',t,val);
							})
						end
						local function pushComputeCosts(header, p)
							if p then
								table.insert(costTbl, {kind = 'label', w=5, h=.5, x=0; text=header});
								if p.cycles then
									pushCost(.5, 'Compute', lib.math.siUI({'cycle','cycles'}, p.cycles, true))
								end
								if p.power then
									local str = lib.math.siUI('J', p.power)
									if p.power > user:suitCharge() then str = unmet(str) end
									pushCost(.5, 'Power', str)
................................................................................
									pushCost(.5, 'Memory', str)
								end
							end
						end

						local function fabToUI(x, inputTbl, req)
							for ci,c in ipairs(req) do
								table.insert(inputTbl, {kind = 'label', w=5-x, h=.5, x=x;
									text=lib.str.capitalize(c.header)});
								for ei,e in ipairs(c.list) do
									table.insert(inputTbl, {kind = 'hztl', w=4.5-x, h=.5, x=x+.5;
										{kind='img',   w=.5,h=.5, img=e.img};
										{kind='label', w=3.3,h=.5,x=.2, text=lib.str.capitalize(e.label)};
									});
								end
							end
						end

						local commitHue, commitLabel = 120
						if not sel.input then
................................................................................
								end
								fabToUI(0, inputTbl, vis)
							end
							if not ok then commitHue = 0 end
						end
						table.insert(pgmSelector, reqPane)
						table.insert(pgmSelector, {kind = 'hztl', w=10,h=1.2;
							{kind = 'button', id='back', label = '← Back', w=5,h=1.2};
							{kind = 'button', id='commit', label = commitLabel .. ' →', w=5,h=1.2, color={hue=commitHue,sat=0,lum=0}};
						})
					end
				end

				local rec = getRecentJobs(state)
				local recentList = {kind = 'hwrap', cols=3}
				if rec then
					for i,id in ipairs(rec) do
						local out = ItemStack(starlit.item.sw.db[id].output)
						table.insert(recentList, {kind='contact',w=1.5,h=1.5,id=string.format('recent_%s',i), item = out})
					end
				end
				local queue = {kind='pane', w=5, h=9.5}

				for i,v in ipairs(state.pgm.file.body.conf) do
					if v.key == 'job' then
						local job = starlit.store.compilerJob.dec(v.value)


						local scm = starlit.item.sw.db[job.schematic]
						local cycTotal = (scm.cost and scm.cost.cycles or 0)
						local secTotal = (scm.input.time and scm.input.time.print or 0)
						local cycPct = cycTotal == 0 and 1 or (1 - job.cyclesLeft/cycTotal)
						local secPct = secTotal == 0 and 1 or (1 - job.timeLeft/secTotal)
						local out = ItemStack(scm.output)
						local cycBar = {kind = 'hbar', fac = cycPct, w=5-1.5, h = .5;
							color={hue=150,sat=0,lum=0}; text=string.format("Compute %s%%", math.floor(cycPct*100));









						}
						local secBar = {kind = 'hbar', fac = secPct, w=5-1.5, h = .5;
							color={hue=50,sat=0,lum=0}; text=string.format("Assemble %s%%", math.floor(secPct*100));










						}

						table.insert(queue, {kind='hztl';  w=5, h=1.5;
							{kind = 'contact', w=1.5,h=1.5, id=string.format('cancel_%s', i), item = out};
							{kind = 'vert', w=5-1.5, h=1.5;
								{kind = 'label', text=out:get_definition().short_description, w=5-1.5, h=.75};
								(cycPct < 1 and cycBar or secBar);

							}
						})
					end

				end

				return starlit.ui.build {
					kind = 'hztl', padding = 0.5; w = 20, h = 10, mode = 'sw';
					{kind = 'vert', w = 5, h = 5;
						{kind = 'hbar', fac=0, w = 5, h = .5, text = '<b><left>Recent Prints</left></b>'};
						recentList;
					};
					{kind = 'vert', w = 10, h = 10;
						{kind = 'hbar', fac=0, w = 10, h = .5, text = '<b>Program Select</b>'};
						{kind = 'pane', w = 10, h = 9.5, id='pgmSelect';
							unpack(pgmSelector)
						};
					};
					{kind = 'vert', w = 5, h = 10;
						{kind = 'hbar', fac=0, w = 5, h = .5, text = '<b><right>Print Queue</right></b>'};
						queue;
					};
				}
			end;
		};
	};
})







>
>







 







|
>




>







 







|



|
>

|
|









|

|


|









|





|


|
|
|




>
>
>
>




|


>









|

|

|
|
|
|
|
|


|





|







 







|


|

|







 







|
|













>



>
>
|
<
<
<
|
|
|
|
>
>
>
>
>
>
>
>
>

<
<
>
>
>
>
>
>
>
>
>
>
|
>
|

|
|
<
>



>



|
|



|
|
|
|


|








27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
..
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
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
...
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
...
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465



466
467
468
469
470
471
472
473
474
475
476
477
478
479


480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495

496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
	local job_t = starlit.store.compilerJob
	local conf = state.pgm.file.body.conf
	table.insert(conf, {
		key='job', value=job_t.enc {
			schematic  = scm.id;
			cyclesLeft = scm.sw.cost and scm.sw.cost.cycles or 0;
			timeLeft   = (fab.time and fab.time.print or 0);
			powerLeft  = cost.power;
			numinaLeft = cost.numina;
		}
	})
end

local function compilerCanPrint(user, cpl, scm)
	local E = starlit.mod.electronics
	local output = ItemStack(scm.sw.output):get_definition()
................................................................................
		user.entity:get_inventory():get_list 'main';
	}

	local cost = {
		fab = fab, output = output;
		consume = consume, unsat = unsat, leftover = leftover, itemSpec = itemSpec;
		runtimeEstimate = scm.speed + cpl.speed + (fab.time and fab.time.print or 0);
		power = cpl.powerCost + scm.powerCost
		      + (sw.input.cost and sw.input.cost.power);
		ram = (cpl.cost and cpl.cost.ram or 0)
		    + (scm.cost and scm.cost.ram or 0);
		cycles = (cpl.cost and cpl.cost.cycles or 0)
		       + (scm.cost and scm.cost.cycles or 0);
		numina = fab.cost and fab.cost.numina or 0;
	}

	local userComp = E.chip.sumCompute(
		user.entity:get_inventory():get_list 'starlit_suit_chips'
	)

	if ok and cost.power <= user:suitCharge() and cost.ram <= userComp.ram then
................................................................................
						end
					end
				end

			end;

			render = function(state, user)
				local sel, pgmSelector = state.select, {kind = 'vert'}
				state.fetch()
				state.ctx.pullConf()

				local function pushSelector(tbl, id, item, label, desc, req, w)
					w = w or 12
					local rh = .5
					local label = {kind = 'text', w = w-1.5, h=1.6;
						text = '<global valign=middle>'..lib.str.htsan(label) }
					if req then
						label.h = label.h - rh - .2

						local imgs = {}
						for ci,c in ipairs(req) do
							for ei, e in ipairs(c.list) do
								table.insert(imgs, {kind = 'img', w=rh, h=rh,  img=e.img})
							end
						end
						label = {kind = 'vert', w = w-1.5, h=1.8;
							label;
							{kind ='hztl', w=w-1.5, h=rh; unpack(imgs); }
						}
					end
					table.insert(tbl, {kind = 'hztl', w=w,h=1.8;
						{kind = 'contact', id=id, w=1.5, h=1.5;
							item = item;
							color = {hue=220, sat=0, lum=0};
							desc = desc;
						};
						label;
					})
				end

				local back = {kind = 'button', id='back', label = '<- Back', w=12,h=1.2}
				if sel.chips == nil then
					table.insert(pgmSelector, {kind = 'img', img = 'starlit-ui-alert.png', w=2, h=2})
				elseif sel.chip == nil then
					for i, c in ipairs(sel.chips.order) do
					-- TODO filter out chips without schematics?
						pushSelector(pgmSelector, 'chip_' .. c.data.uuid, c.stack, c.data.label)
					end
					if next(sel.chips.order) then
						table.insert(pgmSelector, {kind = 'hztl', w=12,h=1.5;
							{kind = 'button', w=6,h=1.5; id='showAll', label='Show All'};
							{kind = 'button', w=6,h=1.5; id='find', label='Find'};
						})
					end
				else
					if sel.scm == nil then
						-- l m a o
-- 						pgmSelector.kind = 'hwrap'
-- 						pgmSelector.cols = 2
						local wrap = {kind='hwrap',cols=2,w=12}
						for idx, ent in ipairs(sel.scms) do
							local fab = ent.sw.input
							if fab.flag and fab.flag.print then
								local req = fab:visualize()
								pushSelector(wrap,'scm_' .. idx, ent.sw.output, ent.sw.name, nil, req,6)
							end
						end
						table.insert(pgmSelector, wrap)
						table.insert(pgmSelector, back)
					else
						local scm = sel.scms[sel.scm]
						local output = ItemStack(scm.sw.output):get_definition()
						local fab = scm.sw.input
						local sw = scm.sw
						local function unmet(str)
							return lib.color(1,.3,.3):fmt(str)
						end
						table.insert(pgmSelector, {kind = 'hztl', w=12, h=1.2;
							{kind = 'img', item = sw.output, w=1.2, h=1.2, desc=output.description};
							{kind = 'text', text = string.format('<global valign=middle><b>%s</b>', lib.str.htsan(sw.name)), w=12-1.2,h=1.2};
						})
						local inputTbl = {kind = 'vert', w=6,h=0;
							{kind = 'hbar', w=6, h=.5, text=sel.input and 'Input Plan' or 'Input'}};
						local costTbl = {kind = 'vert', w=6,h=0;
							{kind = 'hbar', w=6, h=.5, text=sel.input and 'Process Plan' or 'Process'}};
						local reqPane = {kind = 'pane', id='reqPane', w=12, h=8;
							{kind = 'hztl', w=12,h=0; inputTbl, costTbl}
						}
						local function pushCost(x, t, val)
							table.insert(costTbl, {kind='label', w=5.5,h=.5,x=x;
								text=string.format('%s: %s',t,val);
							})
						end
						local function pushComputeCosts(header, p)
							if p then
								table.insert(costTbl, {kind = 'label', w=6, h=.5, x=0; text=header});
								if p.cycles then
									pushCost(.5, 'Compute', lib.math.siUI({'cycle','cycles'}, p.cycles, true))
								end
								if p.power then
									local str = lib.math.siUI('J', p.power)
									if p.power > user:suitCharge() then str = unmet(str) end
									pushCost(.5, 'Power', str)
................................................................................
									pushCost(.5, 'Memory', str)
								end
							end
						end

						local function fabToUI(x, inputTbl, req)
							for ci,c in ipairs(req) do
								table.insert(inputTbl, {kind = 'label', w=6-x, h=.5, x=x;
									text=lib.str.capitalize(c.header)});
								for ei,e in ipairs(c.list) do
									table.insert(inputTbl, {kind = 'hztl', w=5.5-x, h=.5, x=x+.5;
										{kind='img',   w=.5,h=.5, img=e.img};
										{kind='label', w=4.3,h=.5,x=.2, text=lib.str.capitalize(e.label)};
									});
								end
							end
						end

						local commitHue, commitLabel = 120
						if not sel.input then
................................................................................
								end
								fabToUI(0, inputTbl, vis)
							end
							if not ok then commitHue = 0 end
						end
						table.insert(pgmSelector, reqPane)
						table.insert(pgmSelector, {kind = 'hztl', w=10,h=1.2;
							{kind = 'button', id='back', label = '← Back', w=6,h=1.2};
							{kind = 'button', id='commit', label = commitLabel .. ' →', w=6,h=1.2, color={hue=commitHue,sat=0,lum=0}};
						})
					end
				end

				local rec = getRecentJobs(state)
				local recentList = {kind = 'hwrap', cols=3}
				if rec then
					for i,id in ipairs(rec) do
						local out = ItemStack(starlit.item.sw.db[id].output)
						table.insert(recentList, {kind='contact',w=1.5,h=1.5,id=string.format('recent_%s',i), item = out})
					end
				end
				local queue = {kind='pane', w=5, h=9.5}
				local cst = state.ctx.availableChips()
				for i,v in ipairs(state.pgm.file.body.conf) do
					if v.key == 'job' then
						local job = starlit.store.compilerJob.dec(v.value)
						local scm = starlit.mod.electronics.chip.usableSoftware(cst, {{
							id = job.schematic;
							sw = starlit.item.sw.db[job.schematic];



						}})[1]
						if scm == nil then goto badJob end
						local steps = {
							{label = 'Compute', n = job.cyclesLeft, color=lib.color(0,1,0);
								total = scm.sw.cost and scm.sw.cost.cycles or 0};
							{label = 'Energize', n = job.powerLeft, color=lib.color(0,.5,1);
								total = (scm.powerCost or 0)
								      + (state.pgm.powerCost or 0)
								      + (scm.sw.input.cost and scm.sw.input.cost.power or 0)};
							{label = 'Assemble', n = job.timeLeft, color=lib.color(1,.2,0);
								total = scm.sw.input.time and scm.sw.input.time.print or 0};
							{label = 'Numenize', n = job.numinaLeft, color=lib.color(1,0,1);
								total = scm.sw.cost and scm.sw.cost.numina or 0};
						}


						local bar = {kind = 'hbar', text = 'Complete', fac=1, w=5-1.5, h = .5}
						for i, s in ipairs(steps) do
							if s.n > 0 then
								local pct = 1 - s.n/s.total
								bar.color = s.color
								bar.fac = pct
								bar.text = string.format("%s %s%%", s.label, math.floor(pct*100))
								break
							end
						end

						local out = ItemStack(scm.sw.output)
						table.insert(queue, {kind='hztl';  w=6, h=1.5;
							{kind = 'contact', w=1.5,h=1.5, id=string.format('cancel_%s', i), item = out};
							{kind = 'vert', w=6-1.5, h=1.5;
								{kind = 'label', text=out:get_definition().short_description, w=6-1.5, h=.75};

								bar;
							}
						})
					end
					::badJob::
				end

				return starlit.ui.build {
					kind = 'hztl', padding = 0.5; w = 22, h = 11, mode = 'sw';
					{kind = 'vert', w = 5, h = 15;
						{kind = 'hbar', fac=0, w = 5, h = .5, text = '<b><left>Recent Prints</left></b>'};
						recentList;
					};
					{kind = 'vert', w = 12, h = 11;
						{kind = 'hbar', fac=0, w = 12, h = .5, text = '<b>Program Select</b>'};
						{kind = 'pane', w = 12, h = 10.5, id='pgmSelect';
							pgmSelector
						};
					};
					{kind = 'vert', w = 5, h = 11;
						{kind = 'hbar', fac=0, w = 5, h = .5, text = '<b><right>Print Queue</right></b>'};
						queue;
					};
				}
			end;
		};
	};
})

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

191
192
193
194
195
196
197






198
199
200
201
202
203
204
...
378
379
380
381
382
383
384

385
386
387
388
389
390
391
							user:reconfigureSuit()
						end;
						pullConf = function()
							local stack = inv:get_stack('starlit_suit_chips', pgm.chipSlot)
							pgm.fd.chip=stack
							pgm.file = pgm.fd:read()
						end;






					}
				end

				if pgm.sw.powerKind == 'active' then
					if cfg then
						user:openUI(pgm.sw.ui, 'index', {
							context = 'suit';
................................................................................
				return starlit.ui.build {
					kind = 'vert', mode = 'sw';
					padding = 0.5;
				}
			end;
		};
		body = {

			render = function(state, user)
				local barh = .75
				local tb = {
					kind = 'vert', mode = 'sw';
					padding = 0.5, 
					{kind = 'hztl', padding = 0.25;
						{kind = 'label', text = 'Name', w = 2, h = barh};







>
>
>
>
>
>







 







>







191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
...
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
							user:reconfigureSuit()
						end;
						pullConf = function()
							local stack = inv:get_stack('starlit_suit_chips', pgm.chipSlot)
							pgm.fd.chip=stack
							pgm.file = pgm.fd:read()
						end;
						availableChips = function()
							return inv:get_list('starlit_suit_chips')
						end;
						drawCurrent = function(...)
							return user:suitDrawCurrent(...)
						end;
					}
				end

				if pgm.sw.powerKind == 'active' then
					if cfg then
						user:openUI(pgm.sw.ui, 'index', {
							context = 'suit';
................................................................................
				return starlit.ui.build {
					kind = 'vert', mode = 'sw';
					padding = 0.5;
				}
			end;
		};
		body = {
			refresh = true;
			render = function(state, user)
				local barh = .75
				local tb = {
					kind = 'vert', mode = 'sw';
					padding = 0.5, 
					{kind = 'hztl', padding = 0.25;
						{kind = 'label', text = 'Name', w = 2, h = barh};

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

23
24
25
26
27
28
29


30
31
32
33
34
35
36
	)
end

starlit.store.compilerJob = G.struct {
	schematic  = T.str;
	cyclesLeft = T.u64;
	timeLeft   = T.decimal;


}

starlit.store.persona = G.struct {
	name = T.str;
	species = T.str;
	speciesVariant = T.str;
	background = T.str;







>
>







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
	)
end

starlit.store.compilerJob = G.struct {
	schematic  = T.str;
	cyclesLeft = T.u64;
	timeLeft   = T.decimal;
	powerLeft  = T.decimal;
	numinaLeft  = T.decimal;
}

starlit.store.persona = G.struct {
	name = T.str;
	species = T.str;
	speciesVariant = T.str;
	background = T.str;

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

400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
...
437
438
439
440
441
442
443



444
445



446
447
448
449
450
451
452
	elseif act == 'move' then
		local item = inv:get_stack(p.to_list, p.to_index)
		slotChange(p.from_list, 'take', item)
		slotChange(p.to_list, 'put', item)
	end
end)

local suitInterval = 2.0
starlit.startJob('starlit:suit-software', suitInterval, function(delta)
	local runState = {
		pgmsRun = {};
		flags = {};
	}
	for id, u in pairs(starlit.activeUsers) do
		if not u:naked() then
................................................................................
						reconfSuit = true
					end
					function prop.pullConf()
						local stack = inv:get_stack('starlit_suit_chips', suitprog.chipSlot)
						prop.fd.chip=stack
						prop.file = prop.fd:read()
					end



					function prop.giveItem(st)
						u:thrustUpon(st)



					end
					
					if enabled and fn(u, prop, suitInterval, runState) then
						runState.pgmsRun[s] = true
					end
				end
			end







|







 







>
>
>


>
>
>







400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
...
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
	elseif act == 'move' then
		local item = inv:get_stack(p.to_list, p.to_index)
		slotChange(p.from_list, 'take', item)
		slotChange(p.to_list, 'put', item)
	end
end)

local suitInterval = 1.0
starlit.startJob('starlit:suit-software', suitInterval, function(delta)
	local runState = {
		pgmsRun = {};
		flags = {};
	}
	for id, u in pairs(starlit.activeUsers) do
		if not u:naked() then
................................................................................
						reconfSuit = true
					end
					function prop.pullConf()
						local stack = inv:get_stack('starlit_suit_chips', suitprog.chipSlot)
						prop.fd.chip=stack
						prop.file = prop.fd:read()
					end
					function prop.availableChips()
						return inv:get_list('starlit_suit_chips')
					end
					function prop.giveItem(st)
						u:thrustUpon(st)
					end
					function prop.drawCurrent(...)
						return u:suitDrawCurrent(...)
					end
					
					if enabled and fn(u, prop, suitInterval, runState) then
						runState.pgmsRun[s] = true
					end
				end
			end

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

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







|







 







|







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

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

709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725





726
727
728
729
730
731
732
						minetest.item_drop(o, self.entity, where)
					end
				end
				inv:set_list(lst, {})
			end
			dropInv 'main'
			dropInv 'starlit_suit'
			self:statDelta('psi',          0, 'death', true)
			self:statDelta('nutrition', 1500, 'death', true)
			self:statDelta('hydration',    2, 'death', true)
			self:statDelta('fatigue',      0, 'death', true)
			self:statDelta('stamina',      0, 'death', true)
			self:updateSuit()
		end;
		onRespawn = function(self)
			local meta = self.entity:get_meta()
			self.entity:set_pos(vector.from_string(meta:get_string'starlit_spawn'))





			self:updateSuit()
			return true
		end;
		onJoin = function(self)
			local me = self.entity
			local meta = me:get_meta()
			self:pullPersona()







<
<
<
<
<





>
>
>
>
>







709
710
711
712
713
714
715





716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
						minetest.item_drop(o, self.entity, where)
					end
				end
				inv:set_list(lst, {})
			end
			dropInv 'main'
			dropInv 'starlit_suit'





			self:updateSuit()
		end;
		onRespawn = function(self)
			local meta = self.entity:get_meta()
			self.entity:set_pos(vector.from_string(meta:get_string'starlit_spawn'))
			self:statDelta('psi',          0, 'death', true)
			self:statDelta('nutrition', 1500, 'death', true)
			self:statDelta('hydration',    2, 'death', true)
			self:statDelta('fatigue',      0, 'death', true)
			self:statDelta('stamina',      0, 'death', true)
			self:updateSuit()
			return true
		end;
		onJoin = function(self)
			local me = self.entity
			local meta = me:get_meta()
			self:pullPersona()