Differences From
Artifact [c1a1690c3b]:
12 12 img='starlit-ui-icon-nano.png', close=true, color = cmode'nano'};
13 13 {kind = 'contact', w=1.5,h=1.5, id = 'mode_weapon',
14 14 img='starlit-ui-icon-weapon.png', close=true, color = cmode'weapon'};
15 15 {kind = 'contact', w=1.5,h=1.5, id = 'mode_psi',
16 16 img='starlit-ui-icon-psi.png', close=true, color = cmode'psi'};
17 17 };
18 18 {kind = 'hztl';
19 - {kind = 'contact', w=1.5,h=1.5, id = 'open_elements',
19 + {kind = 'contact', w=1.5,h=1.5, id = 'open_nano',
20 20 img='starlit-ui-icon-element.png'};
21 21 {kind = 'contact', w=1.5,h=1.5, id = 'open_suit',
22 22 img='starlit-item-suit.png^[hsl:200:-.7:0'};
23 23 {kind = 'contact', w=1.5,h=1.5, id = 'open_psi',
24 24 img='starlit-ui-icon-psi-cfg.png'};
25 25 {kind = 'contact', w=1.5,h=1.5, id = 'open_body',
26 26 img='starlit-ui-icon-self.png'};
................................................................................
49 49 else
50 50 setSuitMode(e)
51 51 end
52 52 return true
53 53 end
54 54 end
55 55
56 - if fields.open_elements then
57 - user:openUI('starlit:user-menu', 'compiler')
56 + if fields.open_nano then
57 + user:openUI('starlit:user-menu', 'nano')
58 58 return true
59 59 elseif fields.open_psi then
60 60 user:openUI('starlit:user-menu', 'psi')
61 61 return true
62 62 elseif fields.open_suit then
63 63 if not user:naked() then
64 64 user:openUI('starlit:user-menu', 'suit')
................................................................................
118 118 end
119 119
120 120 local function abilityMenu(a)
121 121 -- select primary/secondary abilities or activate ritual abilities
122 122 local p = {kind = 'vert'}
123 123 for _, o in ipairs(a.order) do
124 124 local m = a.menu[o]
125 - table.insert(p, {kind='hbar', fac=0.5, text=string.format("<b>%s</b>",m.label), w=a.w, h = .5})
125 + table.insert(p, {kind='hbar', fac=0, text=string.format("<b>%s</b>",m.label), w=a.w, h = .5})
126 126 table.insert(p, wrapMenu(a.w, a.h, 1.2, 2, m.opts))
127 127 end
128 128 return p
129 129 end
130 130
131 131 local function pptrMatch(a,b)
132 132 if a == nil or b == nil then return false end
................................................................................
133 133 return (a.chipID ~= nil and (a.chipID == b.chipID and a.pgmIndex == b.pgmIndex))
134 134 or (a.ref ~= nil and a.ref == b.ref)
135 135 end
136 136
137 137 starlit.interface.install(starlit.type.ui {
138 138 id = 'starlit:user-menu';
139 139 pages = {
140 - compiler = {
140 + nano = {
141 141 setupState = function(state, user)
142 142 -- nanotech/suit software menu
143 143 local chips = user.entity:get_inventory():get_list 'starlit_suit_chips' -- FIXME need better subinv api
144 144 local sw = starlit.mod.electronics.chip.usableSoftware(chips)
145 145 state.suitSW = {}
146 146 local dedup = {}
147 147 for i, r in ipairs(sw) do if
................................................................................
168 168 end
169 169 end
170 170 if not pgm then return false end -- HAX
171 171
172 172 -- kind=active programs must be assigned to a command slot
173 173 -- kind=direct programs must open their UI
174 174 -- kind=passive programs must toggle on and off
175 + local inv = user.entity:get_inventory()
175 176 local function suitCtx(pgm)
176 - local chips = user.entity:get_inventory():get_list 'starlit_suit_chips'
177 - local pgmctx = starlit.mod.electronics.chip.usableSoftware(chips, {pgm})[1]
177 + local uuid = starlit.mod.electronics.chip.read(pgm.chip).uuid
178 178 return {
179 179 context = 'suit';
180 - program = pgmctx;
180 + program = pgm;
181 + verify = function() -- ew!!
182 + local chipInSlot = inv:get_stack('starlit_suit_chips', pgm.chipSlot)
183 + local csd = starlit.mod.electronics.chip.read(chipInSlot)
184 + return csd.uuid == uuid
185 + end;
186 + saveConf = function(cfg) cfg = cfg or pgm.file.body.conf
187 + pgm.file.body.conf = cfg
188 + pgm.fd:write(pgm.file)
189 + inv:set_stack('starlit_suit_chips', pgm.chipSlot, pgm.fd.chip)
190 + user:reconfigureSuit()
191 + end;
192 + pullConf = function()
193 + local stack = inv:get_stack('starlit_suit_chips', pgm.chipSlot)
194 + pgm.fd.chip=stack
195 + pgm.file = pgm.fd:read()
196 + end;
181 197 }
182 198 end
183 199
184 200 if pgm.sw.powerKind == 'active' then
185 201 if cfg then
186 202 user:openUI(pgm.sw.ui, 'index', {
187 203 context = 'suit';
................................................................................
495 511 if q.powerMode_off then suitMode = 'off'
496 512 elseif q.powerMode_save then suitMode = 'powerSave'
497 513 elseif q.powerMode_on then suitMode = 'on' end
498 514 if suitMode then
499 515 user:suitPowerStateSet(suitMode)
500 516 return true
501 517 end
502 - end;
503 - };
504 - };
505 -})
506 -
507 -local function compilerCanPrint(user, cpl, scm)
508 - local output = ItemStack(scm.sw.output):get_definition()
509 - local fab = output._starlit.fab
510 - local sw = scm.sw
511 - local ok, consume, unsat, leftover, itemSpec = fab:seek {
512 - user.entity:get_inventory():get_list 'main';
513 - }
514 -
515 - local cost = {
516 - consume = consume, unsat = unsat, leftover = leftover, itemSpec = itemSpec;
517 - runtimeEstimate = scm.speed + cpl.speed + (fab.time and fab.time.print or 0);
518 - power = cpl.powerCost + scm.powerCost;
519 - ram = (cpl.cost and cpl.cost.ram or 0)
520 - + (scm.cost and scm.cost.ram or 0);
521 - cycles = (cpl.cost and cpl.cost.cycles or 0)
522 - + (scm.cost and scm.cost.cycles or 0);
523 - }
524 -
525 - local userComp = starlit.mod.electronics.chip.sumCompute(
526 - user.entity:get_inventory():get_list 'starlit_suit_chips'
527 - )
528 -
529 - if ok and cost.power <= user:suitCharge() and cost.ram <= userComp.ram then
530 - return true, cost
531 - else return false, cost end
532 -end
533 -
534 --- TODO destroy suit interfaces when power runs out or suit/chip is otherwise disabled
535 -starlit.interface.install(starlit.type.ui {
536 - id = 'starlit:compile-matter-component';
537 - sub = {
538 - suit = function(state, user, evt)
539 - if evt.kind == 'disrobe' then state:close()
540 - elseif evt.kind == 'power' and evt.mode == 'off' then state:close() end
541 - end;
542 - playerInventory = function(state,user)
543 - -- refresh
544 - end;
545 - };
546 - pages = {
547 - index = {
548 - setupState = function(state, user, ctx)
549 - state.pgm = ctx.program
550 - state.select = {}
551 - local E = starlit.mod.electronics
552 - if ctx.context == 'suit' then
553 - state.fetch = function()
554 - local cst = user.entity:get_inventory():get_list 'starlit_suit_chips'
555 - local cl = {order={}, map={}, slot={}}
556 - for i, c in ipairs(cst) do
557 - if not c:is_empty() then
558 - local d = E.chip.read(c)
559 - local co = {
560 - stack = c;
561 - data = d;
562 - }
563 - table.insert(cl.order, co)
564 - cl.map[d.uuid] = co
565 - cl.slot[i] = co
566 - end
567 - end
568 -
569 - -- kill me fam
570 - if ( state.select.chip
571 - and state.select.chip ~= true
572 - and not cl.map[state.select.chip])
573 - or (state.select.scm
574 - and not state.select.scms[state.select.scm])
575 - then
576 - -- chip or pgm no longer available
577 - user:suitSound 'starlit-error'
578 - state.select = {}
579 - end
580 - state.select.chips = cl
581 -
582 - state.select.scms = {}
583 - if state.select.chip then
584 - state.select.scms = E.chip.usableSoftware(cst,nil, function(s)
585 - if state.select.chip ~= true then
586 - if cl.slot[s.chipSlot].data.uuid ~= state.select.chip then
587 - return false
588 - end
589 - end
590 - return s.sw.kind == 'schematic'
591 - end)
592 - end
593 -
594 - end
595 - end
596 - end;
597 -
598 - onClose = function(state, user)
599 - user:suitSound 'starlit-quit'
600 - end;
601 - handle = function(state, user, q)
602 - local sel = state.select
603 - state.fetch()
604 - local chips = state.select.chips
605 - local function chirp()
606 - user:suitSound 'starlit-nav'
607 - end
608 -
609 - local function trySelection(id)
610 - if sel[id] == nil then
611 - for k in next, q do
612 - local pat = "^"..id.."_(%d+)$" -- ew
613 - local idx = k:match(pat)
614 - if idx then
615 - local cm = tonumber(idx)
616 - if cm then
617 - chirp()
618 - sel[id] = cm
619 - return true
620 - end
621 - end
622 - end
623 - end
624 - end
625 -
626 - if sel.chip == nil then
627 - if q.showAll then
628 - chirp()
629 - sel.chip = true
630 - return true
631 - elseif q.find then
632 - chirp()
633 - -- TODO
634 - return true
635 - end
636 - end
637 -
638 - if trySelection('chip') then
639 - return true
640 - elseif trySelection('scm') then
641 - return true
642 - else
643 - if q.back then
644 - chirp()
645 - if sel.input then
646 - sel.input = nil
647 - elseif sel.scm then
648 - sel.scm = nil
649 - elseif sel.chip then
650 - sel.chip = nil
651 - end
652 - return true
653 - elseif q.commit then
654 - if not sel.input then
655 - chirp()
656 - sel.input = true
657 - return true
658 - else
659 - local scm = sel.scms[sel.scm]
660 - local ok, cost = compilerCanPrint(user, state.pgm, scm)
661 - if ok then
662 - user:suitSound 'starlit-configure'
663 - -- consume consumables
664 - -- add print job
665 - state.select = {}
666 - return true
667 - else
668 - user:suitSound 'starlit-error'
669 - end
670 - end
671 - end
672 - end
673 -
674 - end;
675 -
676 - render = function(state, user)
677 - local sel, pgmSelector = state.select, {}
678 - state.fetch()
679 -
680 - local function pushSelector(id, item, label, desc, req)
681 - local rh = .5
682 - local label = {kind = 'text', w = 10-1.5, h=1.5;
683 - text = '<global valign=middle>'..lib.str.htsan(label) }
684 - if req then
685 - label.h = label.h - rh - .2
686 -
687 - local imgs = {}
688 - for ci,c in ipairs(req) do
689 - for ei, e in ipairs(c.list) do
690 - table.insert(imgs, {kind = 'img', w=rh, h=rh, img=e.img})
691 - end
692 - end
693 - label = {kind = 'vert', w = 10-1.5, h=1.5;
694 - label;
695 - {kind ='hztl', w=10-1.5, h=rh; unpack(imgs); }
696 - }
697 - end
698 - table.insert(pgmSelector, {kind = 'hztl', w=10,h=1.5;
699 - {kind = 'contact', id=id, w=1.5, h=1.5;
700 - item = item;
701 - color = {hue=220, sat=0, lum=0};
702 - desc = desc;
703 - };
704 - label;
705 - })
706 - end
707 -
708 - local back = {kind = 'button', id='back', label = '<- Back', w=10,h=1.2}
709 - if sel.chips == nil then
710 - table.insert(pgmSelector, {kind = 'img', img = 'starlit-ui-alert.png', w=2, h=2})
711 - elseif sel.chip == nil then
712 - for i, c in ipairs(sel.chips.order) do
713 - -- TODO filter out chips without schematics?
714 - pushSelector('chip_' .. c.data.uuid, c.stack, c.data.label)
715 - end
716 - if next(sel.chips.order) then
717 - table.insert(pgmSelector, {kind = 'hztl', w=10,h=1.5;
718 - {kind = 'button', w=5,h=1.5; id='showAll', label='Show All'};
719 - {kind = 'button', w=5,h=1.5; id='find', label='Find'};
720 - })
721 - end
722 - else
723 - if sel.scm == nil then
724 - for idx, ent in ipairs(sel.scms) do
725 - local fab = ItemStack(ent.sw.output):get_definition()._starlit.fab
726 - if fab.flag.print then
727 - local req = fab:visualize()
728 - pushSelector('scm_' .. idx, ent.sw.output, ent.sw.name, nil, req)
729 - end
730 - end
731 - table.insert(pgmSelector, back)
732 - else
733 - local scm = sel.scms[sel.scm]
734 - local output = ItemStack(scm.sw.output):get_definition()
735 - local fab = output._starlit.fab
736 - local sw = scm.sw
737 - local function unmet(str)
738 - return lib.color(1,.3,.3):fmt(str)
739 - end
740 - table.insert(pgmSelector, {kind = 'hztl', w=10, h=1.2;
741 - {kind = 'img', item = sw.output, w=1.2, h=1.2, desc=output.description};
742 - {kind = 'text', text = string.format('<global valign=middle><b>%s</b>', lib.str.htsan(sw.name)), w=10-1.2,h=1.2};
743 - })
744 - local inputTbl = {kind = 'vert', w=5,h=0;
745 - {kind = 'hbar', w=5, h=.5, text=sel.input and 'Input Plan' or 'Input'}};
746 - local costTbl = {kind = 'vert', w=5,h=0;
747 - {kind = 'hbar', w=5, h=.5, text=sel.input and 'Process Plan' or 'Process'}};
748 - local reqPane = {kind = 'pane', id='reqPane', w=10, h=7;
749 - {kind = 'hztl', w=10,h=0; inputTbl, costTbl}
750 - }
751 - local function pushCost(x, t, val)
752 - table.insert(costTbl, {kind='label', w=4.5,h=.5,x=x;
753 - text=string.format('%s: %s',t,val);
754 - })
755 - end
756 - local function pushComputeCosts(header, p)
757 - if p then
758 - table.insert(costTbl, {kind = 'label', w=5, h=.5, x=0; text=header});
759 - if p.cycles then
760 - pushCost(.5, 'Compute', lib.math.siUI({'cycle','cycles'}, p.cycles, true))
761 - end
762 - if p.power then
763 - local str = lib.math.siUI('J', p.power)
764 - if p.power > user:suitCharge() then str = unmet(str) end
765 - pushCost(.5, 'Power', str)
766 - end
767 - if p.ram then
768 - local str = string.format("%s / %s",
769 - lib.math.siUI('B', p.ram),
770 - lib.math.siUI('B', state.pgm.comp.ram))
771 - if p.ram > state.pgm.comp.ram then str = unmet(str) end
772 - pushCost(.5, 'Memory', str)
773 - end
774 - end
775 - end
776 -
777 - local function fabToUI(x, inputTbl, req)
778 - for ci,c in ipairs(req) do
779 - table.insert(inputTbl, {kind = 'label', w=5-x, h=.5, x=x;
780 - text=lib.str.capitalize(c.header)});
781 - for ei,e in ipairs(c.list) do
782 - table.insert(inputTbl, {kind = 'hztl', w=4.5-x, h=.5, x=x+.5;
783 - {kind='img', w=.5,h=.5, img=e.img};
784 - {kind='label', w=3.3,h=.5,x=.2, text=lib.str.capitalize(e.label)};
785 - });
786 - end
787 - end
788 - end
789 -
790 - local commitHue=120, commitLabel
791 - if not sel.input then
792 - commitLabel = 'Plan'
793 - fabToUI(0, inputTbl, fab:visualize())
794 - local function pushComputeCostsSw(header, p)
795 - if p.sw.cost then
796 - pushComputeCosts(header, {
797 - cycles = p.sw.cost.cycles;
798 - power = p.powerCost;
799 - ram = p.sw.cost.ram;
800 - })
801 - end
802 - end
803 - pushComputeCostsSw('Schematic', scm)
804 - pushComputeCostsSw('Compiler', state.pgm)
805 - else
806 - commitLabel = 'Commit'
807 - pushComputeCosts('Total', {
808 - cycles = (scm.sw.cost and scm.sw.cost.cycles or 0)
809 - + (state.pgm.sw.cost and state.pgm.sw.cost.cycles or 0);
810 - power = (scm.powerCost or 0)
811 - + (state.pgm.powerCost or 0)
812 - + (fab.cost and fab.cost.power or 0);
813 - ram = (scm.sw.cost and scm.sw.cost.ram or 0)
814 - + (state.pgm.sw.cost and state.pgm.sw.cost.ram or 0);
815 - })
816 - if fab.time and fab.time.print then
817 - pushCost(0, 'Job Runtime', lib.math.timespec(fab.time.print + scm.speed))
818 - pushCost(.5, 'Print Time', lib.math.timespec(fab.time.print))
819 - pushCost(.5, 'CPU Time', lib.math.timespec(scm.speed + state.pgm.speed))
820 - end
821 - local ok, compileCost = compilerCanPrint(user, state.pgm, scm)
822 - fabToUI(0, inputTbl, compileCost.itemSpec:visualize())
823 -
824 - if next(compileCost.unsat) then
825 - local vis = compileCost.unsat:visualize()
826 - for si, s in ipairs(vis) do
827 - s.header = 'Missing ' .. s.header
828 - for ei, e in ipairs(s.list) do
829 - e.label = lib.color(1,.2,.2):fmt(e.label)
830 - end
831 - end
832 - fabToUI(0, inputTbl, vis)
833 - end
834 - if not ok then commitHue = 0 end
835 - end
836 - table.insert(pgmSelector, reqPane)
837 - table.insert(pgmSelector, {kind = 'hztl', w=10,h=1.2;
838 - {kind = 'button', id='back', label = '← Back', w=5,h=1.2};
839 - {kind = 'button', id='commit', label = commitLabel .. ' →', w=5,h=1.2, color={hue=commitHue,sat=0,lum=0}};
840 - })
841 - end
842 - end
843 -
844 - return starlit.ui.build {
845 - kind = 'hztl', padding = 0.5; w = 20, h = 10, mode = 'sw';
846 - {kind = 'vert', w = 5, h = 5;
847 - {kind = 'hbar', fac=0, w = 5, h = .5, text = '<b><left>Recent Prints</left></b>'};
848 - };
849 - {kind = 'vert', w = 10, h = 10;
850 - {kind = 'hbar', fac=0, w = 10, h = .5, text = '<b>Program Select</b>'};
851 - {kind = 'pane', w = 10, h = 9.5, id='pgmSelect';
852 - unpack(pgmSelector)
853 - };
854 - };
855 - {kind = 'vert', w = 5, h = 10;
856 - {kind = 'hbar', fac=0, w = 5, h = .5, text = '<b><right>Print Queue</right></b>'};
857 - };
858 - }
859 518 end;
860 519 };
861 520 };
862 521 })