Advertisement
Guest User

Mining script improved by Terpla.TPMiningScript-2018-06-21v1

a guest
Jun 21st, 2018
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 31.24 KB | None | 0 0
  1. /* Mining script improved by Terpla. Version TPMiningScript-2018-06-21v1
  2. Features:
  3.     - warp to bookmarks
  4.     - home stantion as bookmark
  5.     - launch drones in asteroid belt and scope them before warp
  6.     - activate shield booster if shield < 90%
  7. */
  8.  
  9. /*
  10. This bot mines ore from asteroids and offloads the ore in a station.
  11.  
  12. Before running this bot, prepare the EVE online client as follows:
  13.  
  14. + Set the UI language to english.
  15. + Move your mining ship to a solar system which has asteroid belts and at least one station in which you can dock.
  16. + In the Overview, create a preset which includes asteroids and rats and enter the name of that preset in the configuration section below at 'OverviewPreset'. The bot will make sure this preset is loaded when it needs to use the overview.
  17. + Set Overview to sort by distance with the nearest entry at the top.
  18. + In the Inventory, select the 'List' view.
  19. + Enable the info panel 'System info'. The bot needs this to find asteroid belts and stations.
  20. + Arrange windows to not occlude ship modules or info panels.
  21. + In the ship UI, disable "Display Passive Modules" and disable "Display Empty Slots" and enable "Display Module Tooltips". The bot uses the module tooltips to automatically identify the properties of the modules.
  22.  
  23. This bot tracks the total mined amount of ore by watching inventory volume while mining modules are active.
  24. */
  25.  
  26. using BotSharp.ToScript.Extension;
  27. using MemoryStruct = Sanderling.Interface.MemoryStruct;
  28. using Parse = Sanderling.Parse;
  29.  
  30. //  Begin of configuration section ->
  31.  
  32. //  The bot loads this preset to the active tab in the Overview window.
  33. string OverviewPreset = null;
  34.  
  35. //  Activate shield hardener.
  36. var ActivateHardener = true;
  37.  
  38. //  Bot will switch mining site when rats are visible and shield hitpoints are lower than this value.
  39. var SwitchMiningSiteHitpointThresholdPercent = 25;
  40.  
  41. var EmergencyWarpOutHitpointPercent = 15;
  42.  
  43. //  Percentage of fill level at which to enter the offload process.
  44. var EnterOffloadOreContainerFillPercent = 96;
  45.  
  46. //  Warp to station when a neutral or hostile is visible in local.
  47. var RetreatOnNeutralOrHostileInLocal = false;
  48.  
  49. //  Name of the container to unload to as shown in inventory.
  50. string UnloadDestContainerName = "Item Hangar";
  51.  
  52. //  When this is set to true, the bot will try to unload when undocked.
  53. bool UnloadInSpace = false;
  54.  
  55. //  ----
  56. //  Should the bot use bookmarks for mining site?
  57. bool ShouldUseMiningBookmarks = true;
  58.  
  59. //  The bot uses the bookmarks from the menu which is opened from the button in the 'System info' panel.
  60. //  Bookmarks of places to mine. Add additional bookmarks separated by comma.
  61. string[] SetMiningSiteBookmark = new[] {
  62.     "mine1",
  63.     "mine2",
  64.     "mine3",
  65.     "mine4",
  66.     "mine5",
  67.     "mine6",
  68.     "mine7",
  69.     "mine8",
  70.     "mine9",
  71.     "mine10",
  72.     "mine11",
  73.     "mine12",
  74.     };
  75.  
  76. //  Bookmark of location where ore should be unloaded.
  77. string UnloadBookmark = "HOME";
  78.  
  79. //  <- End of configuration section
  80.  
  81.  
  82. Func<object> BotStopActivity = () => null;
  83.  
  84. Func<object> NextActivity = MainStep;
  85.  
  86. Queue<string> visitedLocations = new Queue<string>();
  87.  
  88. // State used to track total mined ore. -->
  89. struct StatisticsMeasurementSnapshot
  90. {
  91.     public Int64 timeMilli;
  92.     public Int64 volumeMilli;
  93. }
  94. readonly Queue<StatisticsMeasurementSnapshot> efficiencyRawMeasurements = new Queue<StatisticsMeasurementSnapshot>();
  95. readonly Queue<StatisticsMeasurementSnapshot> efficiencyDebouncedMeasurements = new Queue<StatisticsMeasurementSnapshot>();
  96. Int64 miningModuleLastActiveTime = 0;
  97. Int64 totalMinedOreVolumeMilli = 0;
  98. // <-- State used to track total mined ore.
  99.  
  100. for(;;)
  101. {
  102.     var stepBeginTimeMilli = Host.GetTimeContinuousMilli();
  103.  
  104.     MemoryUpdate();
  105.  
  106.     Host.Log(
  107.         "ore container fill: " + OreContainerFillPercent + "%" +
  108.         ", mining range: " + MiningRange +
  109.         ", mining modules (inactive): " + SetModuleMiner?.Length + "(" + SetModuleMinerInactive?.Length + ")" +
  110.         ", shield.hp: " + ShieldHpPercent + "%" +
  111.         ", retreat: " + RetreatReason +
  112.         ", JLA: " + JammedLastAge +
  113.         ", overview.rats: " + ListRatOverviewEntry?.Length +
  114.         ", overview.roids: " + ListAsteroidOverviewEntry?.Length +
  115.         ", offload count: " + OffloadCount +
  116.         ", nextAct: " + NextActivity?.Method?.Name);
  117.  
  118.     CloseModalUIElement();
  119.  
  120.     if(0 < RetreatReason?.Length && !(Measurement?.IsDocked ?? false))
  121.     {
  122.         InitiateWarpToRetreat();
  123.         continue;
  124.     }
  125.  
  126.     NextActivity = NextActivity?.Invoke() as Func<object>;
  127.  
  128.     if(BotStopActivity == NextActivity)
  129.         break;
  130.  
  131.     if(null == NextActivity)
  132.         NextActivity = MainStep;
  133.  
  134.     Host.Delay((int)Math.Max(0, 1000 - (Host.GetTimeContinuousMilli() - stepBeginTimeMilli)));
  135. }
  136.  
  137. bool? ShipHasOreHold
  138. {
  139.     get
  140.     {
  141.         var inventoryActiveShipEntry = WindowInventory?.ActiveShipEntry;
  142.  
  143.         //  If the tree entry for the ship is not expanded....
  144.         if(!(IsExpanded(inventoryActiveShipEntry) ?? false))
  145.             return null;    // Then I do not know if there is an ore hold.
  146.  
  147.         return inventoryActiveShipEntry?.TreeEntryFromCargoSpaceType(ShipCargoSpaceTypeEnum.OreHold) != null;
  148.     }
  149. }
  150.    
  151.  
  152. //  seconds since ship was jammed.
  153. long? JammedLastAge => Jammed ? 0 : (Host.GetTimeContinuousMilli() - JammedLastTime) / 1000;
  154.  
  155. int?    ShieldHpPercent => ShipUi?.HitpointsAndEnergy?.Shield / 10;
  156.  
  157. bool    ShouldSwitchMiningSite =>
  158.     !(Measurement?.IsDocked ?? false) &&
  159.     0 < ListRatOverviewEntry?.Length &&
  160.     !(SwitchMiningSiteHitpointThresholdPercent < ShieldHpPercent) || JammedLastAge < 10;
  161.  
  162. bool    OreContainerFilledForOffload => Math.Max(0, Math.Min(100, EnterOffloadOreContainerFillPercent)) <= OreContainerFillPercent;
  163.  
  164. Int64?  JammedLastTime = null;
  165. string RetreatReasonTemporary = null;
  166. string RetreatReasonPermanent = null;
  167. string RetreatReason => RetreatReasonPermanent ?? RetreatReasonTemporary;
  168. int? LastCheckOreContainerFillPercent = null;
  169.  
  170. int OffloadCount = 0;
  171.  
  172. Func<object>    MainStep()
  173. {
  174.     if(Measurement?.IsDocked ?? false)
  175.     {
  176.         InInventoryUnloadItems();
  177.  
  178.         if (0 < RetreatReasonPermanent?.Length)
  179.             return BotStopActivity;
  180.  
  181.         if (0 < RetreatReason?.Length)
  182.             return MainStep;
  183.  
  184.         Undock();
  185.     }
  186.  
  187.     EnsureOverviewTypeSelectionLoaded();
  188.  
  189.     EnsureWindowInventoryOreContainerIsOpen();
  190.  
  191.     if(ReadyForManeuver)
  192.     {  
  193.         DroneEnsureInBay();
  194.        
  195.         if(OreContainerFilledForOffload)
  196.         {
  197.             if(ReadyForManeuver)
  198.                 InitiateDockToOffload();
  199.  
  200.             if (UnloadInSpace)
  201.             {
  202.                 Host.Delay(4444);
  203.                 InInventoryUnloadItems();
  204.             }
  205.  
  206.             return MainStep;
  207.         }
  208.  
  209.         if(!(ListAsteroidOverviewEntry?.Length > 0) || ShouldSwitchMiningSite)
  210.         {
  211.             if(ShouldUseMiningBookmarks)
  212.                 InitiateWarpToRandomMiningBookmark();
  213.             else
  214.                 InitiateWarpToMiningSite();
  215.         }
  216.     }
  217.  
  218.     ModuleMeasureAllTooltip();
  219.  
  220.     if(ActivateHardener)
  221.         ActivateHardenerExecute();
  222.  
  223.     return InBeltMineStep;
  224. }
  225.  
  226. int RandomInt() => new Random((int)Host.GetTimeContinuousMilli()).Next();
  227.  
  228. T RandomElement<T>(IEnumerable<T> sequence)
  229. {
  230.     var array = (sequence as T[]) ?? sequence?.ToArray();
  231.  
  232.     if (!(0 < array?.Length))
  233.         return default(T);
  234.  
  235.     return array[RandomInt() % array.Length];
  236. }
  237.  
  238. void CloseModalUIElement()
  239. {
  240.     var ButtonClose =
  241.         ModalUIElement?.ButtonText?.FirstOrDefault(button => (button?.Text).RegexMatchSuccessIgnoreCase("close|no|ok"));
  242.  
  243.     Sanderling.MouseClickLeft(ButtonClose);
  244. }
  245.  
  246. Func<object> InBeltMineStep()
  247. {
  248.     if(ShouldSwitchMiningSite)
  249.         return MainStep;
  250.  
  251.     EnsureWindowInventoryOreContainerIsOpen();
  252.  
  253.     EnsureOverviewTypeSelectionLoaded();
  254.  
  255.     if(OreContainerFilledForOffload)
  256.         return null;
  257.  
  258.     var moduleMinerInactive = SetModuleMinerInactive?.FirstOrDefault();
  259.  
  260.     if (null == moduleMinerInactive || !(ReadyForManeuver))
  261.     {
  262.         DelayWhileUpdatingMemory(6000);
  263.         return InBeltMineStep;
  264.     }
  265.        
  266.     if (!(DronesInSpaceCount > 0))
  267.         DroneLaunch();
  268.  
  269.     var setTargetAsteroidInRange    =
  270.         SetTargetAsteroid?.Where(target => target?.DistanceMax <= MiningRange)?.ToArray();
  271.  
  272.     var setTargetAsteroidInRangeNotAssigned =
  273.         setTargetAsteroidInRange?.Where(target => !(0 < target?.Assigned?.Length))?.ToArray();
  274.  
  275.     Host.Log("targeted asteroids in range (without assignment): " + setTargetAsteroidInRange?.Length + " (" + setTargetAsteroidInRangeNotAssigned?.Length + ")");
  276.  
  277.     if(0 < setTargetAsteroidInRangeNotAssigned?.Length)
  278.     {
  279.         var targetAsteroidInputFocus    =
  280.             setTargetAsteroidInRangeNotAssigned?.FirstOrDefault(target => target?.IsSelected ?? false);
  281.  
  282.         if(null == targetAsteroidInputFocus)
  283.             Sanderling.MouseClickLeft(setTargetAsteroidInRangeNotAssigned?.FirstOrDefault());
  284.  
  285.         ModuleToggle(moduleMinerInactive);
  286.  
  287.         return InBeltMineStep;
  288.     }
  289.    
  290.     bool ShouldTurnOnShieldBooster = ShieldHpPercent <90;
  291.     ToggleShieldBooster(ShouldTurnOnShieldBooster);
  292.  
  293.     var asteroidOverviewEntryNext = ListAsteroidOverviewEntry?.FirstOrDefault();
  294.     var asteroidOverviewEntryNextNotTargeted = ListAsteroidOverviewEntry?.FirstOrDefault(entry => !((entry?.MeTargeted ?? false) || (entry?.MeTargeting ?? false)));
  295.  
  296.     Host.Log("next asteroid: (" + asteroidOverviewEntryNext?.Name + " , distance: " + asteroidOverviewEntryNext?.DistanceMax + ")" +
  297.         ", next asteroid not targeted: (" + asteroidOverviewEntryNext?.Name + " , distance: " + asteroidOverviewEntryNext?.DistanceMax + ")");
  298.  
  299.     if(null == asteroidOverviewEntryNext)
  300.     {
  301.         Host.Log("no asteroid available");
  302.         return null;
  303.     }
  304.  
  305.     if(null == asteroidOverviewEntryNextNotTargeted)
  306.     {
  307.         Host.Log("all asteroids targeted");
  308.         return null;
  309.     }
  310.  
  311.     if (!(asteroidOverviewEntryNextNotTargeted.DistanceMax < MiningRange))
  312.     {
  313.         if(!(1111 < asteroidOverviewEntryNext?.DistanceMin))
  314.         {
  315.             Host.Log("distance between asteroids too large");
  316.             return null;
  317.         }
  318.  
  319.         Host.Log("out of range, approaching");
  320.         ClickMenuEntryOnMenuRoot(asteroidOverviewEntryNext, "approach");
  321.     }
  322.     else
  323.     {
  324.         Host.Log("initiate lock asteroid");
  325.         ClickMenuEntryOnMenuRoot(asteroidOverviewEntryNextNotTargeted, "^lock");
  326.     }
  327.    
  328.     return InBeltMineStep;
  329. }
  330.  
  331. Sanderling.Accumulation.IShipUiModule[] SetModuleShieldBoosters =>
  332.     Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule?.Where(module => module?.TooltipLast?.Value?.IsShieldBooster ?? false)?.ToArray();
  333.  
  334. void ToggleShieldBooster(bool turnOn = true)
  335. {
  336.     var shieldBooster = SetModuleShieldBoosters?.FirstOrDefault();
  337.     if((shieldBooster?.RampActive ?? false) !=turnOn)
  338.     {      
  339.         Host.Log("toggle Shield Booster.");
  340.         ModuleToggle(shieldBooster);
  341.     }
  342. }
  343.  
  344. Sanderling.Parse.IMemoryMeasurement Measurement =>
  345.     Sanderling?.MemoryMeasurementParsed?.Value;
  346.  
  347. IWindow ModalUIElement =>
  348.     Measurement?.EnumerateReferencedUIElementTransitive()?.OfType<IWindow>()?.Where(window => window?.isModal ?? false)
  349.     ?.OrderByDescending(window => window?.InTreeIndex ?? int.MinValue)
  350.     ?.FirstOrDefault();
  351.  
  352. IEnumerable<Parse.IMenu> Menu => Measurement?.Menu;
  353.  
  354. Parse.IShipUi ShipUi => Measurement?.ShipUi;
  355.  
  356. bool Jammed => ShipUi?.EWarElement?.Any(EwarElement => (EwarElement?.EWarType).RegexMatchSuccess("electronic")) ?? false;
  357.  
  358. Sanderling.Interface.MemoryStruct.IMenuEntry MenuEntryLockTarget =>
  359.     Menu?.FirstOrDefault()?.Entry?.FirstOrDefault(entry => entry.Text.RegexMatchSuccessIgnoreCase("^lock"));
  360.  
  361. Sanderling.Parse.IWindowOverview    WindowOverview  =>
  362.     Measurement?.WindowOverview?.FirstOrDefault();
  363.  
  364. Sanderling.Parse.IWindowInventory   WindowInventory =>
  365.     Measurement?.WindowInventory?.FirstOrDefault();
  366.  
  367. ITreeViewEntry InventoryActiveShipOreContainer
  368. {
  369.     get
  370.     {
  371.         var hasOreHold = ShipHasOreHold;
  372.  
  373.         if(hasOreHold == null)
  374.             return null;
  375.  
  376.         return
  377.             WindowInventory?.ActiveShipEntry?.TreeEntryFromCargoSpaceType(
  378.                 hasOreHold.Value ? ShipCargoSpaceTypeEnum.OreHold : ShipCargoSpaceTypeEnum.General);
  379.     }
  380. }
  381.  
  382. IInventoryCapacityGauge OreContainerCapacityMilli =>
  383.     (InventoryActiveShipOreContainer?.IsSelected ?? false) ? WindowInventory?.SelectedRightInventoryCapacityMilli : null;
  384.  
  385. int? OreContainerFillPercent => (int?)((OreContainerCapacityMilli?.Used * 100) / OreContainerCapacityMilli?.Max);
  386.  
  387. Tab OverviewPresetTabActive =>
  388.     WindowOverview?.PresetTab
  389.     ?.OrderByDescending(tab => tab?.LabelColorOpacityMilli ?? 0)
  390.     ?.FirstOrDefault();
  391.  
  392. string OverviewTypeSelectionName =>
  393.     WindowOverview?.Caption?.RegexMatchIfSuccess(@"\(([^\)]*)\)")?.Groups?[1]?.Value;
  394.  
  395. Parse.IOverviewEntry[] ListRatOverviewEntry => WindowOverview?.ListView?.Entry?.Where(entry =>
  396.         (entry?.MainIconIsRed ?? false) && (entry?.IsAttackingMe ?? false))
  397.         ?.OrderBy(entry => entry?.DistanceMax ?? int.MaxValue)
  398.         ?.ToArray();
  399.  
  400. Parse.IOverviewEntry[] ListAsteroidOverviewEntry =>
  401.     WindowOverview?.ListView?.Entry
  402.     ?.Where(entry => null != OreTypeFromAsteroidName(entry?.Name))
  403.     ?.OrderBy(entry => entry.DistanceMax ?? int.MaxValue)
  404.     ?.ToArray();
  405.    
  406.  
  407. bool ReadyForManeuverNot =>
  408.     Measurement?.ShipUi?.Indication?.LabelText?.Any(indicationLabel =>
  409.         (indicationLabel?.Text).RegexMatchSuccessIgnoreCase("warp|docking")) ?? false;
  410.  
  411. bool ReadyForManeuver => !ReadyForManeuverNot && !(Measurement?.IsDocked ?? true);
  412.  
  413. Sanderling.Parse.IShipUiTarget[] SetTargetAsteroid =>
  414.     Measurement?.Target?.Where(target =>
  415.         target?.TextRow?.Any(textRow => textRow.RegexMatchSuccessIgnoreCase("asteroid")) ?? false)?.ToArray();
  416.  
  417. Sanderling.Interface.MemoryStruct.IListEntry    WindowInventoryItem =>
  418.     WindowInventory?.SelectedRightInventory?.ListView?.Entry?.FirstOrDefault();
  419.  
  420. Sanderling.Accumulation.IShipUiModule[] SetModuleMiner =>
  421.     Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule?.Where(module => module?.TooltipLast?.Value?.IsMiner ?? false)?.ToArray();
  422.  
  423. Sanderling.Accumulation.IShipUiModule[] SetModuleMinerInactive   =>
  424.     SetModuleMiner?.Where(module => !(module?.RampActive ?? false))?.ToArray();
  425.  
  426. int?    MiningRange => SetModuleMiner?.Select(module =>
  427.     module?.TooltipLast?.Value?.RangeOptimal ?? module?.TooltipLast?.Value?.RangeMax ?? module?.TooltipLast?.Value?.RangeWithin ?? 0)?.DefaultIfEmpty(0)?.Min();;
  428.  
  429. WindowChatChannel chatLocal =>
  430.      Sanderling.MemoryMeasurementParsed?.Value?.WindowChatChannel
  431.      ?.FirstOrDefault(windowChat => windowChat?.Caption?.RegexMatchSuccessIgnoreCase("local") ?? false);
  432.  
  433. //    assuming that own character is always visible in local
  434. bool hostileOrNeutralsInLocal => 1 != chatLocal?.ParticipantView?.Entry?.Count(IsNeutralOrEnemy);
  435.  
  436. //  extract the ore type from the name as seen in overview. "Asteroid (Plagioclase)"
  437. string OreTypeFromAsteroidName(string AsteroidName) =>
  438.     AsteroidName.ValueFromRegexMatchGroupAtIndex(@"Asteroid \(([^\)]+)", 0);
  439.  
  440. void ClickMenuEntryOnMenuRoot(IUIElement MenuRoot, string MenuEntryRegexPattern)
  441. {
  442.     Sanderling.MouseClickRight(MenuRoot);
  443.    
  444.     var Menu = Measurement?.Menu?.FirstOrDefault();
  445.    
  446.     var MenuEntry = Menu?.EntryFirstMatchingRegexPattern(MenuEntryRegexPattern, RegexOptions.IgnoreCase);
  447.    
  448.     Sanderling.MouseClickLeft(MenuEntry);
  449. }
  450.  
  451. void EnsureWindowInventoryOpen()
  452. {
  453.     if (null != WindowInventory)
  454.         return;
  455.  
  456.     Host.Log("open Inventory.");
  457.     Sanderling.MouseClickLeft(Measurement?.Neocom?.InventoryButton);
  458. }
  459.  
  460. void EnsureWindowInventoryOreContainerIsOpen()
  461. {
  462.     EnsureWindowInventoryOpen();
  463.  
  464.     var inventoryActiveShip = WindowInventory?.ActiveShipEntry;
  465.  
  466.     if(InventoryActiveShipOreContainer == null && !(IsExpanded(inventoryActiveShip) ?? false))
  467.     {
  468.         Host.Log("It looks like the active ships entry in the inventory is not expanded. I try to expand it to see if the ship has an ore hold.");
  469.         Sanderling.MouseClickLeft(inventoryActiveShip?.ExpandToggleButton);
  470.     }
  471.  
  472.     if(!(InventoryActiveShipOreContainer?.IsSelected ?? false))
  473.         Sanderling.MouseClickLeft(InventoryActiveShipOreContainer);
  474. }
  475.  
  476. //  sample label text: Intensive Reprocessing Array <color=#66FFFFFF>1,123 m</color>
  477. string InventoryContainerLabelRegexPatternFromContainerName(string containerName) =>
  478.     @"^\s*" + Regex.Escape(containerName) + @"\s*($|\<)";
  479.  
  480. void InInventoryUnloadItems() => InInventoryUnloadItemsTo(UnloadDestContainerName);
  481.  
  482. void InInventoryUnloadItemsTo(string DestinationContainerName)
  483. {
  484.     Host.Log("unload items to '" + DestinationContainerName + "'.");
  485.  
  486.     EnsureWindowInventoryOreContainerIsOpen();
  487.  
  488.     for (;;)
  489.     {
  490.         var oreContainerListItem = WindowInventory?.SelectedRightInventory?.ListView?.Entry?.ToArray();
  491.  
  492.         var oreContainerItem = oreContainerListItem?.FirstOrDefault();
  493.  
  494.         if(null == oreContainerItem)
  495.             break;    //    0 items in the container which holds the ore.
  496.  
  497.         if(2 < oreContainerListItem?.Length)
  498.             ClickMenuEntryOnMenuRoot(oreContainerItem, @"select\s*all");
  499.  
  500.         var DestinationContainerLabelRegexPattern =
  501.             InventoryContainerLabelRegexPatternFromContainerName(DestinationContainerName);
  502.  
  503.         var DestinationContainer =
  504.             WindowInventory?.LeftTreeListEntry?.SelectMany(entry => new[] { entry }.Concat(entry.EnumerateChildNodeTransitive()))
  505.             ?.FirstOrDefault(entry => entry?.Text?.RegexMatchSuccessIgnoreCase(DestinationContainerLabelRegexPattern) ?? false);
  506.  
  507.         if (null == DestinationContainer)
  508.             Host.Log("error: Inventory entry labeled '" + DestinationContainerName + "' not found");
  509.  
  510.         Sanderling.MouseDragAndDrop(oreContainerItem, DestinationContainer);
  511.     }
  512. }
  513. bool InitiateWarpToRandomMiningBookmark() =>
  514.     InitiateDockToOrWarpToLocationInSolarSystemMenu(RandomElement(SetMiningSiteBookmark));
  515.  
  516. bool InitiateWarpToMiningSite() =>
  517.     InitiateDockToOrWarpToLocationInSolarSystemMenu("asteroid belts", PickNextMiningSiteFromSystemMenu);
  518.  
  519. bool InitiateDockToOffload()    =>
  520.     InitiateDockToOrWarpToLocationInSolarSystemMenu(UnloadBookmark, PickPlaceToOffloadfFromSystemMenu);
  521.  
  522. bool InitiateWarpToRetreat()    =>
  523.     InitiateDockToOrWarpToLocationInSolarSystemMenu("stations");
  524.  
  525. MemoryStruct.IMenuEntry PickNextMiningSiteFromSystemMenu(IReadOnlyList<MemoryStruct.IMenuEntry> availableMenuEntries)
  526. {
  527.     Host.Log("I am seeing " + availableMenuEntries?.Count.ToString() + " mining sites to choose from.");
  528.  
  529.     var nextSite =
  530.         availableMenuEntries
  531.         ?.OrderBy(menuEntry => visitedLocations.ToList().IndexOf(menuEntry?.Text))
  532.         ?.FirstOrDefault();
  533.  
  534.     Host.Log("I pick '" + nextSite?.Text + "' as next mining site, based on the intent to rotate through the mining sites and recorded previous locations.");
  535.     return nextSite;
  536. }
  537.  
  538. MemoryStruct.IMenuEntry PickPlaceToOffloadfFromSystemMenu(IReadOnlyList<MemoryStruct.IMenuEntry> availableMenuEntries)
  539. {
  540.     Host.Log("I am seeing " + availableMenuEntries?.Count.ToString() + " stations to choose from for offloading ore.");
  541.  
  542.     var availableMenuEntriesTexts =
  543.         availableMenuEntries
  544.         ?.Select(menuEntry => menuEntry.Text)
  545.         ?.ToList();
  546.  
  547.     foreach(var visitedLocation in visitedLocations.Reverse())
  548.     {
  549.         var visitedLocationMenuEntry =
  550.             availableMenuEntries?.FirstOrDefault(menuEntry =>
  551.                 StationFromSystemInfoPanelEqualsStationFromSystemMenu(visitedLocation, menuEntry?.Text));
  552.  
  553.         if(visitedLocationMenuEntry == null)
  554.             continue;
  555.  
  556.         Host.Log("I pick '" + visitedLocationMenuEntry?.Text + "' as destination, because this is the last one visited.");
  557.         return visitedLocationMenuEntry;
  558.     }
  559.  
  560.     var dockMenuEntry = availableMenuEntries?.Where(x => x?.Text?.Contains("Dock") ?? false)?.FirstOrDefault();
  561.     var destination = dockMenuEntry ?? availableMenuEntries?.FirstOrDefault();
  562.  
  563.     Host.Log("I pick '" + destination?.Text + "' as destination, because I do not remember having visited any of the available stations.");
  564.     return destination;
  565. }
  566.  
  567. bool InitiateDockToOrWarpToLocationInSolarSystemMenu(
  568.     string submenuLabel,
  569.     Func<IReadOnlyList<MemoryStruct.IMenuEntry>, MemoryStruct.IMenuEntry> pickPreferredDestination = null)
  570. {  
  571.     Host.Log("Attempt to initiate dock to or warp to menu entry in submenu '" + submenuLabel + "'");
  572.    
  573.     var listSurroundingsButton = Measurement?.InfoPanelCurrentSystem?.ListSurroundingsButton;
  574.    
  575.     Sanderling.MouseClickRight(listSurroundingsButton);
  576.  
  577.     var submenuEntry = Measurement?.Menu?.FirstOrDefault()?.EntryFirstMatchingRegexPattern("^" + submenuLabel + "$", RegexOptions.IgnoreCase);
  578.  
  579.     if(null == submenuEntry)
  580.     {
  581.         Host.Log("Submenu '" + submenuLabel + "' not found in the solar system menu.");
  582.         return true;
  583.     }
  584.  
  585.     Sanderling.MouseClickLeft(submenuEntry);
  586.  
  587.     var submenu = Measurement?.Menu?.ElementAtOrDefault(1);
  588.  
  589.     var destinationMenuEntry = pickPreferredDestination?.Invoke(submenu?.Entry?.ToList()) ?? submenu?.Entry?.FirstOrDefault();
  590.  
  591.     if(destinationMenuEntry == null)
  592.     {
  593.         Host.Log("Failed to open submenu '" + submenuLabel + "' in the solar system menu.");
  594.         return true;
  595.     }
  596.  
  597.     Sanderling.MouseClickLeft(destinationMenuEntry);
  598.  
  599.     var actionsMenu = Measurement?.Menu?.ElementAtOrDefault(2);
  600.  
  601.     if(destinationMenuEntry == null)
  602.     {
  603.         Host.Log("Failed to open actions menu for '" + destinationMenuEntry.Text + "' in the solar system menu.");
  604.         return true;
  605.     }
  606.  
  607.     var dockMenuEntry = actionsMenu?.EntryFirstMatchingRegexPattern("dock", RegexOptions.IgnoreCase);
  608.     var warpMenuEntry = actionsMenu?.EntryFirstMatchingRegexPattern(@"warp.*within.*m", RegexOptions.IgnoreCase);
  609.     var approachEntry = actionsMenu?.EntryFirstMatchingRegexPattern(@"approach", RegexOptions.IgnoreCase);
  610.  
  611.     var maneuverMenuEntry = dockMenuEntry ?? warpMenuEntry;
  612.  
  613.     if (null != maneuverMenuEntry)
  614.     {
  615.         Host.Log("initiating '" + maneuverMenuEntry.Text + "' on '" + destinationMenuEntry?.Text + "'");
  616.         Sanderling.MouseClickLeft(maneuverMenuEntry);
  617.         return false;
  618.     }
  619.  
  620.     if (null != approachEntry)
  621.     {
  622.         Host.Log("found menu entry '" + approachEntry.Text + "'. Assuming we are already there.");
  623.         return false;
  624.     }
  625.  
  626.     Host.Log("no suitable menu entry found on '" + destinationMenuEntry?.Text + "'");
  627.     return true;
  628. }
  629.  
  630. IWindowDroneView    WindowDrones    =>
  631.     Measurement?.WindowDroneView?.FirstOrDefault();
  632.  
  633. DroneViewEntryGroup DronesInBayListEntry =>
  634.     WindowDrones?.ListView?.Entry?.OfType<DroneViewEntryGroup>()?.FirstOrDefault(Entry => null != Entry?.Caption?.Text?.RegexMatchIfSuccess(@"Drones in bay", RegexOptions.IgnoreCase));
  635.  
  636. DroneViewEntryGroup DronesInSpaceListEntry =>
  637.     WindowDrones?.ListView?.Entry?.OfType<DroneViewEntryGroup>()?.FirstOrDefault(Entry => null != Entry?.Caption?.Text?.RegexMatchIfSuccess(@"Drones in Local Space", RegexOptions.IgnoreCase));
  638.  
  639. int?    DronesInSpaceCount => DronesInSpaceListEntry?.Caption?.Text?.AsDroneLabel()?.Status?.TryParseInt();
  640.  
  641. void DroneEnsureInBay()
  642. {
  643.     if(0 == DronesInSpaceCount)
  644.         return;
  645.  
  646.     DroneReturnToBay();
  647.    
  648.     Host.Delay(4444);
  649. }
  650.  
  651. void DroneReturnToBay()
  652. {
  653.     Host.Log("return drones to bay.");
  654.     Sanderling.MouseClickRight(DronesInSpaceListEntry);
  655.     Sanderling.MouseClickLeft(Menu?.FirstOrDefault()?.EntryFirstMatchingRegexPattern("return.*bay", RegexOptions.IgnoreCase));
  656. }
  657.  
  658. void DroneLaunch()
  659. {
  660.     Host.Log("launch drones.");
  661.     Sanderling.MouseClickRight(DronesInBayListEntry);
  662.     Sanderling.MouseClickLeft(Menu?.FirstOrDefault()?.EntryFirstMatchingRegexPattern("launch", RegexOptions.IgnoreCase));
  663. }
  664.  
  665. void Undock()
  666. {
  667.     while(Measurement?.IsDocked ?? true)
  668.     {
  669.         Sanderling.MouseClickLeft(Measurement?.WindowStation?.FirstOrDefault()?.UndockButton);
  670.         Host.Log("waiting for undocking to complete.");
  671.         Host.Delay(8000);
  672.     }
  673.  
  674.     Host.Delay(4444);
  675.     Sanderling.InvalidateMeasurement();
  676. }
  677.  
  678. void ModuleMeasureAllTooltip()
  679. {
  680.     Host.Log("measure tooltips of all modules.");
  681.  
  682.     for (;;)
  683.     {
  684.         var NextModule = Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule?.FirstOrDefault(m => null == m?.TooltipLast);
  685.  
  686.         if(null == NextModule)
  687.             break;
  688.  
  689.         Host.Log("measure module.");
  690.         //  take multiple measurements of module tooltip to reduce risk to keep bad read tooltip.
  691.         Sanderling.MouseMove(NextModule);
  692.         Sanderling.WaitForMeasurement();
  693.         Sanderling.MouseMove(NextModule);
  694.     }
  695. }
  696.  
  697. void ActivateHardenerExecute()
  698. {
  699.     var SubsetModuleHardener =
  700.         Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule
  701.         ?.Where(module => module?.TooltipLast?.Value?.IsHardener ?? false);
  702.  
  703.     var SubsetModuleToToggle =
  704.         SubsetModuleHardener
  705.         ?.Where(module => !(module?.RampActive ?? false));
  706.  
  707.     foreach (var Module in SubsetModuleToToggle.EmptyIfNull())
  708.         ModuleToggle(Module);
  709. }
  710.  
  711. void ModuleToggle(Sanderling.Accumulation.IShipUiModule Module)
  712. {
  713.     var ToggleKey = Module?.TooltipLast?.Value?.ToggleKey;
  714.  
  715.     Host.Log("toggle module using " + (null == ToggleKey ? "mouse" : Module?.TooltipLast?.Value?.ToggleKeyTextLabel?.Text));
  716.  
  717.     if(null == ToggleKey)
  718.         Sanderling.MouseClickLeft(Module);
  719.     else
  720.         Sanderling.KeyboardPressCombined(ToggleKey);
  721. }
  722.  
  723. void EnsureOverviewTypeSelectionLoaded()
  724. {
  725.     if(null == OverviewPresetTabActive || null == WindowOverview || null == OverviewPreset)
  726.         return;
  727.  
  728.     if(string.Equals(OverviewTypeSelectionName, OverviewPreset, StringComparison.OrdinalIgnoreCase))
  729.         return;
  730.  
  731.     Host.Log("loading preset '" + OverviewPreset + "' to overview (current selection is '" + OverviewTypeSelectionName + "').");
  732.     Sanderling.MouseClickRight(OverviewPresetTabActive);
  733.     Sanderling.MouseClickLeft(Menu?.FirstOrDefault()?.EntryFirstMatchingRegexPattern("load.*preset", RegexOptions.IgnoreCase));
  734.     var PresetMenuEntry = Menu?.ElementAtOrDefault(1)?.EntryFirstMatchingRegexPattern(@"^\s*" + Regex.Escape(OverviewPreset) + @"\s*$", RegexOptions.IgnoreCase);
  735.  
  736.     if(null == PresetMenuEntry)
  737.     {
  738.         Host.Log("error: menu entry '" + OverviewPreset + "' not found");
  739.         return;
  740.     }
  741.  
  742.     Sanderling.MouseClickLeft(PresetMenuEntry);
  743. }
  744.  
  745. void DelayWhileUpdatingMemory(int delayAmountMilli)
  746. {
  747.     var beginTimeMilli = Host.GetTimeContinuousMilli();
  748.  
  749.     while(true)
  750.     {
  751.         var remainingTimeMilli = beginTimeMilli + delayAmountMilli - Host.GetTimeContinuousMilli();
  752.         if(remainingTimeMilli < 0)
  753.             break;
  754.  
  755.         Host.Delay(Math.Min(1000, (int)remainingTimeMilli));
  756.         Sanderling.InvalidateMeasurement();
  757.         MemoryUpdate();
  758.     }
  759. }
  760.  
  761. void MemoryUpdate()
  762. {
  763.     RetreatUpdate();
  764.     JammedLastTimeUpdate();
  765.     OffloadCountUpdate();
  766.     UpdateLocationRecord();
  767.     TrackTotalMinedOre();
  768. }
  769.  
  770. void UpdateLocationRecord()
  771. {
  772.     //  I am not interested in locations which are only close during warp.
  773.     if(Measurement?.ShipUi?.Indication?.ManeuverType == ShipManeuverTypeEnum.Warp)
  774.         return;
  775.  
  776.     // Purpose of recording locations is to prioritize our next destination when warping to mining site or docking to station.
  777.     // For this purpose, I will compare the recorded locations with the menu entries in the system menu.
  778.     // Therefore I want the format of the recorded location to be the same as it appears in the menu entries in the system menu.
  779.  
  780.     var currentSystemLocationLabelText =
  781.         Measurement?.InfoPanelCurrentSystem?.ExpandedContent?.LabelText
  782.         ?.OrderByCenterVerticalDown()?.FirstOrDefault()?.Text;
  783.  
  784.     if(currentSystemLocationLabelText == null)
  785.         return;
  786.  
  787.     // 2018-03 observed label text: <url=showinfo:15//40088644 alt='Nearest'>Amsen V - Asteroid Belt 1</url>
  788.  
  789.     var currentLocationName = RegexExtension.RemoveXmlTag(currentSystemLocationLabelText)?.Trim();
  790.  
  791.     var lastRecordedLocation = visitedLocations.LastOrDefault();
  792.  
  793.     if(lastRecordedLocation == currentLocationName)
  794.         return;
  795.  
  796.     visitedLocations.Enqueue(currentLocationName);
  797.     Host.Log("Recorded transition from location '" + lastRecordedLocation + "' to location '" + currentLocationName + "'");
  798.  
  799.     if(100 < visitedLocations.Count)
  800.         visitedLocations.Dequeue();
  801. }
  802.  
  803. /*
  804. 2018-03 Observation: Station name containing reference to moon appears different between system menu and system info panel.
  805. In the info panel 'Moon 17' was used while in the system menu it was 'M17'.
  806. */
  807. public bool StationFromSystemInfoPanelEqualsStationFromSystemMenu(
  808.     string stationNameInCurrentSystemInfoPanel,
  809.     string stationNameInSystemMenu)
  810. {
  811.     //  Copied from https://github.com/botengine-de/Optimat.EO/blob/6c19e8f36e30d5468e94d627eb16dcb78bb47d12/src/Optimat.EveOnline.Bot/Sonst/AgentUndMission.Aktualisiire.cs#L1888-L1893
  812.  
  813.     var representationPattern =
  814.         Regex.Replace(
  815.         stationNameInCurrentSystemInfoPanel,
  816.         "Moon\\s*",
  817.         "M([\\w]*\\s*)",
  818.         RegexOptions.IgnoreCase);
  819.  
  820.     return
  821.         stationNameInSystemMenu.RegexMatchSuccessIgnoreCase(representationPattern);
  822. }
  823.  
  824. void JammedLastTimeUpdate()
  825. {
  826.     if(Jammed)
  827.         JammedLastTime  = Host.GetTimeContinuousMilli();
  828. }
  829.  
  830. bool MeasurementEmergencyWarpOutEnter =>
  831.     !(Measurement?.IsDocked ?? false) && !(EmergencyWarpOutHitpointPercent < ShieldHpPercent);
  832.  
  833. void RetreatUpdate()
  834. {
  835.     RetreatReasonTemporary = (RetreatOnNeutralOrHostileInLocal && hostileOrNeutralsInLocal) ? "hostile or neutral in local" : null;
  836.  
  837.     if (!MeasurementEmergencyWarpOutEnter)
  838.         return;
  839.  
  840.     //  measure multiple times to avoid being scared off by noise from a single measurement.
  841.     Sanderling.InvalidateMeasurement();
  842.  
  843.     if (!MeasurementEmergencyWarpOutEnter)
  844.         return;
  845.  
  846.     RetreatReasonPermanent = "shield hp";
  847. }
  848.  
  849. void OffloadCountUpdate()
  850. {
  851.     var OreContainerFillPercentSynced   = OreContainerFillPercent;
  852.  
  853.     if(!OreContainerFillPercentSynced.HasValue)
  854.         return;
  855.  
  856.     if(0 == OreContainerFillPercentSynced && OreContainerFillPercentSynced < LastCheckOreContainerFillPercent)
  857.         ++OffloadCount;
  858.  
  859.     LastCheckOreContainerFillPercent = OreContainerFillPercentSynced;
  860. }
  861.  
  862. bool IsNeutralOrEnemy(IChatParticipantEntry participantEntry) =>
  863.    !(participantEntry?.FlagIcon?.Any(flagIcon =>
  864.      new[] { "good standing", "excellent standing", "Pilot is in your (fleet|corporation)", }
  865.      .Any(goodStandingText =>
  866.         flagIcon?.HintText?.RegexMatchSuccessIgnoreCase(goodStandingText) ?? false)) ?? false);
  867.  
  868. bool? IsExpanded(IInventoryTreeViewEntryShip shipEntryInInventory) =>
  869.     shipEntryInInventory == null ? null :
  870.     (bool?)((shipEntryInInventory.IsExpanded ?? false) || 0 < (shipEntryInInventory.Child?.Count() ?? 0));
  871.  
  872. void TrackTotalMinedOre()
  873. {
  874.     var currentTimeMilli = Sanderling.MemoryMeasurement?.End;
  875.  
  876.     // Host.Log("TrackTotalMinedOre called at " + currentTimeMilli);
  877.  
  878.     if(!currentTimeMilli.HasValue)
  879.         return;
  880.  
  881.     if(Sanderling.MemoryMeasurementParsed?.Value?.IsDocked ?? false)
  882.         return;
  883.  
  884.     if(SetModuleMiner?.Any(module => module?.RampActive ?? false) ?? false)
  885.         miningModuleLastActiveTime = currentTimeMilli.Value;
  886.  
  887.     var containerCurrentUsedVolumeMilli = OreContainerCapacityMilli?.Used;
  888.  
  889.     if(!containerCurrentUsedVolumeMilli.HasValue)
  890.         return;
  891.  
  892.     var lastRecordingAgeMilli = currentTimeMilli - efficiencyRawMeasurements.LastOrDefault().timeMilli;
  893.  
  894.     if(lastRecordingAgeMilli < 500)
  895.         return;
  896.  
  897.     efficiencyRawMeasurements.Enqueue(
  898.         new StatisticsMeasurementSnapshot{ timeMilli = currentTimeMilli.Value, volumeMilli = containerCurrentUsedVolumeMilli.Value });
  899.  
  900.     while(2 < efficiencyRawMeasurements.Count)
  901.         efficiencyRawMeasurements.Dequeue();
  902.  
  903.     if(efficiencyRawMeasurements.Select(measurement => measurement.volumeMilli).Distinct().Count() == 1)
  904.         efficiencyDebouncedMeasurements.Enqueue(efficiencyRawMeasurements.Last());
  905.  
  906.     while(2 < efficiencyDebouncedMeasurements.Count)
  907.         efficiencyDebouncedMeasurements.Dequeue();
  908.  
  909.     if(1 < efficiencyDebouncedMeasurements.Count)
  910.     {
  911.         var lastMeasurement = efficiencyDebouncedMeasurements.Last();
  912.         var secondLastMeasurement = efficiencyDebouncedMeasurements.Reverse().ElementAt(1);
  913.  
  914.         var usedVolumeIncreaseMilli = lastMeasurement.volumeMilli - secondLastMeasurement.volumeMilli;
  915.         var secondLastMeasurementAgeMilli = currentTimeMilli - secondLastMeasurement.timeMilli;
  916.  
  917.         if(0 < usedVolumeIncreaseMilli)
  918.         {
  919.             Host.Log("Inventory used volume increased by " + (usedVolumeIncreaseMilli / 1000) + " m³");
  920.  
  921.             var miningModuleActiveLastAge = (currentTimeMilli.Value - miningModuleLastActiveTime) / 1000;
  922.  
  923.             if(10 * 1000 < miningModuleActiveLastAge)
  924.                 Host.Log("Measuring Statistics: Ignored volume increase because miningModuleActiveLastAge was " + miningModuleActiveLastAge + " s.");
  925.             else
  926.             {
  927.                 if(10 * 1000 < secondLastMeasurementAgeMilli)
  928.                     Host.Log("Measuring Statistics: Ignored volume increase because timespan was " + secondLastMeasurementAgeMilli + " ms.");
  929.                 else
  930.                 {
  931.                     totalMinedOreVolumeMilli += usedVolumeIncreaseMilli;
  932.                     Host.Log("Total_mined_ore_in_cubic_meters: " + (totalMinedOreVolumeMilli / 1000));
  933.                 }
  934.             }
  935.         }
  936.     }
  937. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement