Advertisement
klassekatze

KTZInv

Jan 24th, 2024 (edited)
1,819
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 89.63 KB | None | 0 0
  1. /*
  2.  * R e a d m e
  3.  * -----------
  4.  *
  5.  * In this file you can include any instructions or other comments you want to have injected onto the
  6.  * top of your final script. You can safely delete this file if you do not want any such comments.
  7.  */
  8.  
  9. static bool USE_SKITS = false;//whether to use survival kits in autocrafting
  10. static bool ASSEMBLE = true;//auto assemble items to the quota dictated by Autocraft LCD
  11. static bool DISASSEMBLE = false;//auto disassemble items in excess of quota
  12. static int ASSEMBLE_MARGIN = 50;//margin of error around quota before doing any of that
  13. static bool ASM_FLUSH = true;//whether to periodically clear inputs of an assembler that is not producing
  14. static bool ASM_SHUFFLE = false;//whether to periodically move the first item to back of queue if not producing
  15.  
  16.  
  17. static int MAX_TRANSFERS_PER_OP = 4;
  18. //moving items is fundamentally expensive, moreso that it seems. this limits the amount of actual stacks moved in a given operation.
  19. //Lower numbers make performance more consistent and thus reliable with things like pblimiter, but
  20. //can result in it taking several passes over the grid to get things to their final destinations if things are sufficiently disordered.
  21. static double MAX_TRANSFER_MS = 0.05;//secondary sancheck
  22.  
  23. static bool SORT = true;//whether to do any of that item sorting stuff at all.
  24.                         //Note: Even if sorting isn't otherwise used, input flushing of jammed assemblers won't
  25.                         //happen without a tagged cargo for Ingots, Components
  26. static bool MERGE_STACKS = true;
  27. //whether to merge multiple stacks of the same itemtype in a given container when possible
  28. //could cause desync, they say? idk. stacks should only be fragmented like this by player action
  29.  
  30. static bool ISYCOMPAT = true;
  31.  
  32. //these are blocks by DefinitionDisplayNameText
  33. //typically containers that cannot or should not be managed
  34. static string[] lockBlockTypes = {
  35. };
  36. static string[] hiddenBlockTypes = {
  37. "Cargo Crate",
  38. "Lockers",
  39. "Armory Lockers",
  40. "Armory",
  41. "Weapon Rack",
  42. "Control Seat",
  43. "Parachute Hatch",
  44. "Control Station",
  45. "Vending Machine"
  46. };
  47.  
  48. //PERFORMANCE CONTROLS
  49. static bool CARGODBG = false;
  50. static double maxScriptTimeMSPerSec = 0.1;//maximum ms per sec, exceeding causes the script to pause itself to compensate
  51. static public int blockInterval = 5;//ticks to wait for next block scan when nothing has been moved
  52. static public int blockIntervalMove = 15;//ticks to wait if the last op required moving items around
  53.  
  54.  
  55.  
  56. static public Program gProgram = null;
  57. static public DateTime bootTime;
  58. public Program()
  59. {
  60.     gProgram = this;
  61.     resourceLoader = new ResourceLoader();
  62.     resourceLoader.p = this;
  63.     bootTime = DateTime.Now;
  64.  
  65.     log("BOOT", LT.LOG_N);
  66.     //Config = new Config_();
  67.     Runtime.UpdateFrequency = UpdateFrequency.Update1;
  68. }
  69.  
  70. public void Save()
  71. {
  72.     // Called when the program needs to save its state. Use
  73.     // this method to save your state to the Storage field
  74.     // or some other means.
  75.     //
  76.     // This method is optional and can be removed if not
  77.     // needed.
  78. }
  79. public static int tick = -1;
  80. static BurnoutTrack bt60 = new BurnoutTrack(60, maxScriptTimeMSPerSec);
  81.  
  82. static Profiler initP = new Profiler("init");
  83. static Profiler mainP = new Profiler("main");
  84. public void Main(string arg, UpdateType upd)
  85. {
  86.     tick += 1;
  87.     if (bt60.burnoutpre()) return;
  88.  
  89.     if (tick % 20 == 0) if (Me.Closed)
  90.         {
  91.             Runtime.UpdateFrequency = UpdateFrequency.None;
  92.             return;
  93.         }
  94.     mainP.start();
  95.     main(arg, upd);
  96.     mainP.stop();
  97.     if (tick % 5 == 0)
  98.     {
  99.         Echo(tick.ToString());
  100.         if (profileLog != null) profileLog.WriteText("name:ms1t:ms60t\n" + Profiler.getAllReports());
  101.         if (gInv != null)
  102.         {
  103.             Echo(gInv.lastStatus);
  104.         }
  105.     }
  106.     if (consoleLog != null && tick % 5 == 0)
  107.     {
  108.         if (Logger.loggedMessagesDirty)
  109.         {
  110.             Logger.updateLoggedMessagesRender();
  111.             consoleLog.WriteText(Logger.loggedMessagesRender);
  112.         }
  113.     }
  114.     if (bt60.burnoutpost()) return;
  115. }
  116.  
  117.  
  118. static Inventory gInv = null;
  119. static Autocraft gAutocraft = null;
  120. static AssemblerMgr gAssemblerMgr = null;
  121.  
  122.  
  123. bool first = true;
  124. void main(string arg, UpdateType upd)
  125. {
  126.     initP.start();
  127.     if (tick % 10 == 0)
  128.     {
  129.         resourceLoader.update();
  130.     }
  131.     initP.stop();
  132.     if (resourceLoader.neverFullyLoaded)
  133.     {
  134.         Echo("INITIALIZING: " + resourceLoader.step + "/11");
  135.         if (statusLog != null) statusLog.WriteText("INIT: " + resourceLoader.step + "/11");
  136.         return;
  137.     }
  138.     if (first)
  139.     {
  140.         first = false;
  141.  
  142.         gInv = new Inventory();
  143.         gInv.updateContainers(inventoryBlocks);
  144.         gAssemblerMgr = new AssemblerMgr();
  145.         gAutocraft = new Autocraft();
  146.         log("Basic structures initialized, pausing for 1 second.");
  147.         bt60.setwait(60);
  148.     }
  149.     if (tick % 10 == 0) connectEvent2();
  150.     if (SLEEPING) return;
  151.  
  152.  
  153.     if (autocraftingLCD != null)
  154.     {
  155.         gAssemblerMgr.update();
  156.     }
  157.     gInv.update();
  158.     if (tick % 60 * 5 == 0)
  159.     {
  160.         //if (statusLog != null) statusLog.WriteText(invInterface.listInv());
  161.  
  162.         if (autocraftingLCD != null)
  163.         {
  164.             aclcd.s();
  165.             var txt = autocraftingLCD.GetText();
  166.             //if(txt.StartsWith(""))
  167.             gAutocraft.readLCD(txt);
  168.             aclcd.e();
  169.             aclcd2.s();
  170.             var o = gAutocraft.writeLCD();
  171.             autocraftingLCD.WriteText(o);
  172.             aclcd2.e();
  173.         }
  174.     }
  175.     if (arg == "clearasm")
  176.     {
  177.         foreach(var asm in assemblers)
  178.         {
  179.             asm.ClearQueue();
  180.         }
  181.     }
  182.     if (arg == "test")
  183.     {
  184.         var asm = assemblers[0];
  185.         var bp = Autocraft.blueprints.First().Value;
  186.  
  187.  
  188.  
  189.         List<MyProductionItem> pro = new List<MyProductionItem>();
  190.         asm.GetQueue(pro);
  191.         if (pro.Count == 0)
  192.         {
  193.             asm.AddQueueItem(bp, (MyFixedPoint)1000);
  194.         }
  195.         else
  196.         {
  197.             var e = pro[0];
  198.             asm.InsertQueueItem(0, bp, (MyFixedPoint)(-500));
  199.         }
  200.     }
  201.     else if (arg == "log")
  202.     {
  203.         Logger.writeSuperlog();
  204.     }
  205. }
  206. static Profiler aclcd = new Profiler("aclcd1");
  207. static Profiler aclcd2 = new Profiler("aclcd2");
  208.  
  209. bool SLEEPING = false;
  210.  
  211. static string nosort = "No Sorting";
  212. static string nosort2 = "No IIM";
  213. class ConnectorInfo
  214. {
  215.     bool lcon = false;
  216.     public IMyShipConnector connector = null;
  217.     public IMyShipConnector otherConnector = null;
  218.  
  219.     public bool sortConnected = false;
  220.     //public bool blockConnected = false;
  221.  
  222.     public bool upd()
  223.     {
  224.         var con = connector.Status == MyShipConnectorStatus.Connected;
  225.         var o = connector.OtherConnector;
  226.         if (lcon != con)
  227.         {
  228.             lcon = con;
  229.             if (!con) o = null;
  230.             otherConnector = o;
  231.             sortConnected = false;
  232.             if (otherConnector != null)
  233.             {
  234.                 var n = connector.CustomName;
  235.                 var n2 = otherConnector.CustomName;
  236.                 if (n.Contains(nosort) || n.Contains(nosort2) ||
  237.                 n2.Contains(nosort) || n2.Contains(nosort2))
  238.                 {
  239.                     sortConnected = false;
  240.                 }
  241.                 else sortConnected = true;
  242.             }
  243.             return true;
  244.         }
  245.         else return false;
  246.     }
  247.  
  248. }
  249. Dictionary<IMyShipConnector, ConnectorInfo> connectorState = new Dictionary<IMyShipConnector, ConnectorInfo>();
  250.  
  251. Dictionary<IMyCubeGrid, IMyShipConnector> getCgridmap = new Dictionary<IMyCubeGrid, IMyShipConnector>();
  252. List<IMyCubeGrid> getCbl = new List<IMyCubeGrid>();
  253. int ltick = -1;
  254. IMyShipConnector getRelevantConnector(IMyTerminalBlock b)
  255. {
  256.     if (ltick != tick)
  257.     {
  258.         getCgridmap.Clear();
  259.         getCbl.Clear();
  260.         foreach (var c in connectors)
  261.         {
  262.             var o = c.OtherConnector;
  263.             if (o != null) getCgridmap[c.OtherConnector.CubeGrid] = c;
  264.         }
  265.     }
  266.     IMyShipConnector match = null;
  267.     getCgridmap.TryGetValue(b.CubeGrid, out match);
  268.     if (match != null) return match;
  269.     else
  270.     {
  271.         if (!getCbl.Contains(b.CubeGrid))
  272.         {
  273.             foreach (var kvp in connectorState)
  274.             {
  275.                 var v = kvp.Value; ;
  276.                 if (v.otherConnector != null && v.otherConnector.CubeGrid == b.CubeGrid)
  277.                 {
  278.                     if (v.sortConnected) return getCgridmap[b.CubeGrid] = v.connector;
  279.                     else
  280.                     {
  281.                         getCbl.Add(b.CubeGrid);
  282.                         return null;
  283.                     }
  284.                 }
  285.             }
  286.             foreach (var kvp in connectorState)
  287.             {
  288.                 var v = kvp.Value; ;
  289.                 if (v.otherConnector != null && v.sortConnected)
  290.                 {
  291.                     if (b.IsSameConstructAs(v.otherConnector))
  292.                     {
  293.                         return getCgridmap[b.CubeGrid] = v.connector;
  294.                     }
  295.                 }
  296.             }
  297.         }
  298.         else return null;
  299.     }
  300.     getCbl.Add(b.CubeGrid);
  301.     return null;
  302. }
  303.  
  304.  
  305.  
  306.  
  307. bool cnctE = false;
  308. public void connectEvent2()
  309. {
  310.     try
  311.     {
  312.         bool evnt = false;
  313.         foreach (var c in connectors)
  314.         {
  315.             ConnectorInfo v = null;
  316.             connectorState.TryGetValue(c, out v);
  317.             if (v == null)
  318.             {
  319.                 v = new ConnectorInfo();
  320.                 v.connector = c;
  321.                 connectorState[c] = v;
  322.             }
  323.             evnt = v.upd() || evnt;
  324.         }
  325.  
  326.         if (evnt)
  327.         {
  328.  
  329.             log("connector change");
  330.             List<IMyProgrammableBlock> pgms = new List<IMyProgrammableBlock>();
  331.             GridTerminalSystem.GetBlocksOfType(pgms, b => b != Me && b.HasPlayerAccess(Me.OwnerId) && b.CustomData.StartsWith("KTZINV") && b.IsWorking);
  332.             bool awake = true;
  333.             if (pgms.Count > 0)
  334.             {
  335.                 bool stati = Me.CubeGrid.IsStatic;
  336.                 foreach (var c in pgms)
  337.                 {
  338.                     if (c.CubeGrid.IsStatic && !stati)
  339.                     {
  340.                         awake = false;
  341.                         break;
  342.                     }
  343.                 }
  344.                 if (awake)
  345.                 {
  346.                     foreach (var c in pgms)
  347.                     {
  348.                         if (c.EntityId < Me.EntityId && (!stati || c.CubeGrid.IsStatic))
  349.                         {
  350.                             awake = false;
  351.                             break;
  352.                         }
  353.                     }
  354.                 }
  355.             }
  356.             SLEEPING = !awake;
  357.             if (SLEEPING)
  358.             {
  359.                 gInv.lastStatus = "Sleeping while a connected KTZInv runs.";
  360.                 if (statusLog != null) statusLog.WriteText(gInv.lastStatus);
  361.                 return;
  362.             }
  363.  
  364.             List<IMyTerminalBlock> blox = new List<IMyTerminalBlock>();
  365.             GridTerminalSystem.GetBlocksOfType(blox);
  366.             inventoryBlocks.Clear();
  367.             List<IMyCubeGrid> subGrids = new List<IMyCubeGrid>();
  368.             List<IMyCubeGrid> notSubGrids = new List<IMyCubeGrid>();
  369.             foreach (var b in blox)
  370.             {
  371.                 if (b.HasInventory && b.HasPlayerAccess(Me.OwnerId))
  372.                 {
  373.                     if (b.CubeGrid == Me.CubeGrid) inventoryBlocks.Add(b);
  374.                     else
  375.                     {
  376.                         if (!notSubGrids.Contains(b.CubeGrid))
  377.                         {
  378.                             if (b.IsSameConstructAs(Me)) subGrids.Add(b.CubeGrid);
  379.                             else notSubGrids.Add(b.CubeGrid);
  380.                         }
  381.                         if (subGrids.Contains(b.CubeGrid)) inventoryBlocks.Add(b);
  382.                         else
  383.                         {
  384.                             var rc = getRelevantConnector(b);
  385.                             if (rc != null)
  386.                             {
  387.                                 ConnectorInfo v = null;
  388.                                 connectorState.TryGetValue(rc, out v);
  389.                                 if (v != null && v.sortConnected)
  390.                                 {
  391.                                     inventoryBlocks.Add(b);
  392.                                 }
  393.                             }
  394.                         }
  395.                     }
  396.                 }
  397.             }
  398.             gInv.updateContainers(inventoryBlocks);
  399.         }
  400.     }
  401.     catch (Exception e)//because torch or something i forget
  402.     {
  403.         if (!cnctE)
  404.         {
  405.             cnctE = true;
  406.             log("Exception: " + e.ToString());
  407.         }
  408.     }
  409. }
  410.  
  411. class AssemblerMgr
  412. {
  413.     //List<BPLearn2> bplearners = new List<BPLearn2>();
  414.     List<Asmstate> asmstates = new List<Asmstate>();
  415.     class Asmstate
  416.     {
  417.         public BPLearn2 bpl = null;
  418.         public int flushTick = 0;
  419.         public int lastProduced = 0;
  420.     }
  421.  
  422.  
  423.  
  424.  
  425.  
  426.  
  427.     public AssemblerMgr()
  428.     {
  429.         foreach(var a in Program.assemblers)
  430.         {
  431.             var l = new BPLearn2();
  432.             l.asm = a;
  433.             var s = new Asmstate();
  434.             s.bpl = l;
  435.             asmstates.Add(s);
  436.             //bplearners.Add(l);
  437.         }
  438.     }
  439.     Dictionary<MyDefinitionId, List<IMyAssembler>> bpassemblers = new Dictionary<MyDefinitionId, List<IMyAssembler>>();
  440.  
  441.     int shuffleidx = 0;
  442.     public void shuffleAssemblers()
  443.     {
  444.         shuffleidx = (shuffleidx + 1) % assemblers.Count;
  445.         if (tick % 10 == 0 && assemblers.Count > 0)
  446.         {
  447.             var state = asmstates[shuffleidx];
  448.             var asm = assemblers[shuffleidx];
  449.  
  450.             if (asm.IsQueueEmpty || asm.IsProducing || !asm.Enabled)
  451.             {
  452.                 state.flushTick = state.lastProduced = tick;
  453.             }
  454.             else
  455.             {
  456.                 if (tick - state.flushTick > 60 * 17)
  457.                 {
  458.                     log("flushing " + asm.CustomName);
  459.                     //IMyInventory input = null;
  460.                     //if (asm.Mode == MyAssemblerMode.Assembly) input = asm.InputInventory;
  461.                     //else input = asm.OutputInventory;
  462.                     List<MyInventoryItem> itms = new List<MyInventoryItem>();
  463.                     asm.InputInventory.GetItems(itms);
  464.                     List<MyInventoryItem> itms2 = new List<MyInventoryItem>();
  465.                     asm.OutputInventory.GetItems(itms2);
  466.                     itms.AddRange(itms2);
  467.                     foreach (var itm in itms)
  468.                     {
  469.                         Inventory.BlockInventory bi = Inventory.BlockInventory.getBI(asm);
  470.                         Inventory.expel(bi, itm.Type, itm.Amount, true);
  471.                     }
  472.                     state.flushTick = tick;
  473.                 }
  474.                 if (tick - state.lastProduced > 60 * 30)
  475.                 {
  476.                     log("rear shuffling " + asm.CustomName);
  477.                     List<MyProductionItem> queue = new List<MyProductionItem>();
  478.                     asm.GetQueue(queue);
  479.                     if (queue.Count > 1)
  480.                     {
  481.                         var po = queue[0];
  482.                         asm.RemoveQueueItem(0, po.Amount);
  483.                         asm.AddQueueItem(po.BlueprintId, po.Amount);
  484.                     }
  485.                     //asm.MoveQueueItemRequest(queue[0].ItemId, queue.Count-1);
  486.                     state.flushTick = state.lastProduced = tick;
  487.                 }
  488.             }
  489.         }
  490.     }
  491.     //int lastshuffle = 0;
  492.     //int shufflefreq = 60 * 2;
  493.     public void balanceAssemblers()
  494.     {
  495.  
  496.         /*
  497.  
  498.  
  499.                 if (tick % 60 == 0)
  500.                 {
  501.                     for (int i = 0; i < assemblers.Count; i++)
  502.                     {
  503.                         var state = asmstates[i];
  504.                         var asm = assemblers[i];
  505.  
  506.  
  507.                     }
  508.                 }
  509.                 */
  510.         balanceAssemblers(MyAssemblerMode.Assembly);
  511.         balanceAssemblers(MyAssemblerMode.Disassembly);
  512.     }
  513.  
  514.     //i.e. if you say steel plate, assembling, it will erase any steel plate disassembly jobs
  515.     //since disassembling directly contradicts the goal of assembling
  516.     public void clearContradictingJobs(MyDefinitionId bp, MyAssemblerMode m)
  517.     {
  518.         foreach (var a in assemblers)
  519.         {
  520.             if (a.Mode != m)
  521.             {
  522.                 List<MyProductionItem> queue = new List<MyProductionItem>();
  523.                 a.GetQueue(queue);
  524.                 for (var i = 0; i < queue.Count; i++)
  525.                 {
  526.                     var itm = queue[i];
  527.                     if(itm.BlueprintId == bp)
  528.                     {
  529.                         a.RemoveQueueItem(i, itm.Amount);
  530.                         queue.RemoveAt(i);
  531.                         i--;
  532.                     }
  533.                 }
  534.             }
  535.         }
  536.     }
  537.  
  538.  
  539.     public void balanceAssemblers(MyAssemblerMode m, Dictionary<MyDefinitionId, MyFixedPoint> set = null)
  540.     {
  541.         Dictionary<MyDefinitionId, MyFixedPoint> orders = new Dictionary<MyDefinitionId, MyFixedPoint>();
  542.  
  543.         //List<List<MyProductionItem>> queues = new List<List<MyProductionItem>>();
  544.         Dictionary<IMyAssembler, List<MyProductionItem>> queues = new Dictionary<IMyAssembler, List<MyProductionItem>>();
  545.         foreach (var a in assemblers)
  546.         {
  547.             if (a.Mode == m)
  548.             {
  549.                 List<MyProductionItem> queue = new List<MyProductionItem>();
  550.                 a.GetQueue(queue);
  551.                 for (var i = 0; i < queue.Count; i++)
  552.                 {
  553.                     var itm = queue[i];
  554.                     if (itm.Amount < (MyFixedPoint)1)//because keen. look idk man
  555.                     {
  556.                         a.RemoveQueueItem(i, itm.Amount);
  557.                         queue.RemoveAt(i);
  558.                         i--;
  559.                     }
  560.                     else
  561.                     {
  562.                         var bp = itm.BlueprintId;
  563.                         if (orders.ContainsKey(bp)) orders[bp] += itm.Amount;
  564.                         else orders[bp] = itm.Amount;
  565.                     }
  566.                 }
  567.                 queues[a] = queue;
  568.             }
  569.         }
  570.  
  571.         if (set != null)
  572.         {
  573.             foreach (var kvp in set)
  574.             {
  575.                 MyFixedPoint ct = 0;
  576.                 orders.TryGetValue(kvp.Key, out ct);
  577.                 if (m == MyAssemblerMode.Assembly && kvp.Value > ct) orders[kvp.Key] = kvp.Value;
  578.                 else if (m == MyAssemblerMode.Disassembly && -kvp.Value > ct) orders[kvp.Key] = -kvp.Value;
  579.             }
  580.         }
  581.  
  582.         foreach (var kvp in orders)
  583.         {
  584.             var bp = kvp.Key;
  585.             var amt = kvp.Value;
  586.             List<IMyAssembler> relevant_assemblers = new List<IMyAssembler>();
  587.             foreach (var a in assemblers)
  588.             {
  589.                 if (a.Mode == m && a.CanUseBlueprint(bp)) relevant_assemblers.Add(a);
  590.             }
  591.             if (relevant_assemblers.Count == 0) continue;
  592.             int divided = (int)amt / relevant_assemblers.Count;
  593.             int remainder = (int)amt - (divided * (relevant_assemblers.Count-1));
  594.             for(int i = 0; i < relevant_assemblers.Count; i++)
  595.             {
  596.                 var asm = relevant_assemblers[i];
  597.                 var queue = queues[asm];
  598.  
  599.                 var t_v = i == (relevant_assemblers.Count - 1) ? remainder : divided;
  600.  
  601.                 if (t_v > -1 && t_v < 1) t_v = 0;
  602.  
  603.                 MyProductionItem citem = new MyProductionItem();
  604.                 int idx = -1;
  605.  
  606.                 for (var e = 0; e < queue.Count; e++)
  607.                 {
  608.                     var n = queue[e];
  609.                     if(n.BlueprintId == bp)
  610.                     {
  611.                         citem = n;
  612.                         idx = e;
  613.                         break;
  614.                     }
  615.                 }
  616.                 if (idx != -1)
  617.                 {
  618.                     var c_v = citem.Amount;
  619.                     var diff = t_v - c_v;
  620.  
  621.                     if (Math.Abs((int)diff) >= 3)
  622.                     {
  623.                         //log("doin it: "+diff);
  624.                         asm.InsertQueueItem(idx, bp, diff);
  625.                     }
  626.                 }
  627.                 else if(t_v != 0)asm.AddQueueItem(bp, (MyFixedPoint)t_v);
  628.             }
  629.         }
  630.     }
  631.     static Profiler shufP = new Profiler("asmshuf");
  632.     static Profiler balP = new Profiler("asmbal");
  633.     int lbal = 0;
  634.     public void update()
  635.     {
  636.         if (!gInv.hasUpdatedOnce) return;
  637.  
  638.         shufP.s();
  639.         if (tick % 60 * 15 == 0)
  640.         {
  641.             if(ASM_SHUFFLE)shuffleAssemblers();
  642.         }
  643.         shufP.e();
  644.         balP.s();
  645.         if (tick % 60 * 7 == 0)
  646.         {
  647.             if(ASM_FLUSH)balanceAssemblers();
  648.         }
  649.         balP.e();
  650.  
  651.         foreach (var l in asmstates) l.bpl.update();
  652.  
  653.         if (tick % 60 == 0 && tick - gInv.lastUpdateTick <= 60)
  654.         {
  655.             Dictionary<MyDefinitionId, MyFixedPoint> production = new Dictionary<MyDefinitionId, MyFixedPoint>();
  656.             foreach (var l in asmstates)
  657.             {
  658.                 foreach (var i in l.bpl.lastQueue)
  659.                 {
  660.                     if (!production.ContainsKey(i.BlueprintId)) production.Add(i.BlueprintId, i.Amount);
  661.                     else production[i.BlueprintId] += i.Amount;
  662.                 }
  663.             }
  664.             Dictionary<MyDefinitionId, MyFixedPoint> orders = new Dictionary<MyDefinitionId, MyFixedPoint>();
  665.             int asmjobs = 0;
  666.             int dasmjobs = 0;
  667.             foreach (var kvp in Autocraft.quotas_bp)
  668.             {
  669.                 var itembp = kvp.Key;
  670.                 var recipebp = Autocraft.blueprints[itembp];
  671.  
  672.                 var desired_amt = kvp.Value;
  673.                 //var assembling_amt = production.ContainsKey(recipebp) ? production[recipebp] : 0;
  674.                 MyFixedPoint current_amt = 0;
  675.                 Inventory.globalManifest.stuff.TryGetValue((MyItemType)itembp, out current_amt);
  676.  
  677.                 if (ASSEMBLE && current_amt + ASSEMBLE_MARGIN < desired_amt)
  678.                 {
  679.                     orders[recipebp] = desired_amt - current_amt;
  680.                     asmjobs += 1;
  681.                 }
  682.                 if (DISASSEMBLE && current_amt - ASSEMBLE_MARGIN > desired_amt)
  683.                 {
  684.                     orders[recipebp] = desired_amt - current_amt;
  685.                     dasmjobs += 1;
  686.                 }
  687.             }
  688.  
  689.             if(orders.Count > 0)
  690.             {
  691.                 if(asmjobs == 0 && dasmjobs != 0)
  692.                 {
  693.                     foreach (var s in assemblers) if(s.IsQueueEmpty)s.Mode = MyAssemblerMode.Disassembly;
  694.                 }else if (asmjobs != 0 && dasmjobs == 0)
  695.                 {
  696.                     foreach (var s in assemblers) if (s.IsQueueEmpty) s.Mode = MyAssemblerMode.Assembly;
  697.                 }
  698.  
  699.                 if (ASSEMBLE)
  700.                 {
  701.                     foreach(var kvp in orders)
  702.                     {
  703.                         if (kvp.Value > 0) clearContradictingJobs(kvp.Key,MyAssemblerMode.Assembly);
  704.                     }
  705.                     balanceAssemblers(MyAssemblerMode.Assembly, orders);
  706.                 }
  707.                 if (DISASSEMBLE)
  708.                 {
  709.                     foreach (var kvp in orders)
  710.                     {
  711.                         if (kvp.Value < 0) clearContradictingJobs(kvp.Key, MyAssemblerMode.Disassembly);
  712.                     }
  713.                     balanceAssemblers(MyAssemblerMode.Disassembly, orders);
  714.                 }
  715.             }
  716.         }
  717.         //if(tick % 60*60*10 == 0)
  718.         //{
  719.             /*foreach(var asm in assemblers)
  720.                     {
  721.                         List<MyInventoryItem> items = new List<MyInventoryItem>();
  722.                         //asm.GetInventory(0);
  723.                         asm.InputInventory.GetItems(items);
  724.                         foreach(var i in items) invInterface_noasm.TransferItemTo
  725.                     }*/
  726.             //invInterface_noasm
  727.  
  728.  
  729.             /*List<MyInventoryItem> items = new List<MyInventoryItem>();
  730.  
  731.                     //tick2 += 1;
  732.                     //tick3 += 1;
  733.  
  734.  
  735.                     asm.GetQueue(queue);
  736.                     //todo: if get queue and unknown recipe in it, flush the output inventory of the assembler
  737.  
  738.  
  739.  
  740.                     if (queue.Count != 0 || lastQueue.Count != 0)
  741.                     {
  742.                         asm.OutputInventory.GetItems(items);
  743.                     }*/
  744.     }
  745. }
  746.  
  747. class Autocraft
  748. {
  749.     //Dictionary<MyItemType, MyDefinitionId> bpcache;
  750.  
  751.  
  752.     static public MyItemType nop = MyItemType.MakeComponent("SteelPlate");
  753.     static Dictionary<string, MyItemType> typecast = new Dictionary<string, MyItemType>();
  754.     public static bool canFind(string subtype, out MyItemType t)
  755.     {
  756.         if(typecast.ContainsKey(subtype))
  757.         {
  758.             t = typecast[subtype];
  759.             return true;
  760.         }
  761.         foreach (var bpkvp in blueprints)
  762.         {
  763.             if (bpkvp.Key.SubtypeId.ToString() == subtype)
  764.             {
  765.                 typecast[subtype] = bpkvp.Key;
  766.                 t = bpkvp.Key;
  767.                 return true;
  768.             }
  769.         }
  770.         t = nop;
  771.         return false;
  772.     }
  773.  
  774.  
  775.  
  776.     static public Dictionary<string, int> quotas = new Dictionary<string, int>();
  777.     static public Dictionary<MyDefinitionId, int> quotas_bp = new Dictionary<MyDefinitionId, int>();
  778.  
  779.     static public Dictionary<MyDefinitionId, MyDefinitionId> blueprints = new Dictionary<MyDefinitionId, MyDefinitionId>();
  780.     static public void addBP(MyDefinitionId item, MyDefinitionId bp)
  781.     {
  782.         blueprints[item] = bp;
  783.         writeCD();
  784.     }
  785.     static public void writeCD()
  786.     {
  787.         string newcd = "KTZINV;\nitemID;blueprintID";
  788.         foreach (var kvp in blueprints)
  789.         {
  790.             newcd += "\n" + kvp.Key.ToString() + ";" + kvp.Value.ToString();
  791.         }
  792.         gProgram.Me.CustomData = newcd;
  793.     }
  794.  
  795.  
  796.     public Autocraft()
  797.     {
  798.         var cd = gProgram.Me.CustomData;
  799.         var spl = cd.Split('\n');
  800.         foreach(var l in spl)
  801.         {
  802.             var s2 = l.Split(';');
  803.             if(s2.Length >= 2)
  804.             {
  805.                 try
  806.                 {
  807.                     var itembp = MyDefinitionId.Parse(s2[0]);
  808.                     try
  809.                     {
  810.                         var recipebp = MyDefinitionId.Parse(s2[1]);
  811.                         blueprints[itembp] = recipebp;
  812.                     }
  813.                     catch (Exception) { }
  814.                 }
  815.                 catch (Exception){}
  816.             }
  817.         }
  818.     }
  819.  
  820.     //key=item, val=production bp
  821.     static Profiler p1 = new Profiler("p1");
  822.     static Profiler p2 = new Profiler("p2");
  823.     static Profiler p3 = new Profiler("p3");
  824.     public string writeLCD()
  825.     {
  826.         p1.s();
  827.         Dictionary<string, int> avail = new Dictionary<string, int>();
  828.  
  829.         foreach(var kvp in Inventory.globalManifest.stuff)
  830.         {
  831.             string subtype = kvp.Key.SubtypeId;//.Substring("MyObjectBuilder_".Length);//kvp.Key.SubtypeId.Replace("MyObjectBuilder_", "").Replace("_", " ");
  832.             if (!quotas.ContainsKey(subtype))
  833.             {
  834.                 var nfo = kvp.Key.GetItemInfo();
  835.                 if (!nfo.IsOre && !nfo.IsIngot)quotas[subtype] = 0;
  836.                 else
  837.                 {
  838.                     MyItemType derp = Autocraft.nop;
  839.                     if(canFind(subtype, out derp))
  840.                     {
  841.                         quotas[subtype] = 0;
  842.                         break;
  843.                     }
  844.  
  845.                     /*foreach (var bpkvp in blueprints)
  846.                             {
  847.                                 if (bpkvp.Key.SubtypeId.ToString() == kvp.Key.SubtypeId)
  848.                                 {
  849.                                     quotas[name] = 0;
  850.                                     break;
  851.                                 }
  852.                             }*/
  853.                 }
  854.                 //todo check if we have a bp, if bp ignore type status, fusion fuel etc
  855.             }
  856.             avail[subtype] = (int)kvp.Value;
  857.         }
  858.         p1.e();
  859.         p2.s();
  860.         StringBuilder b = new StringBuilder("Component Current | Wanted\n");
  861.  
  862.         foreach (var kvp in quotas)
  863.         {
  864.             int av = 0;
  865.             int quota = kvp.Value;
  866.             avail.TryGetValue(kvp.Key, out av);
  867.  
  868.             b.Append(kvp.Key);
  869.             b.Append(" ");
  870.             b.Append(av);
  871.             b.Append(" ");
  872.  
  873.             if (av < quota) b.Append("<");
  874.             else if (av == quota) b.Append("=");
  875.             else b.Append(">");
  876.  
  877.             b.Append(" ");
  878.             b.Append(quota);
  879.  
  880.             bool hasbp = false;
  881.             p3.s();
  882.             foreach (var bpkvp in blueprints)
  883.             {
  884.                 if (bpkvp.Key.SubtypeId.ToString() == kvp.Key)
  885.                 {
  886.                     hasbp = true;
  887.                     quotas_bp[bpkvp.Key] = quota;
  888.                     break;
  889.                 }
  890.             }
  891.             p3.e();
  892.             if (!hasbp) b.Append(" (no BP)");
  893.             b.Append("\n");
  894.         }
  895.  
  896.  
  897.         /*foreach (var kvp in quotas)
  898.                 {
  899.                     int av = 0;
  900.                     int quota = kvp.Value;
  901.                     avail.TryGetValue(kvp.Key, out av);
  902.                     r += kvp.Key + " " + av+" ";
  903.                     if (av < quota) r += "<";
  904.                     else if (av == quota) r += "=";
  905.                     else r += ">";
  906.                     r += " " + quota;
  907.                     bool hasbp = false;
  908.                     p3.s();
  909.                     foreach (var bpkvp in blueprints)
  910.                     {
  911.                         if(bpkvp.Key.SubtypeId.ToString() == kvp.Key)
  912.                         {
  913.                             hasbp = true;
  914.                             break;
  915.                         }
  916.                     }
  917.                     p3.e();
  918.                     if (!hasbp) r += " (no BP)";
  919.                     r += "\n";
  920.                 }*/
  921.         p2.e();
  922.         return b.ToString();
  923.     }
  924.     string last = "";
  925.     bool firstread = true;
  926.     public void readLCD(string s)
  927.     {
  928.         if (last == s) return;
  929.  
  930.         last = s;
  931.         var lines = s.Split('\n');
  932.         foreach(var l in lines)
  933.         {
  934.             if (l.Contains("|")) continue;
  935.  
  936.             var tok = l.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  937.             if (tok.Length >= 4)
  938.             {
  939.                 string key = tok[0];
  940.                 string cur = tok[1];
  941.                 char sym = tok[2][0];
  942.                 if (sym == '<' || sym == '>' || sym == '=')
  943.                 {
  944.                     string want = tok[3];
  945.                     int wnt = 0;
  946.                     var isn = int.TryParse(want, out wnt);
  947.                     if (isn)
  948.                     {
  949.                         quotas[key] = wnt;
  950.                     }
  951.                 }
  952.  
  953.             }
  954.         }
  955.     }
  956.  
  957.  
  958. }
  959.  
  960.     //ITEMNAME avail 0 inf [hidden]
  961. class Autocraft2
  962. {
  963.  
  964.     static public MyItemType NOPIT = MyItemType.MakeComponent("SteelPlate");//non-nullable type, and will crash if ever not valid.
  965.     static MyDefinitionId NOPBP = MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/SpaceCredit");
  966.     class CraftOrder
  967.     {
  968.         public MyItemType type = NOPIT;
  969.         public bool htype = false;
  970.         public string subtypeid = "";
  971.         public MyDefinitionId blueprint = NOPBP;
  972.         public CraftOrder(string subtypeid)
  973.         {
  974.             this.subtypeid = subtypeid;
  975.             htype = subType2ItemType(subtypeid, out type);
  976.         }
  977.         public int min = 0;
  978.         public int max = int.MaxValue;
  979.         public bool hide = false;
  980.  
  981.     }
  982.  
  983.  
  984.     static Dictionary<string, MyItemType> typecast = new Dictionary<string, MyItemType>();
  985.     public static bool subType2ItemType(string subtype, out MyItemType t)
  986.     {
  987.         if (typecast.ContainsKey(subtype))
  988.         {
  989.             t = typecast[subtype];
  990.             return true;
  991.         }
  992.         foreach (var bpkvp in blueprints)
  993.         {
  994.             if (bpkvp.Key.SubtypeId.ToString() == subtype)
  995.             {
  996.                 typecast[subtype] = bpkvp.Key;
  997.                 t = bpkvp.Key;
  998.                 return true;
  999.             }
  1000.         }
  1001.         t = NOPIT;
  1002.         return false;
  1003.     }
  1004.  
  1005.     static public Dictionary<MyDefinitionId, MyDefinitionId> blueprints = new Dictionary<MyDefinitionId, MyDefinitionId>();
  1006.     static public void addBP(MyDefinitionId item, MyDefinitionId bp)
  1007.     {
  1008.         blueprints[item] = bp;
  1009.         string newcd = "KTZINV\nitemID;blueprintID";
  1010.         foreach (var kvp in blueprints)
  1011.         {
  1012.             newcd += "\n" + kvp.Key.ToString() + ";" + kvp.Value.ToString();
  1013.         }
  1014.         gProgram.Me.CustomData = newcd;
  1015.     }
  1016.     static void loadBP()
  1017.     {
  1018.         var cd = gProgram.Me.CustomData;
  1019.         var spl = cd.Split('\n');
  1020.         foreach (var l in spl)
  1021.         {
  1022.             var s2 = l.Split(';');
  1023.             if (s2.Length >= 2)
  1024.             {
  1025.                 try
  1026.                 {
  1027.                     var itembp = MyDefinitionId.Parse(s2[0]);
  1028.                     try
  1029.                     {
  1030.                         var recipebp = MyDefinitionId.Parse(s2[1]);
  1031.                         blueprints[itembp] = recipebp;
  1032.                     }
  1033.                     catch (Exception) { }
  1034.                 }
  1035.                 catch (Exception) { }
  1036.             }
  1037.         }
  1038.     }
  1039. }
  1040.  
  1041. class BPLearn2
  1042. {
  1043.     public IMyAssembler asm = null;
  1044.     public int lastCraft = 0;
  1045.  
  1046.     static MyDefinitionId nop = MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/SpaceCredit");
  1047.  
  1048.  
  1049.     public List<MyProductionItem> lastQueue = new List<MyProductionItem>();
  1050.     List<MyInventoryItem> lastItems = new List<MyInventoryItem>();
  1051.     float lastProgress = -1;
  1052.     static Profiler bpl = new Profiler("bpl");
  1053.  
  1054.     //int tick2 = 0;
  1055.     //int tick3 = 0;
  1056.     public void update()
  1057.     {
  1058.         bpl.s();
  1059.         //assemblers only tick once per second, so a faster observation is meaningless
  1060.         if (tick % 60 == 0 && asm.Mode == MyAssemblerMode.Assembly)
  1061.         {
  1062.             var curProg = asm.CurrentProgress;
  1063.             List<MyProductionItem> queue = new List<MyProductionItem>();
  1064.             List<MyInventoryItem> items = new List<MyInventoryItem>();
  1065.  
  1066.             //tick2 += 1;
  1067.             //tick3 += 1;
  1068.  
  1069.  
  1070.             asm.GetQueue(queue);
  1071.             //todo: if get queue and unknown recipe in it, flush the output inventory of the assembler
  1072.  
  1073.  
  1074.  
  1075.             if (queue.Count != 0 || lastQueue.Count != 0)
  1076.             {
  1077.                 asm.OutputInventory.GetItems(items);
  1078.  
  1079.                 //this is because of nasty things like guns and tools that don't stack :|
  1080.                 List<MyItemType> types = new List<MyItemType>();
  1081.                 List<MyInventoryItem> itemsCompact = new List<MyInventoryItem>();
  1082.                 foreach (var i in items)
  1083.                 {
  1084.                     var t = i.Type;
  1085.                     if (types.Contains(t)) continue;
  1086.                     types.Add(t);
  1087.  
  1088.                     MyFixedPoint c = 0;
  1089.                     foreach (var i2 in items)
  1090.                     {
  1091.                         if (i2.Type == t) c += i2.Amount;
  1092.                     }
  1093.                     if (c > 0)
  1094.                     {
  1095.                         itemsCompact.Add(new MyInventoryItem(i.Type, i.ItemId, c));
  1096.                     }
  1097.                 }
  1098.                 items = itemsCompact;
  1099.  
  1100.                 if (curProg < lastProgress && lastQueue.Count > 0)
  1101.                 {
  1102.                     //log("tick2=" + tick2, LT.LOG_N);
  1103.                     //tick2 = 0;
  1104.  
  1105.                     MyDefinitionId recipe = nop;
  1106.                     //compare production queue to last known state.
  1107.                     //Check for any item with a decreased count or that has left the queue altogether
  1108.                     //and save it
  1109.                     foreach (var lastitem in lastQueue)
  1110.                     {
  1111.                         bool still_queued = false;
  1112.                         foreach (var curitem in queue)
  1113.                         {
  1114.                             if (curitem.ItemId == lastitem.ItemId && curitem.BlueprintId == lastitem.BlueprintId)
  1115.                             {
  1116.                                 still_queued = true;
  1117.                                 if (curitem.Amount < lastitem.Amount)
  1118.                                 {
  1119.                                     recipe = lastitem.BlueprintId;
  1120.                                     break;
  1121.                                 }
  1122.                             }
  1123.                         }
  1124.                         if (recipe != nop) break;
  1125.  
  1126.                         if (!still_queued)
  1127.                         {
  1128.                             recipe = lastitem.BlueprintId;
  1129.                             break;
  1130.                         }
  1131.                     }
  1132.  
  1133.                     if (recipe != nop)
  1134.                     {
  1135.  
  1136.                         //assuming we found the above, check for a newly appearing or count-increased item in the output inventory.
  1137.                         //if progress has reset, a production order has shrunk or vanished, and a new item has appeared,
  1138.                         ///we can reasonably assume that the production order blueprint generates that item.
  1139.                         MyDefinitionId itemdef = nop;
  1140.                         foreach (var i in items)
  1141.                         {
  1142.                             bool newitem = true;
  1143.                             foreach (var o in lastItems)
  1144.                             {
  1145.                                 if (o.Type == i.Type) newitem = false;
  1146.  
  1147.                                 if (o.Type == i.Type && o.Amount < i.Amount)
  1148.                                 {
  1149.                                     try
  1150.                                     {
  1151.                                         var n = MyDefinitionId.Parse(i.Type.TypeId + "/" + i.Type.SubtypeId);
  1152.                                         itemdef = n;
  1153.                                         newitem = false;
  1154.                                         break;
  1155.                                     }
  1156.                                     catch (Exception) { }
  1157.                                 }
  1158.                                 //if (itembp != nop) break;
  1159.                             }
  1160.  
  1161.  
  1162.                             //if we found no increased item (the bp is nop) but we did notice a new kind of item, it's that one
  1163.                             if (newitem)// && itembp == nop)
  1164.                             {
  1165.                                 try
  1166.                                 {
  1167.                                     var n = MyDefinitionId.Parse(i.Type.TypeId + "/" + i.Type.SubtypeId);
  1168.                                     itemdef = n;
  1169.                                 }
  1170.                                 catch (Exception) { }
  1171.                             }
  1172.                             if (itemdef != nop) break;
  1173.                         }
  1174.                         if (recipe != nop && itemdef != nop)
  1175.                         {
  1176.                             lastCraft = tick;
  1177.                             if (!Autocraft.blueprints.ContainsKey(itemdef))
  1178.                             {
  1179.                                 log("Learned recipe " + itemdef.ToString() + ";" + recipe.ToString(), LT.LOG_N);
  1180.                                 Autocraft.addBP(itemdef, recipe);
  1181.                             }
  1182.                             //Autocraft.blueprints[itemdef] = recipe;
  1183.                             //todo:flush output inventory here
  1184.  
  1185.                         }
  1186.  
  1187.                     }
  1188.                 }
  1189.             }
  1190.             if (lastQueue.Count == 0) curProg = 0;
  1191.  
  1192.  
  1193.             lastProgress = curProg;
  1194.             lastQueue = queue;
  1195.             lastItems = items;
  1196.         }
  1197.         bpl.e();
  1198.     }
  1199. }
  1200.  
  1201. const double MAX_MS_PER_SEC_BOOT = 0.1;
  1202. const double MAX_MS_PER_SEC = 0.1;
  1203. const int PBLIMIT_STARTUPTICKS = 0;//20 by default
  1204. class BurnoutTrack
  1205. {
  1206.     public double maxmspersec = 0.25;
  1207.     public static double[] defertrack;
  1208.     public int len = 60;
  1209.     public BurnoutTrack(int l, double ms)
  1210.     {
  1211.         len = l;
  1212.         maxmspersec = ms;
  1213.         defertrack = new double[len];
  1214.     }
  1215.     int defercalls = 0;
  1216.     int deferpos = 0;
  1217.     static bool hangflag = false;
  1218.     int hangticks = 0;
  1219.     int hangtick = 0;
  1220.     bool fsdbg = false;
  1221.     DateTime bf = DateTime.Now;
  1222.     public bool burnoutpre()
  1223.     {
  1224.         bf = DateTime.Now;
  1225.         if (hangflag)
  1226.         {
  1227.             if (tick > hangtick)
  1228.             {
  1229.                 double avg = 0;
  1230.                 foreach (var d in defertrack) avg += d;
  1231.                 avg = avg / (defercalls > defertrack.Length ? defertrack.Length : defercalls);
  1232.                 if (avg > maxmspersec * len / 60)
  1233.                 {
  1234.                     defertrack[deferpos] = 0;
  1235.                     defercalls += 1;
  1236.                     deferpos = (deferpos + 1) % defertrack.Length;
  1237.                     return true;
  1238.                 }
  1239.                 else
  1240.                 {
  1241.                     hangflag = false;
  1242.                     //log("Resuming after " + (hangticks / 60.0d).ToString("0.0") + "s", LT.LOG_N);
  1243.                 }
  1244.             }
  1245.         }
  1246.         return hangflag;
  1247.     }
  1248.     public double avg()
  1249.     {
  1250.         double avg = 0;
  1251.         foreach (var d in defertrack) avg += d;
  1252.         avg = avg / (defercalls > defertrack.Length ? defertrack.Length : defercalls);
  1253.         return avg;
  1254.     }
  1255.     public void setwait(int ticks)
  1256.     {
  1257.         hangticks = ticks;
  1258.         hangtick = tick + ticks;
  1259.         hangflag = true;
  1260.     }
  1261.     public bool burnoutpost()
  1262.     {
  1263.         double ms = (DateTime.Now - bf).TotalMilliseconds;
  1264.         defertrack[deferpos] = ms;
  1265.         defercalls += 1;
  1266.         deferpos = (deferpos + 1) % defertrack.Length;
  1267.         if (!hangflag)
  1268.         {
  1269.             double p_avg = 0;
  1270.             foreach (var d in defertrack) p_avg += d;
  1271.             int divisor = defercalls > defertrack.Length ? defertrack.Length : defercalls;
  1272.             var avg = p_avg / divisor;
  1273.             var mtch = maxmspersec * len / 60;
  1274.             if (avg > mtch)
  1275.             {
  1276.                 int tickstodoom = PBLIMIT_STARTUPTICKS - tick;
  1277.                 if (tickstodoom > 0 && tickstodoom * maxmspersec < avg) return false;
  1278.  
  1279.                 int waitticks = 0;
  1280.                 while (p_avg / (divisor + waitticks) > mtch) waitticks++;
  1281.  
  1282.                 hangticks = waitticks;
  1283.                 hangtick = tick + waitticks;
  1284.                 hangflag = true;
  1285.  
  1286.  
  1287.                 var lstr = tick + ": " + avg.ToString("0.00") + ">" + (mtch).ToString("0.00") + "ms/s exec. Sleeping " + (hangticks / 60.0d).ToString("0.0") + "s";
  1288.                 log(lstr, LT.LOG_N);
  1289.                 /*var c = getCtrl();
  1290.                         if (c != null)
  1291.                         {
  1292.                             if (!fsdbg)
  1293.                             {
  1294.                                 c.CustomData = "";
  1295.                                 fsdbg = true;
  1296.                             }
  1297.                             c.CustomData += "\n\n" + lstr + "\n\n" + Profiler.getAllReports();
  1298.                         }
  1299.                         else getCtrlTick = -9000;*/
  1300.  
  1301.                 return true;
  1302.             }
  1303.         }
  1304.         else return true;
  1305.         return false;
  1306.     }
  1307. }
  1308.  
  1309. class Inventory
  1310. {
  1311.     public static InventoryManifest globalManifest = new InventoryManifest();
  1312.     public static Dictionary<string, MyFixedPoint> nonFractionalMinMarginByCat = new Dictionary<string, MyFixedPoint>();
  1313.     public static List<MyItemType> encounteredTypes = new List<MyItemType>();
  1314.  
  1315.     static Dictionary<string, MyItemType> typeTable = new Dictionary<string, MyItemType>();
  1316.     static MyItemType getType(string type, string subtype)
  1317.     {//can throw exceptions, MyItemType is fiddly
  1318.         var k = type + "/" + subtype;
  1319.         if (typeTable.ContainsKey(k)) return typeTable[k];
  1320.         else
  1321.         {
  1322.             return typeTable[k] = new MyItemType(type, subtype);
  1323.         }
  1324.     }
  1325.  
  1326.     //there's some giga-fucky shit going on with tanks
  1327.     static List<string> unstackhardcode = new List<string>(){
  1328.     "MyObjectBuilder_OxygenContainerObject",
  1329.     "MyObjectBuilder_GasContainerObject",
  1330.     "MyObjectBuilder_PhysicalGunObject",
  1331.     "MyObjectBuilder_PhysicalObject",
  1332.     "MyObjectBuilder_Datapad",
  1333.     };
  1334.  
  1335.     static Dictionary<string, string> cattocargo = new Dictionary<string, string>()
  1336.         {
  1337.             {"MyObjectBuilder_OxygenContainerObject","Bottles"},
  1338.             {"MyObjectBuilder_GasContainerObject","Bottles"},
  1339.  
  1340.             {"MyObjectBuilder_PhysicalGunObject","Tools"},
  1341.             {"MyObjectBuilder_PhysicalObject","Tools"},//space credit
  1342.             {"MyObjectBuilder_ConsumableItem","Tools"},//cola, coffee
  1343.             {"MyObjectBuilder_Datapad","Tools"},//datapad duh
  1344.  
  1345.             {"MyObjectBuilder_AmmoMagazine","Ammo"},
  1346.             {"MyObjectBuilder_Ore","Ores"},
  1347.             {"MyObjectBuilder_Ingot","Ingots"},
  1348.             {"MyObjectBuilder_Component","Components"},
  1349.         };
  1350.     public static string cargokeywordbytype(string type)
  1351.     {
  1352.         string r = "Unknown";
  1353.         cattocargo.TryGetValue(type, out r);
  1354.         return r;
  1355.     }
  1356.  
  1357.     //static bool sortProductionInput = false;
  1358.     static bool treatBlankAsAlltype = false;
  1359.     public class InventoryManifest
  1360.     {
  1361.  
  1362.  
  1363.  
  1364.         public InventoryManifest()
  1365.         {
  1366.  
  1367.         }
  1368.  
  1369.         public Dictionary<MyItemType, MyFixedPoint> stuff = new Dictionary<MyItemType, MyFixedPoint>();
  1370.         public MyFixedPoint maxVolume;
  1371.         public MyFixedPoint freeVolume;
  1372.         public Dictionary<string, MyFixedPoint> typeVolume = new Dictionary<string, MyFixedPoint>();
  1373.         public void set(BlockInventory bi)
  1374.         {
  1375.             stuff.Clear();
  1376.             maxVolume = freeVolume = 0;
  1377.  
  1378.             var invs = bi.getSortedInventories(false);
  1379.             int merges = 0;
  1380.             //const int MAX_MERGES = 10;
  1381.             DateTime b4 = DateTime.Now;
  1382.             foreach (var nv in invs)
  1383.             {
  1384.                 var mv = nv.MaxVolume;
  1385.                 var cv = nv.CurrentVolume;
  1386.                 maxVolume += mv;
  1387.                 freeVolume += mv - cv;
  1388.                 List<MyInventoryItem> itms = new List<MyInventoryItem>();
  1389.                 nv.GetItems(itms);
  1390.                 Dictionary<MyItemType, int> lItem = new Dictionary<MyItemType, int>();
  1391.  
  1392.                 for(int i = itms.Count-1; i >= 0; i--)
  1393.                 {
  1394.                     var it = itms[i];
  1395.                     //stack deduplication
  1396.                     if (MERGE_STACKS && merges < MAX_TRANSFERS_PER_OP && lItem.ContainsKey(it.Type))
  1397.                     {
  1398.                         var stackable = !unstackhardcode.Contains(it.Type.TypeId);
  1399.                         if (stackable)
  1400.                         {
  1401.                             var lpos = lItem[it.Type];
  1402.                             var nfo = it.Type.GetItemInfo();
  1403.                             var lit = itms[lpos];
  1404.                             if (it.Amount + lit.Amount < nfo.MaxStackAmount)
  1405.                             {
  1406.                                 log(it.Type.SubtypeId + " msa " + nfo.MaxStackAmount + " stacking now ");
  1407.                                 nv.TransferItemTo(nv, lpos, i, true);
  1408.                                 merges++;
  1409.                                 if ((DateTime.Now - b4).TotalMilliseconds > MAX_TRANSFER_MS) merges = MAX_TRANSFERS_PER_OP;
  1410.                             }
  1411.                         }
  1412.                     }
  1413.                     lItem[it.Type] = i;
  1414.  
  1415.                     //manifest generate
  1416.                     if (!stuff.ContainsKey(it.Type)) stuff[it.Type] = it.Amount;
  1417.                     else stuff[it.Type] += it.Amount;
  1418.                     var t = cargokeywordbytype(it.Type.TypeId);
  1419.                     MyFixedPoint tv = 0;
  1420.                     typeVolume.TryGetValue(t, out tv);
  1421.                     tv += it.Type.GetItemInfo().Volume * it.Amount;
  1422.                     typeVolume[t] = tv;
  1423.                 }
  1424.             }
  1425.             if (merges > 0)
  1426.             {
  1427.                 log(merges + " merges in " + bi.b.CustomName + " took " + (DateTime.Now - b4).TotalMilliseconds + "ms");
  1428.             }
  1429.  
  1430.             //very ugly.
  1431.             foreach (var kvp in stuff)
  1432.             {
  1433.                 var k = kvp.Key;
  1434.                 if(!encounteredTypes.Contains(k))
  1435.                 {
  1436.                     encounteredTypes.Add(k);
  1437.                     MyFixedPoint minVol = (MyFixedPoint)0.01;
  1438.                     if (!k.GetItemInfo().UsesFractions) minVol = (MyFixedPoint)k.GetItemInfo().Volume;
  1439.                     var cat = cargokeywordbytype(k.TypeId);
  1440.                     MyFixedPoint kval = 0;
  1441.                     nonFractionalMinMarginByCat.TryGetValue(cat, out kval);
  1442.                     if (minVol > kval) kval = minVol;
  1443.                     nonFractionalMinMarginByCat[cat] = kval;
  1444.                 }
  1445.             }
  1446.         }
  1447.         public void sub(InventoryManifest o)
  1448.         {//if we don't even have the thing being subtracted nothing will be subtracted
  1449.             if (o == null) return;
  1450.  
  1451.             List<MyItemType> del = new List<MyItemType>();
  1452.             foreach (var kvp in o.stuff)
  1453.             {
  1454.                 if (stuff.ContainsKey(kvp.Key))
  1455.                 {
  1456.                     var nv = stuff[kvp.Key] - kvp.Value;
  1457.                     if (nv > 0) stuff[kvp.Key] = nv;
  1458.                     else del.Add(kvp.Key);
  1459.                 }
  1460.             }
  1461.             foreach (var k in del) stuff.Remove(k);
  1462.         }
  1463.         public void add(InventoryManifest o)
  1464.         {
  1465.             if (o == null) return;
  1466.             foreach (var kvp in o.stuff)
  1467.             {
  1468.                 if (stuff.ContainsKey(kvp.Key)) stuff[kvp.Key] += kvp.Value;
  1469.                 else stuff[kvp.Key] = kvp.Value;
  1470.             }
  1471.         }
  1472.         public bool equals(InventoryManifest o)
  1473.         {
  1474.             if (o == null || this.stuff.Count != o.stuff.Count) return false;
  1475.  
  1476.  
  1477.             foreach(var kvp in stuff)
  1478.             {
  1479.                 MyFixedPoint v = 0;
  1480.                 if (!o.stuff.TryGetValue(kvp.Key, out v)) return false;
  1481.                 else if (kvp.Value != v) return false;
  1482.             }
  1483.             foreach(var kvp in o.stuff)
  1484.             {
  1485.                 if (!stuff.ContainsKey(kvp.Key)) return false;
  1486.             }
  1487.             return true;
  1488.         }
  1489.     }
  1490.  
  1491.     static public List<PriorityAggregate> prAggs = new List<PriorityAggregate>();
  1492.     static public PriorityAggregate getPI(int p)
  1493.     {
  1494.         foreach (var pr in prAggs) if (pr.priority == p) return pr;
  1495.         var x = new PriorityAggregate();
  1496.         x.priority = p;
  1497.         prAggs.Add(x);
  1498.         prAggs.Sort();
  1499.         return x;
  1500.     }
  1501.     static PriorityAggregate higherPriorityWithRoomFor(BlockInventory bi, string category)
  1502.     {
  1503.         //PriorityAggregate pi = null;
  1504.         var pidx = 0;
  1505.         for(int i =0; i < prAggs.Count; i++)
  1506.         {
  1507.             var pr = prAggs[i];
  1508.             if (pr.priority == bi.priority)
  1509.             {
  1510.                 //pi = pr;
  1511.                 pidx = i;
  1512.                 break;
  1513.             }
  1514.         }
  1515.  
  1516.         for (int i = 0; i < pidx; i++)
  1517.         {
  1518.             var c = prAggs[i];
  1519.             MyFixedPoint free = 0;
  1520.             c.typeVolumeFree.TryGetValue(category, out free);
  1521.  
  1522.             MyFixedPoint minmargin = 0;
  1523.             nonFractionalMinMarginByCat.TryGetValue(category, out minmargin);
  1524.             if (free > minmargin) return c;
  1525.         }
  1526.         return null;
  1527.     }
  1528.     public class PriorityAggregate : IComparable<PriorityAggregate>
  1529.     {
  1530.         int IComparable<PriorityAggregate>.CompareTo(PriorityAggregate y)
  1531.         {
  1532.             var x = this;
  1533.             return x.priority.CompareTo(y.priority);
  1534.         }
  1535.         public List<BlockInventory> bis = new List<BlockInventory>();
  1536.         public int priority = 0;
  1537.         public Dictionary<string, MyFixedPoint> typeVolumeFree = new Dictionary<string, MyFixedPoint>();
  1538.         public List<string> categories = new List<string>();
  1539.         public void update()
  1540.         {
  1541.             typeVolumeFree.Clear();
  1542.             categories.Clear();
  1543.             foreach (var bi in bis)
  1544.             {
  1545.  
  1546.                 foreach (var c in bi.categories)
  1547.                 {
  1548.                     if (!categories.Contains(c)) categories.Add(c);
  1549.                     MyFixedPoint v = 0;
  1550.                     typeVolumeFree.TryGetValue(c, out v);
  1551.                     //for our purposes, we are recording the largest free volume in a single container in the set that accepts this category.
  1552.                     if (bi.manifest.freeVolume > v)
  1553.                     {
  1554.                         typeVolumeFree[c] = bi.manifest.freeVolume;
  1555.                     }
  1556.                 }
  1557.             }
  1558.         }
  1559.     }
  1560.  
  1561.     public class BlockInventory : IComparable<BlockInventory>
  1562.     {
  1563.  
  1564.         int IComparable<BlockInventory>.CompareTo(BlockInventory y)
  1565.         {
  1566.             var x = this;
  1567.             if (x.priority == y.priority)
  1568.             {
  1569.                 return x.idx.CompareTo(y.idx);
  1570.             }
  1571.             return x.priority.CompareTo(y.priority);
  1572.         }
  1573.  
  1574.         public static List<BlockInventory> bPriorityList = new List<BlockInventory>();
  1575.         public static Dictionary<IMyTerminalBlock, BlockInventory> bIDict = new Dictionary<IMyTerminalBlock, BlockInventory>();
  1576.         public static BlockInventory getBI(IMyTerminalBlock b)
  1577.         {
  1578.             BlockInventory r = null;
  1579.             bIDict.TryGetValue(b, out r);
  1580.             if (r == null) r = new BlockInventory(b);
  1581.             return r;
  1582.         }
  1583.         const string bpprefix = "MyObjectBuilder_";
  1584.         const string everything = "alltypes";
  1585.  
  1586.  
  1587.         public static int idl = 0;
  1588.         public int idx = 0;
  1589.         public BlockInventory(IMyTerminalBlock b)
  1590.         {
  1591.             this.b = b;
  1592.  
  1593.             bPriorityList.Add(this);
  1594.             bIDict[b] = this;
  1595.             idx = idl;
  1596.             idl++;
  1597.  
  1598.  
  1599.             for (var i = 0; i < b.InventoryCount; i++)
  1600.             {
  1601.                 sortedInventories.Add(b.GetInventory(i));
  1602.             }
  1603.             if (b is IMyProductionBlock)
  1604.             {
  1605.                 isProduction = true;
  1606.                 var p = (IMyProductionBlock)b;
  1607.                 sortedInventoriesNoInput.Add(p.OutputInventory);
  1608.                 sortedInventoriesNoOutput.Add(p.InputInventory);
  1609.                 if (b is IMyAssembler)
  1610.                 {
  1611.                     isAssembler = true;
  1612.                     asmref = (IMyAssembler)b;
  1613.                 }
  1614.             }
  1615.             else
  1616.             {
  1617.                 sortedInventoriesNoInput.AddRange(sortedInventories);
  1618.             }
  1619.         }
  1620.         public IMyTerminalBlock b = null;
  1621.         public InventoryManifest manifest = null;
  1622.  
  1623.         public List<string> categories = new List<string>();
  1624.  
  1625.         List<IMyInventory> sortedInventoriesNoInput = new List<IMyInventory>();
  1626.         List<IMyInventory> sortedInventoriesNoOutput = new List<IMyInventory>();
  1627.         List<IMyInventory> sortedInventories = new List<IMyInventory>();
  1628.         public List<IMyInventory> getSortedInventories(bool inc_input)
  1629.         {
  1630.             if (inc_input) return sortedInventories;
  1631.             else
  1632.             {
  1633.                 if (asmref != null && asmref.Mode == MyAssemblerMode.Disassembly) return sortedInventoriesNoOutput;
  1634.                 return sortedInventoriesNoInput;
  1635.             }
  1636.  
  1637.         }
  1638.         public bool isProduction = false;
  1639.         public bool isAssembler = false;
  1640.         IMyAssembler asmref = null;
  1641.  
  1642.  
  1643.         public Dictionary<MyItemType, MyFixedPoint> stocktargets = new Dictionary<MyItemType, MyFixedPoint>();
  1644.         public bool special = false;
  1645.         public bool locked = true;//we don't move shit to shit until first updateP
  1646.         public bool hidden = false;
  1647.         public bool holdall = false;
  1648.         const int default_p = 100000;
  1649.         public int priority = int.MaxValue;
  1650.         public string lastCD = "-31234";
  1651.         public string lastN = "-234523";
  1652.  
  1653.         void _locked()
  1654.         {
  1655.             locked = true;
  1656.             special = false;
  1657.         }
  1658.         void _hidden()
  1659.         {
  1660.             locked = true;
  1661.             hidden = true;
  1662.             special = false;
  1663.         }
  1664.  
  1665.         public void updateP()
  1666.         {
  1667.             if (b.CustomName != lastN)
  1668.             {
  1669.                 lastN = b.CustomName;
  1670.  
  1671.                 var lpriority = priority;
  1672.                 //var PI = getPI(priority);
  1673.                 //PI.bis.Remove(this);
  1674.  
  1675.                 priority = default_p;
  1676.                 special = false;
  1677.                 locked = false;
  1678.                 hidden = false;
  1679.                 holdall = false;
  1680.                 var t = lastN.Split(' ', '.');
  1681.                 categories.Clear();
  1682.  
  1683.                 if (lockBlockTypes.Contains(b.DefinitionDisplayNameText)) _locked();
  1684.                 if (hiddenBlockTypes.Contains(b.DefinitionDisplayNameText)) _hidden();
  1685.  
  1686.                 if (!hidden)
  1687.                 {
  1688.                     foreach (var tok in t)
  1689.                     {
  1690.                         var ltok = tok.ToLower();
  1691.                         if (ltok.StartsWith("[") && ltok.EndsWith("]"))
  1692.                         {
  1693.                             ltok = ltok.Substring(1, ltok.Length - 2);
  1694.                         }
  1695.                         if (ltok == "special")
  1696.                         {
  1697.                             special = true;
  1698.                             priority -= 10000;
  1699.  
  1700.                         }
  1701.                         else if (ltok == "locked")
  1702.                         {
  1703.                             _locked();
  1704.                         }
  1705.                         else if (ltok == "hidden")
  1706.                         {
  1707.                             _hidden();
  1708.                         }
  1709.                         else if (ltok.StartsWith("p"))
  1710.                         {
  1711.                             var ap = tok.Substring(1);
  1712.                             if (ap == "max") priority = int.MinValue;
  1713.                             else if (ap == "min") priority = int.MaxValue;
  1714.                             else if (ap.All(char.IsDigit))
  1715.                             {
  1716.                                 priority -= 10000;
  1717.                                 int c = 0;
  1718.                                 int.TryParse(ap, out c);
  1719.                                 if (c.ToString() == ap)
  1720.                                 {
  1721.                                     priority += c;
  1722.                                 }
  1723.                             }
  1724.                         }
  1725.                         else if (ltok == everything)
  1726.                         {
  1727.                             holdall = true;
  1728.                         }
  1729.                         else
  1730.                         {
  1731.                             foreach (var kvp in cattocargo)
  1732.                             {
  1733.                                 if (tok == kvp.Value)
  1734.                                 {
  1735.                                     if (!categories.Contains(tok)) categories.Add(tok);
  1736.                                     break;
  1737.                                 }
  1738.                             }
  1739.                         }
  1740.                     }
  1741.  
  1742.                     if (treatBlankAsAlltype && !special && !locked && categories.Count == 0 && !isProduction)
  1743.                     {
  1744.                         holdall = true;
  1745.                         priority += 1;
  1746.                     }
  1747.                 }
  1748.  
  1749.                 if(special)
  1750.                 {
  1751.                     holdall = false;
  1752.                     categories.Clear();
  1753.                 }
  1754.                 if(holdall)
  1755.                 {
  1756.                     foreach(var kvp in cattocargo)
  1757.                     {
  1758.                         if (!categories.Contains(kvp.Value)) categories.Add(kvp.Value);
  1759.                     }
  1760.                 }
  1761.                 if (!special && categories.Count == 0 && APIWC.HasCoreWeapon(b))
  1762.                 {
  1763.                     locked = true;
  1764.                 }
  1765.  
  1766.                 if (lpriority != priority)
  1767.                 {
  1768.                     bPriorityList.Sort();
  1769.  
  1770.                     var PI = getPI(lpriority);
  1771.                     PI.bis.Remove(this);
  1772.                     PI.update();
  1773.                     PI = getPI(priority);
  1774.                     PI.bis.Add(this);
  1775.                     PI.update();
  1776.                 }
  1777.                 if(!special) stocktargets.Clear();
  1778.             }
  1779.             if (special && b.CustomData != lastCD)
  1780.             {
  1781.                 if (special && b.CustomData == "")
  1782.                 {
  1783.                     List<MyItemType> alltypes = new List<MyItemType>();
  1784.                     List<MyItemType> t = new List<MyItemType>();
  1785.                     for (var i = 0; i < b.InventoryCount; i++)
  1786.                     {
  1787.                         b.GetInventory(i).GetAcceptedItems(t);
  1788.                         foreach (var e in t) if (!alltypes.Contains(e)) alltypes.Add(e);
  1789.                     }
  1790.                     List<string> clinesNZ = new List<string>();
  1791.                     List<string> clines = new List<string>();
  1792.                     foreach (var e in alltypes)
  1793.                     {
  1794.                         MyFixedPoint amt = 0;
  1795.                         manifest.stuff.TryGetValue(e, out amt);
  1796.                         if (amt > 0) clinesNZ.Add(e.TypeId.Substring(bpprefix.Length) + "/" + e.SubtypeId + "=" + amt.ToString());//\n";
  1797.                         else clines.Add(e.TypeId.Substring(bpprefix.Length) + "/" + e.SubtypeId + "=0");
  1798.                     }
  1799.                     clinesNZ.Sort();
  1800.                     clines.Sort();
  1801.                     if(clinesNZ.Count == 0)clinesNZ.AddRange(clines);
  1802.  
  1803.                     b.CustomData = String.Join("\n", clinesNZ);
  1804.                 }
  1805.                 if(ISYCOMPAT && b.CustomData.IndexOf("Special Container modes:") == -1)
  1806.                 {
  1807.                     b.CustomData = "@Special Container modes:\n- isycompat\n" + b.CustomData;
  1808.                 }
  1809.                 lastCD = b.CustomData;
  1810.                 stocktargets.Clear();
  1811.                 var lines = lastCD.Split('\n');
  1812.                 //var newlines = new List<string>();
  1813.                 foreach (var l in lines)
  1814.                 {
  1815.                     //bool kl = true;
  1816.                     var lr = l.Split('=');
  1817.                     if (lr.Length == 2)
  1818.                     {
  1819.                         var ids = lr[0].Split('/');
  1820.                         if (ids.Length == 2)
  1821.                         {
  1822.                             try
  1823.                             {
  1824.                                 var t = getType(bpprefix + ids[0], ids[1]);
  1825.                                 if (lr[1] == "all")
  1826.                                 {
  1827.                                     stocktargets[t] = int.MaxValue;
  1828.                                     //if (LOG) log(b.CustomName + " " + t.SubtypeId + "=all");
  1829.                                 }
  1830.                                 else
  1831.                                 {
  1832.                                     var c = (MyFixedPoint)double.Parse(lr[1]);
  1833.                                     if (c > 0)
  1834.                                     {
  1835.                                         stocktargets[t] = c;
  1836.                                         //if (LOG) log(b.CustomName + " " + t.SubtypeId + "=" + c);
  1837.                                     }else
  1838.                                     {
  1839.                                         //kl = false;
  1840.                                     }
  1841.                                 }
  1842.                             }
  1843.                             catch (Exception) { }
  1844.                         }
  1845.                     }
  1846.                 }
  1847.             }
  1848.         }
  1849.         public void updateM()
  1850.         {
  1851.             InventoryManifest nm = new InventoryManifest();
  1852.             if (!hidden) nm.set(this);
  1853.             if (manifest == null || !manifest.equals(nm))
  1854.             {
  1855.                 if (manifest != null) Inventory.globalManifest.sub(manifest);
  1856.                 Inventory.globalManifest.add(nm);
  1857.                 manifest = nm;
  1858.                 getPI(this.priority).update();
  1859.             }
  1860.         }
  1861.         public bool updateT()
  1862.         {
  1863.             //updateT_incomplete = false;
  1864.             if (locked) return false;
  1865.  
  1866.             int transfers = transfer_count;
  1867.  
  1868.             IDBG.set(this, null);
  1869.  
  1870.             //int MOVES = 0;
  1871.             //const int MAX_MOVES = 8;
  1872.  
  1873.             //this should actually run always and first, i think
  1874.             {
  1875.                 Dictionary<string, PriorityAggregate> targs = new Dictionary<string, PriorityAggregate>();
  1876.                 List<MyItemType> keys = new List<MyItemType>(manifest.stuff.Keys);
  1877.                 //this ensures the dict can be edited during our loop
  1878.  
  1879.                 foreach (var type in keys)//things we have
  1880.                 {
  1881.                     var cat = cargokeywordbytype(type.TypeId);
  1882.                     //this should only end up actually calling higherPriorityWithRoomFor once per relevant category tag.
  1883.                     PriorityAggregate pa = null;
  1884.                     if (!targs.ContainsKey(cat))
  1885.                     {
  1886.                         targs[cat] = pa = higherPriorityWithRoomFor(this, cat);
  1887.                     }
  1888.                     else pa = targs[cat];
  1889.  
  1890.                     int errchk = 0;
  1891.  
  1892.                     while(pa != null && errchk < 10)//there is a higher priority container in a PriorityAggregate that does want the item's category
  1893.                     {
  1894.  
  1895.                         MyFixedPoint amt = 0;
  1896.  
  1897.                         manifest.stuff.TryGetValue(type, out amt);
  1898.                         if (amt == 0) break;
  1899.  
  1900.                         MyFixedPoint goalstock = 0;//in case this is a special container, we don't want to push shit we should be keeping
  1901.                         stocktargets.TryGetValue(type, out goalstock);
  1902.                         amt -= goalstock;
  1903.                         if (amt == 0) break;
  1904.  
  1905.                         errchk++;
  1906.                         var margin = nonFractionalMinMarginByCat[cat];
  1907.                         BlockInventory dest = null;
  1908.                         foreach(var bi in pa.bis)
  1909.                         {
  1910.                             if(bi.categories.Contains(cat) && bi.manifest.freeVolume >= margin)
  1911.                             {//this cargo accepts this category and has more free space than the minimum margin for this category
  1912.                                 dest = bi;
  1913.                                 break;
  1914.                             }
  1915.                         }
  1916.                         if(dest != null)
  1917.                         {
  1918.                             //we should start transferring this item.
  1919.                             IDBG.set(this, dest);
  1920.                             if (amt > dest.manifest.freeVolume)amt = dest.manifest.freeVolume;
  1921.                             var rem = transfer_item(this, dest, type, amt, false, true);
  1922.                             if (transfer_count - transfers > MAX_TRANSFERS_PER_OP || transMS > MAX_TRANSFER_MS) return true;
  1923.  
  1924.                             if (rem > 0)
  1925.                             {
  1926.                                 IDBG.log("Unable to xfer " + rem + " of " + type.SubtypeId);
  1927.                             }
  1928.                             //...
  1929.  
  1930.                             pa.update();//recompute the PriorityAggregate values.
  1931.                             //if we filled the destination beyond nonFractionalMaxMarginByCat,
  1932.                             //we should delete entry in targs so that higherPriorityWithRoomFor is recomputed for next relevant item
  1933.                             if (dest.manifest.freeVolume < margin)
  1934.                             {
  1935.                                 targs[cat] = pa = higherPriorityWithRoomFor(this, cat);
  1936.                             }
  1937.                         }
  1938.                     }
  1939.                     if(errchk == 10)
  1940.                     {
  1941.                         IDBG.log("errchk loop abort");
  1942.                     }
  1943.  
  1944.                     {
  1945.                         if (!categories.Contains(cat)/* && !holdall*/ && !special)
  1946.                         {
  1947.                             MyFixedPoint amt = 0;
  1948.  
  1949.                             if(manifest.stuff.TryGetValue(type, out amt) && amt > 0)
  1950.                             {
  1951.                                 MyFixedPoint goalstock = 0;//in case this is a special container, we don't want to push shit we should be keeping
  1952.                                 stocktargets.TryGetValue(type, out goalstock);
  1953.                                 if (amt > goalstock)
  1954.                                 {
  1955.                                     //nobody higher wants it, but it's not supposed to be in this cargo either.
  1956.                                     //todo: search for equal or lower priority place that wants it. if we can't find one, generate error log message.
  1957.                                     expel(this, type, amt-goalstock);
  1958.                                     if (transfer_count - transfers > MAX_TRANSFERS_PER_OP || transMS > MAX_TRANSFER_MS) return true;
  1959.                                 }
  1960.                             }
  1961.                         }
  1962.                     }
  1963.                 }
  1964.             }
  1965.  
  1966.             if (special)
  1967.             {
  1968.                 List<MyItemType> keys = new List<MyItemType>(stocktargets.Keys);
  1969.                 foreach (var kvp in manifest.stuff) if (!keys.Contains(kvp.Key)) keys.Add(kvp.Key);
  1970.  
  1971.                 foreach (var type in keys)
  1972.                 {
  1973.                     MyFixedPoint curstock = 0;
  1974.                     manifest.stuff.TryGetValue(type, out curstock);
  1975.                     MyFixedPoint goalstock = 0;
  1976.                     stocktargets.TryGetValue(type, out goalstock);
  1977.                     if (goalstock > curstock && manifest.freeVolume > (MyFixedPoint)type.GetItemInfo().Volume)
  1978.                     {
  1979.                         MyFixedPoint globalstock = 0;
  1980.                         globalManifest.stuff.TryGetValue(type, out globalstock);
  1981.                         if (globalstock > curstock)
  1982.                         {
  1983.                             IDBG.set(type.SubtypeId);
  1984.                             IDBG.log(b.CustomName + " globalchk " + type.SubtypeId + " pmove " + goalstock + " " + curstock);
  1985.                             var r = retrieve(this, type, goalstock - curstock);
  1986.  
  1987.                             if (transfer_count - transfers > MAX_TRANSFERS_PER_OP || transMS > MAX_TRANSFER_MS) return true;
  1988.  
  1989.                             if (r > 0) IDBG.log("unable to satisfy by " + r);
  1990.                         }
  1991.                     }
  1992.                     else if (goalstock < curstock)
  1993.                     {
  1994.                         IDBG.set(type.SubtypeId);
  1995.                         IDBG.log("attempt expel " + type.SubtypeId + ": " + goalstock + " < " + curstock + " in " + this.b.CustomName);
  1996.                         expel(this, type, curstock - goalstock);
  1997.  
  1998.                         if (transfer_count - transfers > MAX_TRANSFERS_PER_OP || transMS > MAX_TRANSFER_MS) return true;
  1999.                     }
  2000.                 }
  2001.             }
  2002.  
  2003.  
  2004.             return transfer_count - transfers > 0;
  2005.         }
  2006.     }
  2007.  
  2008.     //todo review and update this one
  2009.     static public MyFixedPoint retrieve(BlockInventory dest, MyItemType t, MyFixedPoint v)
  2010.     {
  2011.         IDBG.set(dest, null);
  2012.         IDBG.set(t.SubtypeId);
  2013.         var nfo = t.GetItemInfo();
  2014.         int pidx = BlockInventory.bPriorityList.IndexOf(dest);
  2015.         IDBG.log("pidx=" + pidx);
  2016.         IDBG.log("BlockInventory.bPriorityList.Count=" + BlockInventory.bPriorityList.Count);
  2017.         for (var i = BlockInventory.bPriorityList.Count - 1; i > pidx; i--)
  2018.         {
  2019.             var inv = BlockInventory.bPriorityList[i];
  2020.             IDBG.set(dest, inv);
  2021.             if (inv.manifest != null && inv.manifest.stuff.ContainsKey(t))
  2022.             {
  2023.  
  2024.                 MyFixedPoint avail = inv.manifest.stuff[t];
  2025.                 IDBG.log(inv.b.CustomName + "has item, stock " + avail);
  2026.                 MyFixedPoint trns_amt = avail > v ? v : avail;
  2027.                 IDBG.log("tamt=" + trns_amt);
  2028.  
  2029.                 MyFixedPoint max_accept = (inv.manifest.freeVolume * (MyFixedPoint)(1 / nfo.Volume));
  2030.                 if (!nfo.IsOre && !nfo.IsIngot) max_accept = MyFixedPoint.Floor(max_accept + (MyFixedPoint)0.001);
  2031.                 if (trns_amt > max_accept) trns_amt = max_accept;
  2032.                 IDBG.log("tamt_ma=" + trns_amt);
  2033.                 var rem = transfer_item(inv, dest, t, trns_amt);
  2034.                 v -= trns_amt;
  2035.                 v += rem;
  2036.             }
  2037.             if (v <= 0) break;
  2038.         }
  2039.         return v;
  2040.     }
  2041.  
  2042.     static public MyFixedPoint expel(BlockInventory origin, MyItemType type, MyFixedPoint amount, bool inputs = false)
  2043.     {
  2044.         var nfo = type.GetItemInfo();
  2045.         var kw = cargokeywordbytype(type.TypeId);
  2046.         IDBG.set(type.SubtypeId);
  2047.         for (var i = 0; i < BlockInventory.bPriorityList.Count; i++)
  2048.         {
  2049.             var inv = BlockInventory.bPriorityList[i];
  2050.             if (inv != origin && !inv.locked)
  2051.             {
  2052.                 IDBG.set(origin, inv);
  2053.                 MyFixedPoint amt = 0;
  2054.                 MyFixedPoint max_accept = (inv.manifest.freeVolume * (MyFixedPoint)(1 / nfo.Volume));
  2055.                 if (!nfo.IsOre && !nfo.IsIngot) max_accept = MyFixedPoint.Floor(max_accept + (MyFixedPoint)0.001);
  2056.  
  2057.                 if (!inv.special && (inv.categories.Contains(kw)/* || inv.holdall*/)) amt = max_accept;
  2058.                 else if (inv.special)
  2059.                 {
  2060.                     MyFixedPoint stock = 0;
  2061.                     inv.manifest.stuff.TryGetValue(type, out stock);
  2062.                     MyFixedPoint trg = 0;
  2063.                     inv.stocktargets.TryGetValue(type, out trg);
  2064.                     if (trg > stock)
  2065.                     {
  2066.                         amt = trg - stock;
  2067.                         if (amt > max_accept) amt = max_accept;
  2068.                     }
  2069.                 }
  2070.                 if (amt > 0)
  2071.                 {
  2072.                     IDBG.log("maxaccept=" + max_accept);
  2073.                     IDBG.log("pushing " + amt + " to " + inv.b.CustomName);
  2074.                     var remaining = transfer_item(origin, inv, type, amt, inputs, inputs);
  2075.                     amount -= amt;
  2076.                     amount += remaining;
  2077.                 }
  2078.             }
  2079.             if (amount <= 0) break;
  2080.         }
  2081.         if (amount > 0)
  2082.         {
  2083.             StringBuilder err = new StringBuilder();
  2084.             bapp(err, "Warning: failed to expel ", type.SubtypeId, " from \"", origin.lastN, "\": nowhere else to store?");
  2085.             gInv.rerrlog(err.ToString());
  2086.         }
  2087.         return amount;
  2088.     }
  2089.  
  2090.     static public MyFixedPoint transfer_item(BlockInventory origin, BlockInventory dest, MyItemType type, MyFixedPoint amount,
  2091.     bool sendinputs = false, bool recieveinputs = false)
  2092.     {
  2093.         IDBG.set(origin, dest);
  2094.         IDBG.set(type.SubtypeId);
  2095.         if (amount == 0) return 0;
  2096.         IDBG.log("transfer_item " + type.SubtypeId + " " + amount+" "+origin.lastN+" > "+dest.lastN);
  2097.         //bool cerr = false;
  2098.         var sa = amount;
  2099.         foreach (var inva in origin.getSortedInventories(sendinputs))
  2100.         {
  2101.             foreach (var invb in dest.getSortedInventories(recieveinputs))
  2102.             {
  2103.                 amount = transfer_item(inva, invb, type, amount);
  2104.                 if (amount <= 0 || conveyor_error) break;
  2105.             }
  2106.             if (amount <= 0 || conveyor_error) break;
  2107.         }
  2108.         if (conveyor_error)
  2109.         {
  2110.             conveyor_error = false;
  2111.             StringBuilder err = new StringBuilder();
  2112.             bapp(err, "Warning: xfer fail: no conveyor \"", origin.lastN, "\" > \"", dest.lastN + "\"");
  2113.             gInv.rerrlog(err.ToString());
  2114.         }
  2115.         if (sa != amount)
  2116.         {
  2117.             //IDBG.log("moved " + (sa - v) + " " + t.SubtypeId + " to " + b.b.CustomName + " from " + a.b.CustomName);
  2118.             origin.updateM();
  2119.             dest.updateM();
  2120.         }
  2121.         return amount;
  2122.     }
  2123.  
  2124.     static int transfer_count = 0;
  2125.     static bool conveyor_error = false;
  2126.  
  2127.     static int transTick = 0;
  2128.     static double transMS = 0;
  2129.     //transfers up to amount of type to dest from origin. returns how much of the amount couldn't be sent for whatever reason
  2130.     static public MyFixedPoint transfer_item(IMyInventory origin, IMyInventory dest, MyItemType type, MyFixedPoint amount)
  2131.     {
  2132.         if(transTick != tick)
  2133.         {
  2134.             transTick = tick;
  2135.             transMS = 0;
  2136.         }
  2137.         DateTime s=  DateTime.Now;
  2138.         conveyor_error = false;
  2139.         List<MyInventoryItem> itms = new List<MyInventoryItem>();
  2140.         origin.GetItems(itms);
  2141.         foreach (MyInventoryItem item in itms)
  2142.         {
  2143.             if (item.Type == type)
  2144.             {
  2145.                 MyFixedPoint trns_amt = item.Amount > amount ? amount : item.Amount;
  2146.                 IDBG.log("_transfer_item " + type.SubtypeId + " " + trns_amt);
  2147.                 var nfo = type.GetItemInfo();
  2148.                 MyFixedPoint max_accept = ((dest.MaxVolume - dest.CurrentVolume) * (1f / nfo.Volume));
  2149.                 if(!nfo.UsesFractions) max_accept = MyFixedPoint.Floor(max_accept + (MyFixedPoint)0.001);
  2150.  
  2151.                 if (trns_amt > max_accept)
  2152.                 {
  2153.                     IDBG.log("_capping amt to "+max_accept);
  2154.                     trns_amt = max_accept;
  2155.                 }
  2156.                 if (trns_amt > 0)
  2157.                 {
  2158.                     transfer_count++;
  2159.                     if (origin.TransferItemTo(dest, item, trns_amt))
  2160.                     {
  2161.                         amount -= trns_amt;
  2162.                         IDBG.log("_successfully moved " + trns_amt + " of " + item.Type.SubtypeId);
  2163.                     }
  2164.                     else
  2165.                     {
  2166.                         bool conveyed = origin.CanTransferItemTo(dest, type);
  2167.                         IDBG.log("_failed to move. checkset conveyor flag");
  2168.                         if (!conveyed) conveyor_error = true;
  2169.                     }
  2170.                 }
  2171.             }
  2172.             if (amount <= 0) break;
  2173.         }
  2174.         transMS += (DateTime.Now - s).TotalMilliseconds;
  2175.         return amount;
  2176.     }
  2177.  
  2178.     class IDebugger
  2179.     {
  2180.         bool DEBUG = false;
  2181.         string FIRST_CARGO = "Nascent.Cargo [1.7].[Barge].AllTypes.P70";//2 CCTT Cargo Components Ammo P40";
  2182.         string SECOND_CARGO = "";//"3 Nascent.Cargo [1.7].[Barge].AllTypes.P70";
  2183.         string ITEM = "";// BelterComponent";// LidarComponent";// LithiumCell";
  2184.                                        //bool retrieve = true;
  2185.                                        //bool expel = false;
  2186.  
  2187.         BlockInventory a = null;
  2188.         BlockInventory b = null;
  2189.         string curitem = "";
  2190.         public void set(BlockInventory a, BlockInventory b)
  2191.         {
  2192.             this.a = a; this.b = b;
  2193.         }
  2194.         public void set(string item)
  2195.         {
  2196.             this.curitem = item;
  2197.         }
  2198.         public void log(string msg)
  2199.         {
  2200.             if (!DEBUG) return;
  2201.             bool l = true;
  2202.             if (FIRST_CARGO.Length > 0 && (a == null || a.lastN != FIRST_CARGO)) l = false;
  2203.             if (SECOND_CARGO.Length > 0 && (b == null || b.lastN != SECOND_CARGO)) l = false;
  2204.             if (ITEM.Length > 0 && curitem != ITEM) l = false;
  2205.             if (l)
  2206.             {
  2207.                 Program.log(msg);
  2208.             }
  2209.         }
  2210.     }
  2211.     static IDebugger IDBG = new IDebugger();
  2212.  
  2213.  
  2214.     public void updateContainers(List<IMyTerminalBlock> c)
  2215.     {
  2216.  
  2217.         List<IMyTerminalBlock> del = new List<IMyTerminalBlock>();
  2218.         foreach (var b in containers)
  2219.         {
  2220.             if (!c.Contains(b)) del.Add(b);
  2221.         }
  2222.         InventoryManifest dMan = new InventoryManifest();
  2223.         foreach (var b in del)
  2224.         {
  2225.             BlockInventory bi = BlockInventory.getBI(b);
  2226.             Inventory.globalManifest.sub(bi.manifest);
  2227.         }
  2228.         containers.Clear();
  2229.  
  2230.  
  2231.         foreach (var e in c) if (e.CubeGrid.EntityId != gProgram.Me.EntityId) containers.Add(e);
  2232.         foreach (var e in c) if (e.CubeGrid.EntityId == gProgram.Me.EntityId) containers.Add(e);
  2233.  
  2234.         upd();
  2235.     }
  2236.     List<IMyTerminalBlock> containers = new List<IMyTerminalBlock>();
  2237.     int nextC = 0;
  2238.     int nextCS = 0;
  2239.  
  2240.     bool itemsUpdating = true;
  2241.     public bool hasUpdatedOnce = false;
  2242.     public int updateInterval = 1;//60 * 10;
  2243.  
  2244.     public int lastUpdateTick = 0;
  2245.     public int ticksRun = 0;
  2246.  
  2247.     static Profiler invuP = new Profiler("invu");
  2248.  
  2249.     int updateCounter = 0;
  2250.  
  2251.     enum STATUS
  2252.     {
  2253.         PREINIT,
  2254.         INIT,
  2255.         MANIFESTS,
  2256.         IDLE
  2257.     }
  2258.     string[] statlbl = {
  2259.         "PREINIT",
  2260.         "INIT",
  2261.         "PROCESSING",
  2262.         "IDLE"
  2263.     };
  2264.     STATUS cstat = STATUS.PREINIT;
  2265.     Queue<string> errors = new Queue<string>();
  2266.     int rerrtick = 0;
  2267.     bool errd = false;
  2268.     public void rerrlog(string s)
  2269.     {
  2270.         errd = true;
  2271.         errors.Enqueue(s);
  2272.         if (errors.Count > 5) errors.Dequeue();
  2273.         rerrtick = tick;
  2274.     }
  2275.     static Profiler statP = new Profiler("stat");
  2276.     public string lastStatus = "";
  2277.     public void genstatus()
  2278.     {
  2279.         statP.s();
  2280.         if (tick % 5 == 0)
  2281.         {
  2282.             if (errd && tick - rerrtick > 10 * 60)
  2283.             {
  2284.                 errd = false;
  2285.                 errors.Clear();
  2286.             }
  2287.             StringBuilder status = new StringBuilder();
  2288.             var lbl = statlbl[(int)cstat];
  2289.             if (cstat >= STATUS.MANIFESTS && cstat != STATUS.IDLE)
  2290.             {
  2291.                 bapp(status, "Working ", nextC + 1, "/", containers.Count, "\n");
  2292.                 if(nextC < containers.Count)bapp(status, containers[nextC].CustomName, "\n");
  2293.             }
  2294.             bapp(status, lbl, "\n");
  2295.             bapp(status, transfer_count+" xfer ops this runtime\n\n");
  2296.             foreach (var l in errors) bapp(status, l, "\n");
  2297.             var s = status.ToString();
  2298.             if (s != lastStatus)
  2299.             {
  2300.                 lastStatus = s;
  2301.                 if (statusLog != null) statusLog.WriteText(s);
  2302.             }
  2303.         }
  2304.         statP.e();
  2305.     }
  2306.  
  2307.  
  2308.     void clr()
  2309.     {
  2310.         itemsUpdating = false;
  2311.         ticksRun = tick - lastUpdateTick;
  2312.         lastUpdateTick = tick;
  2313.         nextC = 0;
  2314.         cstat = STATUS.IDLE;
  2315.     }
  2316.     void upd()
  2317.     {
  2318.         cstat = STATUS.INIT;
  2319.  
  2320.         itemsUpdating = true;
  2321.         lastUpdateTick = tick;
  2322.         nextC = 0;
  2323.     }
  2324.  
  2325.     bool movedItems = false;
  2326.     int lastBlockUpdate = 0;
  2327.     int blockUpdateStep = 0;
  2328.     int SANchk = 0;
  2329.  
  2330.     static Profiler cdbgP = new Profiler("cdbg");
  2331.  
  2332.     public void update()
  2333.     {
  2334.         cdbgP.s();
  2335.         if (!itemsUpdating && (tick - lastUpdateTick > updateInterval))
  2336.         {
  2337.             upd();
  2338.  
  2339.             if (CARGODBG && cargodbg != null)
  2340.             {
  2341.                 //  genstatus();
  2342.                 //string o = "";
  2343.                 StringBuilder fml = new StringBuilder();
  2344.                 foreach (var e in BlockInventory.bPriorityList)
  2345.                 {
  2346.                     fml.Append(e.priority + "|||" + e.b.CustomName + "\n");
  2347.                 }
  2348.                 cargodbg.WriteText(fml.ToString());
  2349.             }
  2350.         }
  2351.         cdbgP.e();
  2352.         invuP.s();
  2353.         if (itemsUpdating)
  2354.         {
  2355.             if (nextC >= containers.Count)
  2356.             {
  2357.                 clr();
  2358.                 log("full run (" + containers.Count + "):" + ticksRun + "t (" + (ticksRun / 60.0d).ToString("0.0") + "s");
  2359.                 hasUpdatedOnce = true;
  2360.                 updateCounter += 1;
  2361.             }
  2362.             else
  2363.             {
  2364.                 var intrvl = blockInterval;
  2365.                 if (movedItems) intrvl = blockIntervalMove;
  2366.  
  2367.                 if (tick - lastBlockUpdate > intrvl)
  2368.                 {
  2369.                     IMyTerminalBlock t = containers[nextC];
  2370.                     BlockInventory bi = BlockInventory.getBI(t);
  2371.                     var bus = blockUpdateStep;
  2372.                     blockUpdateStep++;
  2373.                     if (bus == 0) bi.updateM();
  2374.                     if (bus == 1) bi.updateP();
  2375.                     if (bus == 2)
  2376.                     {
  2377.                         if (SORT)
  2378.                         {
  2379.                             movedItems = bi.updateT();
  2380.                             SANchk++;
  2381.                         }
  2382.                         if (!movedItems || SANchk > 10)
  2383.                         {
  2384.                             SANchk = 0;
  2385.                             lastBlockUpdate = tick;
  2386.                             blockUpdateStep = 0;
  2387.                             nextC++;
  2388.                         }
  2389.                         else
  2390.                         {
  2391.                             lastBlockUpdate = tick;
  2392.                             blockUpdateStep--;
  2393.                         }
  2394.                     }
  2395.                     cstat = STATUS.MANIFESTS;
  2396.                 }
  2397.             }
  2398.         }
  2399.         invuP.e();
  2400.         genstatus();
  2401.     }
  2402. }
  2403.  
  2404. public enum LT
  2405. {
  2406.     LOG_N = 0,
  2407.     LOG_D,
  2408.     LOG_DD
  2409. }
  2410. string[] logtype_labels = { "INFO","_DBG","DDBG"};
  2411.  
  2412. public static LT LOG_LEVEL = LT.LOG_N;
  2413. public static Logger logger = new Logger();
  2414. public static void log(string s, LT level)
  2415. {
  2416.     Logger.log(s, level);
  2417. }
  2418. public static void log(string s)
  2419. {
  2420.     Logger.log(s, LT.LOG_N);
  2421. }
  2422.  
  2423. public class Logger
  2424. {
  2425.     public class logmsg
  2426.     {
  2427.         public logmsg(string m,string m2, LT l) { msg = m; msg_raw = m2; level = l; }
  2428.         public string msg = "";
  2429.         public string msg_raw = "";
  2430.         public int c = 1;
  2431.         public LT level = LT.LOG_N;
  2432.     }
  2433.  
  2434.     static List<logmsg> loggedMessages = new List<logmsg>();
  2435.     static int MAX_LOG = 50;
  2436.     static List<logmsg> superLoggedMessages = new List<logmsg>();
  2437.     static int MAX_SUPER_LOG = 1000;
  2438.  
  2439.     static public bool loggedMessagesDirty = true;
  2440.  
  2441.     public static void log(string s, LT level)
  2442.     {
  2443.         if (level > LOG_LEVEL) return;
  2444.         string s2 = s;
  2445.         if (s.Length > 50)
  2446.         {
  2447.             List<string> tok = new List<string>();
  2448.             while (s.Length > 50)
  2449.             {
  2450.                 int c = 0;
  2451.                 if (tok.Count > 0) c = 2;
  2452.                 tok.Add(s.Substring(0, 50 - c));
  2453.                 s = s.Substring(50 - c);
  2454.             }
  2455.             tok.Add(s);
  2456.             s = string.Join("\n ", tok);
  2457.         }
  2458.         var p = gProgram;
  2459.         logmsg l = null;
  2460.         if (loggedMessages.Count > 0)
  2461.         {
  2462.             l = loggedMessages[loggedMessages.Count - 1];
  2463.         }
  2464.         if (l != null)
  2465.         {
  2466.             if (l.msg == s) l.c += 1;
  2467.             else loggedMessages.Add(new logmsg(s, s2, level));
  2468.         }
  2469.         else loggedMessages.Add(new logmsg(s, s2, level));
  2470.         if (loggedMessages.Count > MAX_LOG) loggedMessages.RemoveAt(0);
  2471.  
  2472.         l = null;
  2473.         if (superLoggedMessages.Count > 0)
  2474.         {
  2475.             l = superLoggedMessages[superLoggedMessages.Count - 1];
  2476.         }
  2477.         if (l != null)
  2478.         {
  2479.             if (l.msg == s) l.c += 1;
  2480.             else superLoggedMessages.Add(new logmsg(s, s2, level));
  2481.         }
  2482.         else superLoggedMessages.Add(new logmsg(s, s2, level));
  2483.         if (superLoggedMessages.Count > MAX_SUPER_LOG) superLoggedMessages.RemoveAt(0);
  2484.  
  2485.         loggedMessagesDirty = true;
  2486.     }
  2487.  
  2488.  
  2489.     static public string loggedMessagesRender = "";
  2490.     static public void updateLoggedMessagesRender()
  2491.     {
  2492.         if (!loggedMessagesDirty) return;
  2493.         StringBuilder b = new StringBuilder();
  2494.         //if (!loggedMessagesDirty) return;// loggedMessagesRender;
  2495.  
  2496.  
  2497.         foreach (var m in loggedMessages)
  2498.         {
  2499.             b.Append(m.msg);
  2500.             if (m.c > 1) bapp(b," (" ,m.c ,")");
  2501.             b.Append("\n");
  2502.         }
  2503.         string o = b.ToString();
  2504.         loggedMessagesDirty = false;
  2505.         loggedMessagesRender = o;
  2506.     }
  2507.     static public void writeSuperlog()
  2508.     {
  2509.         StringBuilder b = new StringBuilder();
  2510.         //if (!loggedMessagesDirty) return;// loggedMessagesRender;
  2511.  
  2512.  
  2513.         foreach (var m in superLoggedMessages)
  2514.         {
  2515.             b.Append(m.msg);
  2516.             if (m.c > 1) bapp(b, " (", m.c, ")");
  2517.             b.Append("\n");
  2518.         }
  2519.         string o = b.ToString();
  2520.         controllers[0].CustomData = o;
  2521.         log(controllers[0].CustomName, LT.LOG_N);
  2522.     }
  2523. }
  2524.  
  2525. static void bapp(StringBuilder b, params object[] args)
  2526. {
  2527.     foreach (object a in args)
  2528.     {
  2529.         b.Append(a.ToString());
  2530.     }
  2531. }
  2532. public class Stopwatch
  2533. {
  2534.     DateTime start;
  2535.     public Stopwatch()
  2536.     {
  2537.     start = DateTime.Now;
  2538.     }
  2539.     public double e()
  2540.     {
  2541.         return (DateTime.Now - start).TotalMilliseconds;
  2542.     }
  2543. }
  2544.  
  2545.  
  2546. public class Profiler
  2547. {
  2548.  
  2549.  
  2550.     static bool PROFILING_ENABLED = true;
  2551.     static List<Profiler> profilers = new List<Profiler>();
  2552.     const int mstracklen = 60;
  2553.     double[] mstrack = new double[mstracklen];
  2554.     double msdiv = 1.0d / mstracklen;
  2555.     int mscursor = 0;
  2556.     DateTime start_time = DateTime.MinValue;
  2557.     string Name = "";
  2558.     string pre = "";
  2559.     string post = "";
  2560.     int _ticks_between_calls = 1;
  2561.     int ltick = int.MinValue;
  2562.     //..int callspertick = 1;
  2563.  
  2564.     static int base_sort_position_c = 0;
  2565.     int base_sort_position = 0;
  2566.  
  2567.     bool nevercalled = true;
  2568.     //bool closed = true;
  2569.     public int getSortPosition()
  2570.     {
  2571.         if (nevercalled) return int.MaxValue;
  2572.         int mult = (int)Math.Pow(10, 8 - (depth * 2));
  2573.         if (parent != null) return parent.getSortPosition() + (base_sort_position * mult);
  2574.         return base_sort_position * mult;
  2575.     }
  2576.     static int basep = (int)Math.Pow(10, 5);
  2577.     public Profiler(string name)
  2578.     {
  2579.         if (PROFILING_ENABLED)
  2580.         {
  2581.             Name = name;
  2582.             profilers.Add(this);
  2583.             for(var i =0; i < mstracklen; i++)mstrack[i] = 0;
  2584.             base_sort_position = base_sort_position_c;
  2585.             base_sort_position_c += 1;
  2586.         }
  2587.     }
  2588.     public void s()
  2589.     {
  2590.         start();
  2591.     }
  2592.     public void e()
  2593.     {
  2594.         stop();
  2595.     }
  2596.     static List<Profiler> stack = new List<Profiler>();
  2597.     Profiler parent = null;
  2598.     int depth = 0;
  2599.     bool adding = false;
  2600.     public void start()
  2601.     {
  2602.         if (PROFILING_ENABLED)
  2603.         {
  2604.             //closed = false;
  2605.             nevercalled = false;
  2606.             if (tick != ltick)
  2607.             {
  2608.                 if (_ticks_between_calls == 1 && ltick != int.MinValue)
  2609.                 {
  2610.                     _ticks_between_calls = tick - ltick;
  2611.                 }else
  2612.                 {
  2613.                     var tbc = tick - ltick;
  2614.                     if (tbc != _ticks_between_calls)
  2615.                     {
  2616.                         _ticks_between_calls = 1;
  2617.                         for (var i = 0; i < mstracklen; i++) mstrack[i] = 0;
  2618.                     }
  2619.                 }
  2620.  
  2621.                 ltick = tick;
  2622.                 //callspertick = 1;
  2623.                 adding = false;
  2624.             }
  2625.             else
  2626.             {
  2627.                 adding = true;
  2628.             }
  2629.             if (depth == 0) depth = stack.Count;
  2630.             if (depth > 11) depth = 11;
  2631.             if (stack.Count > 0 && parent == null) parent = stack[stack.Count - 1];
  2632.             stack.Add(this);
  2633.             start_time = DateTime.Now;
  2634.         }
  2635.     }
  2636.     double lastms = 0;
  2637.     double average = 0;
  2638.  
  2639.  
  2640.     /// <summary>
  2641.             /// records a fake ms consumption for this timeframe - for tests or demo
  2642.             /// </summary>
  2643.     public double FAKE_stop(double fakems)
  2644.     {
  2645.         return stop(fakems);
  2646.     }
  2647.     /// <summary>
  2648.             /// adds the elapsed time since start() to the records
  2649.             /// </summary>
  2650.     public double stop()
  2651.     {
  2652.         double time = 0;
  2653.         if (PROFILING_ENABLED)
  2654.         {
  2655.             //closed = true;
  2656.             time = (DateTime.Now - start_time).TotalMilliseconds;
  2657.         }
  2658.         return stop(time);
  2659.     }
  2660.  
  2661.     private double stop(double _ms)
  2662.     {
  2663.         double time = 0;
  2664.         if (PROFILING_ENABLED)
  2665.         {
  2666.             time = _ms;
  2667.  
  2668.             stack.Pop();
  2669.             if (parent != null)
  2670.             {
  2671.                 depth = parent.depth + 1;
  2672.             }
  2673.  
  2674.             //if(!adding)mscursor = (mscursor + 1) % mstracklen;
  2675.  
  2676.  
  2677.             if (!adding) mstrack[mscursor] = 0;
  2678.             mstrack[mscursor] += time;
  2679.             if (!adding) mscursor = (mscursor + 1) % mstracklen;
  2680.  
  2681.             average = 0d;
  2682.             foreach (double ms in mstrack) average += ms;
  2683.             average *= msdiv;
  2684.             average /= _ticks_between_calls;
  2685.             lastms = time;
  2686.         }
  2687.         return time;
  2688.     }
  2689.     /// <summary>
  2690.             /// generates a monospaced report text. If called every tick, every 120 ticks it will recalculate treeview data.
  2691.             /// </summary>
  2692.     //the treeview can be initially inaccurate as some profilers might not be called every tick, depending on program architecture
  2693.     public string getReport(StringBuilder bu)
  2694.     {
  2695.         if (PROFILING_ENABLED)
  2696.         {
  2697.             if (tick % 120 == 25)//recalculate hacky treeview data, delayed by 25 ticks from program start
  2698.             {
  2699.                 try
  2700.                 {
  2701.                     profilers.Sort(delegate (Profiler x, Profiler y)
  2702.                     {
  2703.                         return x.getSortPosition().CompareTo(y.getSortPosition());
  2704.                     });
  2705.                 }
  2706.                 catch (Exception) { }
  2707.  
  2708.                 for (int i = 0; i < profilers.Count; i++)
  2709.                 {
  2710.                     Profiler p = profilers[i];
  2711.  
  2712.                     p.pre = "";
  2713.                     if (p.depth > 0 && p.parent != null)
  2714.                     {
  2715.                         bool parent_has_future_siblings = false;
  2716.                         bool has_future_siblings_under_parent = false;
  2717.                         for (int b = i + 1; b < profilers.Count; b++)
  2718.                         {
  2719.                             if (profilers[b].depth == p.parent.depth) parent_has_future_siblings = true;
  2720.                             if (profilers[b].depth == p.depth) has_future_siblings_under_parent = true;
  2721.                             if (profilers[b].depth < p.depth) break;
  2722.  
  2723.                         }
  2724.                         while (p.pre.Length < p.parent.depth)
  2725.                         {
  2726.                             if (parent_has_future_siblings) p.pre += "│";
  2727.                             else p.pre += " ";
  2728.                         }
  2729.                         bool last = false;
  2730.  
  2731.                         if (!has_future_siblings_under_parent)
  2732.                         {
  2733.                             if (i < profilers.Count - 1)
  2734.                             {
  2735.                                 if (profilers[i + 1].depth != p.depth) last = true;
  2736.                             }
  2737.                             else last = true;
  2738.                         }
  2739.                         if (last) p.pre += "└";
  2740.                         else p.pre += "├";
  2741.                         while (p.pre.Length < p.depth) p.pre += "─";
  2742.                     }
  2743.                 }
  2744.                 int mlen = 0;
  2745.                 foreach (Profiler p in profilers) if (p.pre.Length + p.Name.Length > mlen) mlen = p.pre.Length + p.Name.Length;
  2746.                 foreach (Profiler p in profilers)
  2747.                 {
  2748.                     p.post = "";
  2749.                     int l = p.pre.Length + p.Name.Length + p.post.Length;
  2750.                     if (l < mlen) p.post = new string('_', mlen - l);
  2751.                 }
  2752.             }
  2753.             if (nevercalled) bapp(bu, "!!!!", Name , "!!!!: NEVER CALLED!");
  2754.             else bapp(bu, pre, Name, post, ": " , lastms.ToString("0.00") , ";", average.ToString("0.00"));
  2755.         }
  2756.         return "";
  2757.     }
  2758.     static public string getAllReports()
  2759.     {
  2760.         StringBuilder b = new StringBuilder();
  2761.         //string r = "";
  2762.         if (PROFILING_ENABLED)
  2763.         {
  2764.             foreach (Profiler watch in profilers)
  2765.             {
  2766.                 watch.getReport(b);
  2767.                 b.Append("\n");
  2768.             }
  2769.         }
  2770.         if (stack.Count > 0)
  2771.         {
  2772.             bapp(b,"profile stack error:\n", stack.Count, "\n");
  2773.             foreach (var s in stack)
  2774.             {
  2775.                 bapp(b, s.Name, ",");
  2776.             }
  2777.         }
  2778.         return b.ToString();
  2779.     }
  2780. }
  2781.  
  2782. static IMyTextSurface consoleLog = null;
  2783. static IMyTextSurface statusLog = null;
  2784. static IMyTextSurface profileLog = null;
  2785. static IMyTextSurface cargodbg = null;
  2786.  
  2787. static IMyTextSurface autocraftingLCD = null;
  2788.  
  2789. static List<IMyAssembler> assemblers = new List<IMyAssembler>();
  2790.  
  2791.  
  2792. //static List<IMyTerminalBlock> weaponCoreWeapons = new List<IMyTerminalBlock>();
  2793. static List<IMyShipController> controllers = new List<IMyShipController>();
  2794. static List<IMyShipConnector> connectors = new List<IMyShipConnector>();
  2795. static List<IMyTerminalBlock> inventoryBlocks = new List<IMyTerminalBlock>();
  2796. static public WcPbApi APIWC = null;
  2797. static public ResourceLoader resourceLoader = null;
  2798. public class ResourceLoader
  2799. {
  2800.     public Program p;
  2801.  
  2802.     public bool neverFullyLoaded = true;
  2803.     public ResourceLoader()
  2804.     {
  2805.         mkBlockCheckMachine();
  2806.     }
  2807.  
  2808.     bool readConfig = false;
  2809.  
  2810.     public void update()
  2811.     {
  2812.         if (APIWC == null)
  2813.         {
  2814.             APIWC = new WcPbApi();
  2815.             try
  2816.             {
  2817.                 APIWC.Activate(gProgram.Me);
  2818.             }
  2819.             catch (Exception) { }
  2820.  
  2821.         }
  2822.         if (!APIWC.isReady && tick % 30 == 0)
  2823.         {
  2824.             try
  2825.             {
  2826.                 APIWC.Activate(gProgram.Me);
  2827.             }
  2828.             catch (Exception) { }
  2829.         }
  2830.         if (!APIWC.isReady) return;
  2831.  
  2832.         if (!readConfig || tick % 60 == 0)
  2833.         {
  2834.             readConfig = true;
  2835.             /*if (p.Me.CustomData != lastCustomData)
  2836.                     {
  2837.                         //log("Loading CustomData.", LT.LOG_N);
  2838.                         //deserializeConfig(p.Me.CustomData);
  2839.                         //p.Me.CustomData = lastCustomData = serializeConfig();
  2840.                     }*/
  2841.         }
  2842.  
  2843.         if (blockCheckMachine != null)
  2844.         {
  2845.             if (!blockCheckMachine.MoveNext())
  2846.             {
  2847.                 blockCheckMachine.Dispose();
  2848.                 blockCheckMachine = null;
  2849.             }
  2850.         }
  2851.         else if (readConfig && tick % (5 * 60 * 60) == 0) mkBlockCheckMachine();
  2852.     }
  2853.     public string lastCustomData = "-1";
  2854.  
  2855.     IEnumerator<bool> blockCheckMachine = null;
  2856.     void mkBlockCheckMachine()
  2857.     {
  2858.         if (blockCheckMachine != null) blockCheckMachine.Dispose();
  2859.         blockCheckMachine = blockLoader();
  2860.         step = 0;
  2861.     }
  2862.     public int step = 0;
  2863.  
  2864.     public bool isThis(IMyTerminalBlock b)
  2865.     {
  2866.         return b.OwnerId == p.Me.OwnerId && b.CubeGrid == p.Me.CubeGrid;
  2867.     }
  2868.     public IEnumerator<bool> blockLoader()
  2869.     {
  2870.         var gts = p.GridTerminalSystem;
  2871.         consoleLog = null;
  2872.         statusLog = null;
  2873.         profileLog = null;
  2874.         List<IMyTerminalBlock> LCDs = new List<IMyTerminalBlock>();
  2875.         gts.GetBlocksOfType(LCDs, b => (b is IMyTextSurface) && b.CubeGrid == p.Me.CubeGrid);
  2876.         foreach (var b in LCDs)
  2877.         {
  2878.             IMyTextSurface s = b as IMyTextSurface;
  2879.             if (b.CustomData.Contains("statusLog")) statusLog = s;
  2880.             else if (b.CustomData.Contains("consoleLog")) consoleLog = s;
  2881.             else if (b.CustomData.Contains("profileLog")) profileLog = s;
  2882.             else if (b.CustomData.Contains("cargodbg")) cargodbg = s;
  2883.             else if (b.CustomName.Contains("Autocrafting")) autocraftingLCD = s;
  2884.         }
  2885.         step++;
  2886.         yield return true;
  2887.         gts.GetBlocksOfType(controllers, isThis);
  2888.         step++;
  2889.         yield return true;
  2890.         step++;
  2891.         connectors.Clear();
  2892.         gts.GetBlocksOfType(connectors, isThis);
  2893.         yield return true;
  2894.         step++;
  2895.  
  2896.         if (USE_SKITS) gts.GetBlocksOfType(assemblers, isThis);
  2897.         else gts.GetBlocksOfType(assemblers, b => isThis(b) && b.DefinitionDisplayNameText != "Survival Kit");
  2898.  
  2899.         yield return true;
  2900.         step++;
  2901.         //gts.GetBlocksOfType(weaponCoreWeapons, b => b.CubeGrid == p.Me.CubeGrid && b.IsFunctional && APIWC.HasCoreWeapon(b));
  2902.         //yield return true;
  2903.         //step++;
  2904.         gts.GetBlocksOfType(inventoryBlocks, b => b.HasInventory && b.HasPlayerAccess(p.Me.OwnerId));
  2905.         yield return true;
  2906.         step++;
  2907.         if (neverFullyLoaded) log("BOOT DONE. "+tick+"t ("+(((float)tick)/60).ToString("0.0")+"s)", LT.LOG_N);
  2908.         neverFullyLoaded = false;
  2909.         step++;
  2910.         yield return false;
  2911.     }
  2912. }
  2913.  
  2914. public class SpriteHUDLCD
  2915. {
  2916.     static Dictionary<string, Color> ColorList = new Dictionary<string, Color> { { "aliceblue", Color.AliceBlue }, { "antiquewhite", Color.AntiqueWhite }, { "aqua", Color.Aqua }, { "aquamarine", Color.Aquamarine }, { "azure", Color.Azure }, { "beige", Color.Beige }, { "bisque", Color.Bisque }, { "black", Color.Black }, { "blanchedalmond", Color.BlanchedAlmond }, { "blue", Color.Blue }, { "blueviolet", Color.BlueViolet }, { "brown", Color.Brown }, { "burlywood", Color.BurlyWood }, { "badetblue", Color.CadetBlue }, { "chartreuse", Color.Chartreuse }, { "chocolate", Color.Chocolate }, { "coral", Color.Coral }, { "cornflowerblue", Color.CornflowerBlue }, { "cornsilk", Color.Cornsilk }, { "crimson", Color.Crimson }, { "cyan", Color.Cyan }, { "darkblue", Color.DarkBlue }, { "darkcyan", Color.DarkCyan }, { "darkgoldenrod", Color.DarkGoldenrod }, { "darkgray", Color.DarkGray }, { "darkgreen", Color.DarkGreen }, { "darkkhaki", Color.DarkKhaki }, { "darkmagenta", Color.DarkMagenta }, { "darkoliveGreen", Color.DarkOliveGreen }, { "darkorange", Color.DarkOrange }, { "darkorchid", Color.DarkOrchid }, { "darkred", Color.DarkRed }, { "darksalmon", Color.DarkSalmon }, { "darkseagreen", Color.DarkSeaGreen }, { "darkslateblue", Color.DarkSlateBlue }, { "darkslategray", Color.DarkSlateGray }, { "darkturquoise", Color.DarkTurquoise }, { "darkviolet", Color.DarkViolet }, { "deeppink", Color.DeepPink }, { "deepskyblue", Color.DeepSkyBlue }, { "dimgray", Color.DimGray }, { "dodgerblue", Color.DodgerBlue }, { "firebrick", Color.Firebrick }, { "floralwhite", Color.FloralWhite }, { "forestgreen", Color.ForestGreen }, { "fuchsia", Color.Fuchsia }, { "gainsboro", Color.Gainsboro }, { "ghostwhite", Color.GhostWhite }, { "gold", Color.Gold }, { "goldenrod", Color.Goldenrod }, { "gray", Color.Gray }, { "green", Color.Green }, { "greenyellow", Color.GreenYellow }, { "doneydew", Color.Honeydew }, { "hotpink", Color.HotPink }, { "indianred", Color.IndianRed }, { "indigo", Color.Indigo }, { "ivory", Color.Ivory }, { "khaki", Color.Khaki }, { "lavender", Color.Lavender }, { "lavenderblush", Color.LavenderBlush }, { "lawngreen", Color.LawnGreen }, { "lemonchiffon", Color.LemonChiffon }, { "lightblue", Color.LightBlue }, { "lightcoral", Color.LightCoral }, { "lightcyan", Color.LightCyan }, { "lightgoldenrodyellow", Color.LightGoldenrodYellow }, { "lightgray", Color.LightGray }, { "lightgreen", Color.LightGreen }, { "lightpink", Color.LightPink }, { "lightsalmon", Color.LightSalmon }, { "lightseagreen", Color.LightSeaGreen }, { "lightskyblue", Color.LightSkyBlue }, { "lightslategray", Color.LightSlateGray }, { "lightsteelblue", Color.LightSteelBlue }, { "lightyellow", Color.LightYellow }, { "lime", Color.Lime }, { "limegreen", Color.LimeGreen }, { "linen", Color.Linen }, { "magenta", Color.Magenta }, { "maroon", Color.Maroon }, { "mediumaquamarine", Color.MediumAquamarine }, { "mediumblue", Color.MediumBlue }, { "mediumorchid", Color.MediumOrchid }, { "mediumpurple", Color.MediumPurple }, { "mediumseagreen", Color.MediumSeaGreen }, { "mediumslateblue", Color.MediumSlateBlue }, { "mediumspringgreen", Color.MediumSpringGreen }, { "mediumturquoise", Color.MediumTurquoise }, { "mediumvioletred", Color.MediumVioletRed }, { "midnightblue", Color.MidnightBlue }, { "mintcream", Color.MintCream }, { "mistyrose", Color.MistyRose }, { "moccasin", Color.Moccasin }, { "navajowhite", Color.NavajoWhite }, { "navy", Color.Navy }, { "oldlace", Color.OldLace }, { "olive", Color.Olive }, { "olivedrab", Color.OliveDrab }, { "orange", Color.Orange }, { "orangered", Color.OrangeRed }, { "orchid", Color.Orchid }, { "palegoldenrod", Color.PaleGoldenrod }, { "palegreen", Color.PaleGreen }, { "paleturquoise", Color.PaleTurquoise }, { "palevioletred", Color.PaleVioletRed }, { "papayawhip", Color.PapayaWhip }, { "peachpuff", Color.PeachPuff }, { "peru", Color.Peru }, { "pink", Color.Pink }, { "plum", Color.Plum }, { "powderblue", Color.PowderBlue }, { "purple", Color.Purple }, { "red", Color.Red }, { "rosybrown", Color.RosyBrown }, { "royalblue", Color.RoyalBlue }, { "saddlebrown", Color.SaddleBrown }, { "salmon", Color.Salmon }, { "sandybrown", Color.SandyBrown }, { "seagreen", Color.SeaGreen }, { "seashell", Color.SeaShell }, { "sienna", Color.Sienna }, { "silver", Color.Silver }, { "skyblue", Color.SkyBlue }, { "slateblue", Color.SlateBlue }, { "slategray", Color.SlateGray }, { "snow", Color.Snow }, { "springgreen", Color.SpringGreen }, { "steelblue", Color.SteelBlue }, { "tan", Color.Tan }, { "teal", Color.Teal }, { "thistle", Color.Thistle }, { "tomato", Color.Tomato }, { "turquoise", Color.Turquoise }, { "violet", Color.Violet }, { "wheat", Color.Wheat }, { "white", Color.White }, { "whitesmoke", Color.WhiteSmoke }, { "yellow", Color.Yellow }, { "yellowgreen", Color.YellowGreen } };
  2917.     public IMyTextSurface s = null;
  2918.  
  2919.     public SpriteHUDLCD(IMyTextSurface s)
  2920.     {
  2921.         this.s = s;
  2922.     }
  2923.     int ltick = -1;
  2924.     string lasttext = "-1";
  2925.     public void setLCD(string text)
  2926.     {
  2927.         if (text != lasttext || tick - ltick > 120)
  2928.         {
  2929.             ltick = tick;
  2930.             lasttext = text;
  2931.             s.WriteText(text);
  2932.             List<object> tok = new List<object>();
  2933.             string[] tokens = text.Split(new string[] { "<color=" }, StringSplitOptions.None);
  2934.             for (int i = 0; i < tokens.Length; i++)
  2935.             {
  2936.                 var t = tokens[i];
  2937.                 foreach (var kvp in ColorList)
  2938.                 {
  2939.                     if (t.StartsWith(kvp.Key + ">"))
  2940.                     {
  2941.                         t = t.Substring(kvp.Key.Length + 1);
  2942.                         tok.Add(kvp.Value);
  2943.                         break;
  2944.                     }
  2945.                 }
  2946.                 tok.Add(t);
  2947.             }
  2948.  
  2949.             s.ContentType = ContentType.SCRIPT;
  2950.             s.Script = "";
  2951.             s.Font = "Monospace";
  2952.             RectangleF _viewport;
  2953.             _viewport = new RectangleF(
  2954.     (s.TextureSize - s.SurfaceSize) / 2f,
  2955.     s.SurfaceSize
  2956. );
  2957.             using (var frame = s.DrawFrame())
  2958.             {
  2959.                 var zpos = new Vector2(0, 0) + _viewport.Position + new Vector2(s.TextPadding / 100 * s.SurfaceSize.X, s.TextPadding / 100 * s.SurfaceSize.Y);
  2960.                 var position = zpos;
  2961.                 Color cColor = Color.White;
  2962.                 foreach (var t in tok)
  2963.                 {
  2964.                     if (t is Color) cColor = (Color)t;
  2965.                     else if (t is string) writeText((string)t, frame, ref position, zpos, s.FontSize, cColor);
  2966.                 }
  2967.             }
  2968.         }
  2969.     }
  2970.  
  2971.     public void writeText(string text, MySpriteDrawFrame frame, ref Vector2 pos, Vector2 zpos, float textSize, Color color)
  2972.     {
  2973.         string[] lines = text.Split('\n');
  2974.         for (int l = 0; l < lines.Length; l++)
  2975.         {
  2976.             var line = lines[l];
  2977.             if (line.Length > 0)
  2978.             {
  2979.                 MySprite sprite = MySprite.CreateText(line, "Monospace", color, textSize, TextAlignment.LEFT);
  2980.                 sprite.Position = pos;
  2981.                 frame.Add(sprite);
  2982.             }
  2983.             if (l < lines.Length - 1)
  2984.             {
  2985.                 pos.X = zpos.X;
  2986.                 pos.Y += 28 * textSize;
  2987.             }
  2988.             else pos.X += 20 * textSize * line.Length;
  2989.         }
  2990.     }
  2991. }
  2992.  
  2993. }
  2994.  
  2995. public class WcPbApi
  2996. {
  2997.     public string[] WcBlockTypeLabels = new string[]
  2998.         {
  2999.             "Any",
  3000.             "Offense",
  3001.             "Utility",
  3002.             "Power",
  3003.             "Production",
  3004.             "Thrust",
  3005.             "Jumping",
  3006.             "Steering"
  3007.         };
  3008.  
  3009.     private Action<ICollection<MyDefinitionId>> a;
  3010.     private Func<IMyTerminalBlock, IDictionary<string, int>, bool> b;
  3011.     private Action<IMyTerminalBlock, IDictionary<MyDetectedEntityInfo, float>> c;
  3012.     private Func<long, bool> d;
  3013.     private Func<long, int, MyDetectedEntityInfo> e;
  3014.     private Func<IMyTerminalBlock, long, int, bool> f;
  3015.     private Action<IMyTerminalBlock, bool, bool, int> g;
  3016.     private Func<IMyTerminalBlock, bool> h;
  3017.     private Action<IMyTerminalBlock, ICollection<MyDetectedEntityInfo>> i;
  3018.     private Func<IMyTerminalBlock, ICollection<string>, int, bool> j;
  3019.     private Action<IMyTerminalBlock, ICollection<string>, int> k;
  3020.     private Func<IMyTerminalBlock, long, int, Vector3D?> l;
  3021.  
  3022.     private Func<IMyTerminalBlock, int, Matrix> m;
  3023.     private Func<IMyTerminalBlock, int, Matrix> n;
  3024.     private Func<IMyTerminalBlock, long, int, MyTuple<bool, Vector3D?>> o;
  3025.     private Func<IMyTerminalBlock, int, string> p;
  3026.     private Action<IMyTerminalBlock, int, string> q;
  3027.     private Func<long, float> r;
  3028.     private Func<IMyTerminalBlock, int, MyDetectedEntityInfo> s;
  3029.     private Action<IMyTerminalBlock, long, int> t;
  3030.     private Func<long, MyTuple<bool, int, int>> u;
  3031.  
  3032.     private Action<IMyTerminalBlock, bool, int> v;
  3033.     private Func<IMyTerminalBlock, int, bool, bool, bool> w;
  3034.     private Func<IMyTerminalBlock, int, float> x;
  3035.     private Func<IMyTerminalBlock, int, MyTuple<Vector3D, Vector3D>> y;
  3036.     private Func<IMyTerminalBlock, float> _getCurrentPower;
  3037.     public Func<Sandbox.ModAPI.Ingame.IMyTerminalBlock, float> _getHeatLevel;
  3038.  
  3039.     public bool isReady = false;
  3040.     IMyTerminalBlock pbBlock = null;
  3041.     public bool Activate(IMyTerminalBlock pbBlock)
  3042.     {
  3043.         this.pbBlock = pbBlock;
  3044.         var dict = pbBlock.GetProperty("WcPbAPI")?.As<IReadOnlyDictionary<string, Delegate>>().GetValue(pbBlock);
  3045.         if (dict == null) throw new Exception("WcPbAPI failed to activate");
  3046.         return ApiAssign(dict);
  3047.     }
  3048.  
  3049.     public bool ApiAssign(IReadOnlyDictionary<string, Delegate> delegates)
  3050.     {
  3051.         if (delegates == null)
  3052.             return false;
  3053.         AssignMethod(delegates, "GetCoreWeapons", ref a);
  3054.         AssignMethod(delegates, "GetBlockWeaponMap", ref b);
  3055.         AssignMethod(delegates, "GetSortedThreats", ref c);
  3056.         AssignMethod(delegates, "GetObstructions", ref i);
  3057.         AssignMethod(delegates, "HasGridAi", ref d);
  3058.         AssignMethod(delegates, "GetAiFocus", ref e);
  3059.         AssignMethod(delegates, "SetAiFocus", ref f);
  3060.         AssignMethod(delegates, "HasCoreWeapon", ref h);
  3061.         AssignMethod(delegates, "GetPredictedTargetPosition", ref l);
  3062.         AssignMethod(delegates, "GetTurretTargetTypes", ref j);
  3063.         AssignMethod(delegates, "SetTurretTargetTypes", ref k);
  3064.         AssignMethod(delegates, "GetWeaponAzimuthMatrix", ref m);
  3065.         AssignMethod(delegates, "GetWeaponElevationMatrix", ref n);
  3066.         AssignMethod(delegates, "IsTargetAlignedExtended", ref o);
  3067.         AssignMethod(delegates, "GetActiveAmmo", ref p);
  3068.         AssignMethod(delegates, "SetActiveAmmo", ref q);
  3069.         AssignMethod(delegates, "GetConstructEffectiveDps", ref r);
  3070.         AssignMethod(delegates, "GetWeaponTarget", ref s);
  3071.         AssignMethod(delegates, "SetWeaponTarget", ref t);
  3072.         AssignMethod(delegates, "GetProjectilesLockedOn", ref u);
  3073.  
  3074.         AssignMethod(delegates, "FireWeaponOnce", ref v);
  3075.         AssignMethod(delegates, "ToggleWeaponFire", ref g);
  3076.         AssignMethod(delegates, "IsWeaponReadyToFire", ref w);
  3077.         AssignMethod(delegates, "GetMaxWeaponRange", ref x);
  3078.         AssignMethod(delegates, "GetWeaponScope", ref y);
  3079.  
  3080.         AssignMethod(delegates, "GetCurrentPower", ref _getCurrentPower);
  3081.         AssignMethod(delegates, "GetHeatLevel", ref _getHeatLevel);
  3082.  
  3083.         //Delegate.CreateDelegate(null, null);
  3084.  
  3085.         isReady = true;
  3086.         return true;
  3087.     }
  3088.     private void AssignMethod<T>(IReadOnlyDictionary<string, Delegate> delegates, string name, ref T field) where T : class
  3089.     {
  3090.         if (delegates == null)
  3091.         {
  3092.             field = null;
  3093.             return;
  3094.         }
  3095.         Delegate del;
  3096.         if (!delegates.TryGetValue(name, out del))
  3097.             throw new Exception($"{GetType().Name} :: Couldn't find {name} delegate of type {typeof(T)}");
  3098.         field = del as T;
  3099.         if (field == null)
  3100.             throw new Exception(
  3101.                 $"{GetType().Name} :: Delegate {name} is not type {typeof(T)}, instead it's: {del.GetType()}");
  3102.     }
  3103.  
  3104.     public void GetAllCoreWeapons(ICollection<MyDefinitionId> collection) => a?.Invoke(collection);
  3105.     public void GetSortedThreats(IDictionary<MyDetectedEntityInfo, float> collection) =>
  3106.         c?.Invoke(pbBlock, collection);
  3107.     public bool HasGridAi(long entity) => d?.Invoke(entity) ?? false;
  3108.     public MyDetectedEntityInfo? GetAiFocus(long shooter, int priority = 0) => e?.Invoke(shooter, priority);
  3109.  
  3110.     public bool SetAiFocus(IMyTerminalBlock pBlock, long target, int priority = 0) =>
  3111.         f?.Invoke(pBlock, target, priority) ?? false;
  3112.  
  3113.     public void ToggleWeaponFire(IMyTerminalBlock weapon, bool on, bool allWeapons, int weaponId = 0) =>
  3114.         g?.Invoke(weapon, on, allWeapons, weaponId);
  3115.     public bool HasCoreWeapon(IMyTerminalBlock weapon) => h?.Invoke(weapon) ?? false;
  3116.  
  3117.     public void GetObstructions(IMyTerminalBlock pBlock, ICollection<MyDetectedEntityInfo> collection) =>
  3118.         i?.Invoke(pBlock, collection);
  3119.  
  3120.     public Vector3D? GetPredictedTargetPosition(IMyTerminalBlock weapon, long targetEnt, int weaponId) =>
  3121.         l?.Invoke(weapon, targetEnt, weaponId) ?? null;
  3122.  
  3123.     public Matrix GetWeaponAzimuthMatrix(IMyTerminalBlock weapon, int weaponId) =>
  3124.         m?.Invoke(weapon, weaponId) ?? Matrix.Zero;
  3125.  
  3126.     public Matrix GetWeaponElevationMatrix(IMyTerminalBlock weapon, int weaponId) =>
  3127.         n?.Invoke(weapon, weaponId) ?? Matrix.Zero;
  3128.  
  3129.     public MyTuple<bool, Vector3D?> IsTargetAlignedExtended(IMyTerminalBlock weapon, long targetEnt, int weaponId) =>
  3130.         o?.Invoke(weapon, targetEnt, weaponId) ?? new MyTuple<bool, Vector3D?>();
  3131.     public string GetActiveAmmo(IMyTerminalBlock weapon, int weaponId) =>
  3132.         p?.Invoke(weapon, weaponId) ?? null;
  3133.  
  3134.     public void SetActiveAmmo(IMyTerminalBlock weapon, int weaponId, string ammoType) =>
  3135.         q?.Invoke(weapon, weaponId, ammoType);
  3136.  
  3137.     public float GetConstructEffectiveDps(long entity) => r?.Invoke(entity) ?? 0f;
  3138.  
  3139.     public MyDetectedEntityInfo? GetWeaponTarget(IMyTerminalBlock weapon, int weaponId = 0) =>
  3140.         s?.Invoke(weapon, weaponId);
  3141.  
  3142.     public void SetWeaponTarget(IMyTerminalBlock weapon, long target, int weaponId = 0) =>
  3143.         t?.Invoke(weapon, target, weaponId);
  3144.  
  3145.     public bool GetBlockWeaponMap(IMyTerminalBlock weaponBlock, IDictionary<string, int> collection) =>
  3146.         b?.Invoke(weaponBlock, collection) ?? false;
  3147.  
  3148.     public MyTuple<bool, int, int> GetProjectilesLockedOn(long victim) =>
  3149.         u?.Invoke(victim) ?? new MyTuple<bool, int, int>();
  3150.  
  3151.     public void FireWeaponOnce(IMyTerminalBlock weapon, bool allWeapons = true, int weaponId = 0) =>
  3152.             v?.Invoke(weapon, allWeapons, weaponId);
  3153.  
  3154.  
  3155.     public bool IsWeaponReadyToFire(IMyTerminalBlock weapon, int weaponId = 0, bool anyWeaponReady = true,
  3156.         bool shootReady = false) =>
  3157.         w?.Invoke(weapon, weaponId, anyWeaponReady, shootReady) ?? false;
  3158.  
  3159.     public float GetMaxWeaponRange(IMyTerminalBlock weapon, int weaponId) =>
  3160.         x?.Invoke(weapon, weaponId) ?? 0f;
  3161.  
  3162.     public MyTuple<Vector3D, Vector3D> GetWeaponScope(IMyTerminalBlock weapon, int weaponId) =>
  3163.         y?.Invoke(weapon, weaponId) ?? new MyTuple<Vector3D, Vector3D>();
  3164.     public float GetCurrentPower(IMyTerminalBlock weapon) => _getCurrentPower?.Invoke(weapon) ?? 0f;
  3165.  
  3166.     public float GetHeatLevel(Sandbox.ModAPI.Ingame.IMyTerminalBlock weapon) => _getHeatLevel?.Invoke(weapon) ?? 0f;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement