Advertisement
Guest User

Sanderling mining script v2018-05-05

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