Terpla adventures or blog-style guide for begginers

Hi guys!
I decided to test the new version of Sanderling, and at the same time share experience with beginners. My English is not so good, but the number of recurring questions is growing and no one dares to create a guide.

So, what is Sanderling? This is a platform that allows you to write scripts / programs to automate actions in the eve online game. It allows you to read the game information from the computer’s memory and make decisions based on the algorithm: press the keys or click the mouse on the specified coordinates or objects of the game window.

image

Attention!!! Sanderling is intended for informational purposes and does not aim to violate the rules of the game, but CCP can think differently and classify this program as a bot.

So your account can be banned, keep this in mind!!!

Sanderling is Shareware: you can use it for free 2 hours, after that you need some credits, which can be bought for real money. 16 credits per minute, 300 000 credit cost 4,3 euro. 72 hours for one euro, if I did not get it wrong. The old versions of the program were free (until 2018) and you can adapt them to the current changes. But in this case you will have to do this adaptation allmost every new big patch from CCP.

Sanderling has several pre-installed scripts for various activities: mining, autopilot, anomaly ratting. They are not ideal and require your presence, but allow you to automate most of the actions. And most remarkable: we can edit these scripts ourselves and bring them to perfection.

I decided to try it from the beginning, so I created an alpha account (referal link not mine, but give me 250k SP).
Gallente is my choice (I like drones). I went through training to get the initial money, books, modules and ships.

Ok, now we need last version of Sanderling (v2018-05-05).

image

Let’s start with an autopilot (read “Setting Up The Bot” section). After the initial tutorial I decided to try the trucking. For this, agents of the distribution type are well suited.
I used part of free SP for social skill: Connections, Diplomacy, Social and completed several missions of the first level agent using the shuttle. After that, a Level 2 agent became available and I bought a Magnate with 4 cargoholds.

Let’s find out how works our autopilot script.

image

To work and modify the script, you must know the basics of working with the C# programming language: variables, conditional statements, loops, LINQ, etc.

Default autopilot programm
while(true)
{
	var Measurement = Sanderling?.MemoryMeasurementParsed?.Value;

	var ManeuverType = Measurement?.ShipUi?.Indication?.ManeuverType;

	if(ShipManeuverTypeEnum.Warp == ManeuverType	||
		ShipManeuverTypeEnum.Jump == ManeuverType)
		goto loop;	//	do nothing while warping or jumping.

	//	from the set of route element markers in the Info Panel pick the one that represents the next Waypoint/System.
	//	We assume this is the one which is nearest to the topleft corner of the Screen which is at (0,0)
	var RouteElementMarkerNext =
		Measurement?.InfoPanelRoute?.RouteElementMarker
		?.OrderByCenterDistanceToPoint(new Vektor2DInt(0, 0))?.FirstOrDefault();

	if(null == RouteElementMarkerNext)
	{
		Host.Log("no route found in info panel.");
		goto loop;
	}
	
	//	rightclick the marker to open the contextmenu.
	Sanderling.MouseClickRight(RouteElementMarkerNext);

	//	retrieve a new measurement.
	Measurement = Sanderling?.MemoryMeasurementParsed?.Value;

	//	from the first menu, pick the first entry that contains "dock" or "jump".
	var MenuEntry =
		Measurement?.Menu?.FirstOrDefault()
		?.Entry?.FirstOrDefault(candidate => candidate.Text.RegexMatchSuccessIgnoreCase("dock|jump"));
	
	if(null == MenuEntry)
	{
		Host.Log("no suitable menu entry found.");
		goto loop;
	}

	Host.Log("menu entry found. clicking to initiate warp.");
	Sanderling.MouseClickLeft(MenuEntry);

loop:
	//	wait for four seconds before repeating.
	Host.Delay(4000);
	//	make sure new measurement will be taken.
	Sanderling.InvalidateMeasurement();
}
4 Likes

Every few seconds, the Sanderling reads the memory and enters the values into variables:

  • MemoryMeasurement - raw memory data
  • MemoryMeasurementParsed - converted data for easier use
  • MemoryMeasurementAccu - accumulates data that will not change in the future (for example, a set of ship modules)

image

while(true) 	//	this code will be executed infinitely again and again
{
	//	We write to the variable the result of reading the memory for a shorter path.
	var Measurement = Sanderling?.MemoryMeasurementParsed?.Value;

	//	Every window in eve represent as object in MemoryMeasurementParsed?.Value 
	//	(see the screenshot below)
	//	So, ShipUI contains information about what our ship doing now: 
	//	warp, jump trought gate, docked, orbiting, etc.
	var ManeuverType = Measurement?.ShipUi?.Indication?.ManeuverType;

	if(ShipManeuverTypeEnum.Warp == ManeuverType	||
		ShipManeuverTypeEnum.Jump == ManeuverType)
		goto loop;	//	do nothing while warping or jumping.

	//	Let's skip some code


loop:
	//	wait for four seconds before repeating.
	Host.Delay(4000);
	//	If you do not want to wait for the next memory reading, you can force this.
	Sanderling.InvalidateMeasurement();
}
Some windows from MemoryMeasurementParsed?.Value:

image

So if we warp or jump through the gate this code wait 4 seconds and start again. Now i want add improvement: some indication about the completion of the trip. So let’s add some sound if our ship is docked.

while(true) 	
{
	var Measurement = Sanderling?.MemoryMeasurementParsed?.Value;	
	var ManeuverType = Measurement?.ShipUi?.Indication?.ManeuverType;
	
	if(Measurement.IsDocked ?? true)  //	if Measurement.IsDocked == true or null then
	{	
		Console.Beep(500, 200); //	play sound
		Host.Delay(6000);		//	wait 6 sec
		goto loop;
	}

	if(ShipManeuverTypeEnum.Warp == ManeuverType	||
		ShipManeuverTypeEnum.Jump == ManeuverType)
		goto loop;	

loop:
	Host.Delay(4000);	
	Sanderling.InvalidateMeasurement();
}

Why we use

if(Measurement.IsDocked ?? true)

instead of

if(ShipManeuverTypeEnum.Docked == ManeuverType)

for example?
Because our ManeuverType is obtained from the Measurement?.ShipUi, but when we are docked this part of the interface is not available and ShipUi == null.

Also you can use

		System.Media.SoundPlayer simpleSound = new System.Media.SoundPlayer(@"path to your Sound.wav");
	 	simpleSound.Play();

instead of Console.Beep.


Ok, Let’s do more improvements. Sometimes script can missclick and close InfoPanelRoute. In this case our RouteElementMarker == null and autopilot will stuck with “no route found in info panel” message:

	//	from the set of route element markers in the Info Panel pick the one that represents the next Waypoint/System.
	//	We assume this is the one which is nearest to the topleft corner of the Screen which is at (0,0)
	var RouteElementMarkerNext =
		Measurement?.InfoPanelRoute?.RouteElementMarker
		?.OrderByCenterDistanceToPoint(new Vektor2DInt(0, 0))?.FirstOrDefault();

	if(null == RouteElementMarkerNext)
	{
		Host.Log("no route found in info panel.");
		goto loop;
	}

image

In this case, we can try clicking on the ExpandToggleButton:

	//	from the set of route element markers in the Info Panel pick the one that represents the next Waypoint/System.
	//	We assume this is the one which is nearest to the topleft corner of the Screen which is at (0,0)
	var RouteElementMarkerNext =
		Measurement?.InfoPanelRoute?.RouteElementMarker
		?.OrderByCenterDistanceToPoint(new Vektor2DInt(0, 0))?.FirstOrDefault();

	if(null == RouteElementMarkerNext)
	{
		Host.Log("no route found in info panel.");
		//	if we found our button and it's not null
		var toogleButton = Measurement?.InfoPanelRoute?.ExpandToggleButton;
		if(toogleButton !=null) Sanderling.MouseClickLeft(toogleButton); //	click him
		goto loop;
	}

image

Well done.

2 Likes

Also, sometimes our script have other missclick and open solar info instead of Jump throught stargate:

image

This window will be known as WindowOther:

image

To close this window, we must hover the mouse cursor. Then there will be 3 buttons in header. We need click to Close button.

image

Let’s write a function to close this window.

public void CloseWindowOther()
{
	Host.Log("close WindowOther");	
	var windowOther = Sanderling?.MemoryMeasurementParsed?.Value?.WindowOther?.FirstOrDefault();
	
	//	if close button not visible then move mouse to the our window
	if(!windowOther.HeaderButtonsVisible ?? false) 
		Sanderling.MouseMove(windowOther.LabelText.FirstOrDefault());
		
	Sanderling.InvalidateMeasurement();	//	make sure we have new measurement
	if(windowOther.HeaderButton != null)
	{
		//	we have 3 buttons and looking with HintText "Close"
		var closeButton = windowOther.HeaderButton?.FirstOrDefault(x => x.HintText == "Close");
		if(closeButton !=null)
			Sanderling.MouseClickLeft(closeButton);			
	}	
}

I think enough for the first time. Save the resulting script:

image

Full code for Terpla autopilot script
/* Autopilot improved by Terpla. Version TPAutopilot-2018-05-31v1
Features:
-plays sound if ship docked
-toggles InfoPanelRoute if no route found
-closes system info window if it was opened by misstake
*/

/*
This is a warp to 0km auto-pilot, making your travels faster and thus safer by directly warping to gates/stations.

The bot follows the route set in the in-game autopilot and uses the context menu to initiate warp and dock commands.

To use the bot, set the in-game autopilot route before starting the bot.
Make sure you are undocked before starting the bot because the bot does not undock.
*/

while(true)
{
	var Measurement = Sanderling?.MemoryMeasurementParsed?.Value;

	var ManeuverType = Measurement?.ShipUi?.Indication?.ManeuverType;
	
	if(Measurement.IsDocked ?? true)
	{	
		//	play sound if we docked
		Console.Beep(500, 200);
		Host.Delay(6000);
		goto loop;
	}
	
	if(Measurement.WindowOther != null) CloseWindowOther();

	if(ShipManeuverTypeEnum.Warp == ManeuverType	||
		ShipManeuverTypeEnum.Jump == ManeuverType)		
			goto loop;	//	do nothing while warping or jumping.
		

	//	from the set of route element markers in the Info Panel pick the one that represents the next Waypoint/System.
	//	We assume this is the one which is nearest to the topleft corner of the Screen which is at (0,0)
	var RouteElementMarkerNext =
		Measurement?.InfoPanelRoute?.RouteElementMarker
		?.OrderByCenterDistanceToPoint(new Vektor2DInt(0, 0))?.FirstOrDefault();

	if(null == RouteElementMarkerNext)
	{
		Host.Log("no route found in info panel.");
		//	Let's try toggle expand button
		var toogleButton = Measurement?.InfoPanelRoute?.ExpandToggleButton;
		if(toogleButton !=null) Sanderling.MouseClickLeft(toogleButton);
		goto loop;
	}
	
	//	rightclick the marker to open the contextmenu.
	Sanderling.MouseClickRight(RouteElementMarkerNext);

	//	retrieve a new measurement.
	Measurement = Sanderling?.MemoryMeasurementParsed?.Value;

	//	from the first menu, pick the first entry that contains "dock" or "jump".
	var MenuEntry =
		Measurement?.Menu?.FirstOrDefault()
		?.Entry?.FirstOrDefault(candidate => candidate.Text.RegexMatchSuccessIgnoreCase("dock|jump"));
	
	if(null == MenuEntry)
	{
		Host.Log("no suitable menu entry found.");
		goto loop;
	}

	Host.Log("menu entry found. clicking to initiate warp.");
	Sanderling.MouseClickLeft(MenuEntry);

loop:
	//	wait for four seconds before repeating.
	Host.Delay(4000);
	//	make sure new measurement will be taken.
	Sanderling.InvalidateMeasurement();
}

public void CloseWindowOther()
{
	Host.Log("close WindowOther");	
	var windowOther = Sanderling?.MemoryMeasurementParsed?.Value?.WindowOther?.FirstOrDefault();
	
	//	if close button not visible then move mouse to the our window
	if(!windowOther.HeaderButtonsVisible ?? false) 
		Sanderling.MouseMove(windowOther.LabelText.FirstOrDefault());
		
	Sanderling.InvalidateMeasurement();	//	make sure we have new measurement
	if(windowOther.HeaderButton != null)
	{
		//	we have 3 buttons and looking with HintText "Close"
		var closeButton = windowOther.HeaderButton?.FirstOrDefault(x => x.HintText == "Close");
		if(closeButton !=null)
			Sanderling.MouseClickLeft(closeButton);			
	}	
}

I do not think that you can earn a lot of money on transportation missions, but this autopilot is useful if you are driving a large ship for a long distance. We also learned the basics for writing improvements to default scripts.

Also i tested this script with old version of Sanderling (v17.11.19) and it works! So, you can use this autopilot for free.

5 Likes

Should I continue to write in this style?

  • Yes
  • No

0 voters


Hello, capsuleers!
Today we will play miners. My alpha accaount need some isks for buying a ship and modules. So I built a simple venture:

[Venture, ...]
Mining Laser Upgrade I

Small Shield Booster II
Cap Recharger II
Medium Shield Extender I

Miner II
Miner II

Hornet I x2

Fit can be different, the main thing is T2 lasers (Miner II) and Mining Laser Upgrade I (you can use T2 version). And also skills for mining and venture.

image

In the latest version of the Sanderling, you can find such a code for simple mining.
Version in release v2018-05-05.

image

I tested default script and noticed a few limitations.
Firstly, I would like to jump on the bookmarks - this will save time. Let’s add a few bookmarks in the asteroids and add them to the code.
In the configuration section, add the variable bool ShouldUseMiningBookmarks. Also add an array of bookmarks.

//  Should the bot use bookmarks for mining site?
bool ShouldUseMiningBookmarks = true;

//	The bot uses the bookmarks from the menu which is opened from the button in the 'System info' panel.
//	Bookmarks of places to mine. Add additional bookmarks separated by comma.
string[] SetMiningSiteBookmark = new[] {
	"bookmark1",
	"bookmark2",
	"bookmark3",
	"bookmark4",
	"bookmark5",
	"bookmark6",
	"bookmark7",
	"bookmark8",
	"bookmark9",
	};

Now let’s find the code responsible for selecting the bookmark for the warp to.
In the function MainStep() there is the following conditional operator:

//  if we dont have any asteroids or ShouldSwitchMiningSite
if(!(0 < ListAsteroidOverviewEntry?.Length) || ShouldSwitchMiningSite)
			InitiateWarpToMiningSite();

//  part of the code is removed for clarity

//  open menu, select "asteroid belts" and warp to it
bool InitiateWarpToMiningSite() =>
			InitiateDockToOrWarpToLocationInSolarSystemMenu("asteroid belts", PickNextMiningSiteFromSystemMenu);

Let’s change conditions in MainStep() :

		if(!(ListAsteroidOverviewEntry?.Length > 0) || ShouldSwitchMiningSite)
		{
			if(ShouldUseMiningBookmarks)
				InitiateWarpToRandomMiningBookmark();
			else
				InitiateWarpToMiningSite();
		}

and add new function for random boorkmark from SetMiningSiteBookmark array:

bool InitiateWarpToRandomMiningBookmark() =>
	InitiateDockToOrWarpToLocationInSolarSystemMenu(RandomElement(SetMiningSiteBookmark));

Now it is enough to change the variable ShouldUseMiningBookmarks to the true in the configuration section for using bookmarks.


Secondly, in the default script, we unload the ore to the last visited station. Sometimes I run a bot right in space or from another station. Therefore, I would like to unload ore in one particular station.
Save this station in the Eve as ‘HomeBookmark’ bookmark and add a new variable in the configuration section.

//	Bookmark of location where ore should be unloaded.
string UnloadBookmark = "HomeBookmark";

Now let’s fix this part of the code:

bool InitiateDockToOffload() =>
     InitiateDockToOrWarpToLocationInSolarSystemMenu(UnloadBookmark, PickPlaceToOffloadfFromSystemMenu);

After testing, I found that the script does not click on the “Dock” but click “Warp to Location Within 0 m”! What is the problem?
After a little debugging, I found it. Let’s fix this:

	//  we looking "Dock" menu
	var dockMenuEntry = availableMenuEntries?.Where(x => x?.Text?.Contains("Dock") ?? false)?.FirstOrDefault();
	var	destination = dockMenuEntry ?? availableMenuEntries?.FirstOrDefault();
2 Likes

Thirdly, I do not want to run from rats - it’s a waste of time. In earlier versions, there was an algorithm for killing rats. We use it partially.

In the ship I have 2 drones and a ShieldBooster. We will configure the drones for the aggressive mode and we will release them on arrival to asteroid belt. Before the warp it is necessary to scope the drones to the drone bay.

image

Let’s write the functions for working with drones:

//  our window with drones
IWindowDroneView	WindowDrones	=>
	Measurement?.WindowDroneView?.FirstOrDefault();

//  drones in bay
DroneViewEntryGroup DronesInBayListEntry =>
	WindowDrones?.ListView?.Entry?.OfType<DroneViewEntryGroup>()?.FirstOrDefault(Entry => null != Entry?.Caption?.Text?.RegexMatchIfSuccess(@"Drones in bay", RegexOptions.IgnoreCase));

// drones in space
DroneViewEntryGroup DronesInSpaceListEntry =>
	WindowDrones?.ListView?.Entry?.OfType<DroneViewEntryGroup>()?.FirstOrDefault(Entry => null != Entry?.Caption?.Text?.RegexMatchIfSuccess(@"Drones in Local Space", RegexOptions.IgnoreCase));

int?	DronesInSpaceCount => DronesInSpaceListEntry?.Caption?.Text?.AsDroneLabel()?.Status?.TryParseInt();
int?	DronesInBayListEntryCount => DronesInBayListEntry?.Caption?.Text?.AsDroneLabel()?.Status?.TryParseInt();

// check our drones and scope them
void DroneEnsureInBay()
{
	if(0 == DronesInSpaceCount)
		return;

	DroneReturnToBay();
	
	Host.Delay(4444);
}

//  scope our drones 
void DroneReturnToBay()
{
	Host.Log("return drones to bay.");
	Sanderling.MouseClickRight(DronesInSpaceListEntry);
	Sanderling.MouseClickLeft(Menu?.FirstOrDefault()?.EntryFirstMatchingRegexPattern("return.*bay", RegexOptions.IgnoreCase));
}

// launch our drones
void DroneLaunch()
{
	Host.Log("launch drones.");
	Sanderling.MouseClickRight(DronesInBayListEntry);
	Sanderling.MouseClickLeft(Menu?.FirstOrDefault()?.EntryFirstMatchingRegexPattern("launch", RegexOptions.IgnoreCase));
}

Now we need to make sure that the drones are scoped before our ship warp to anywhere.

Func<object>	MainStep()
{
	// ...

	if(ReadyForManeuver)
	{	
		DroneEnsureInBay();
		
		//  ...
	}
}

In belt we should launch our drones and activate our shield booster if the rats attacked us.

Func<object> InBeltMineStep()
{if(ShouldSwitchMiningSite)
		return MainStep;

	EnsureWindowInventoryOreContainerIsOpen();

	EnsureOverviewTypeSelectionLoaded();

	if(OreContainerFilledForOffload)
		return null;
		
	if (!(ReadyForManeuver))
	{
		DelayWhileUpdatingMemory(6000);
		return InBeltMineStep;	
	}
		
	if (!(DronesInSpaceCount > 0) && (DronesInBayListEntryCount > 0))
		DroneLaunch();

	bool ShouldTurnOnShieldBooster = ShieldHpPercent <90;
	ToggleShieldBooster(ShouldTurnOnShieldBooster);

        //  the rest of the code is unchanged...

	return InBeltMineStep;
}

Now we’ll write a function to turn on / off the shield booster.

// get all shield boosters in one array
Sanderling.Accumulation.IShipUiModule[] SetModuleShieldBoosters =>
	Sanderling.MemoryMeasurementAccu?.Value?.ShipUiModule?.Where(module => module?.TooltipLast?.Value?.IsShieldBooster ?? false)?.ToArray();

// if true then we should turn on shield booster if false - turn off
void ToggleShieldBooster(bool turnOn = true)
{
	var shieldBooster = SetModuleShieldBoosters?.FirstOrDefault();
	if((shieldBooster?.RampActive ?? false) !=turnOn)
	{		
		Host.Log("toggle Shield Booster.");
		ModuleToggle(shieldBooster);
	}
}

Now we will collect all the changes in one script and test it in practice.
PS: I’m testing on the old version of Sanderling ((v17.11.19 - it’s free), so local chat checking is disabled.

3 Likes

reserved for future posts
reserved for future posts

reserved for future posts
reserved for future posts
reserved for future posts

reserved for future posts
reserved for future posts
reserved for future posts
reserved for future posts

reserved for future posts
reserved for future posts
reserved for future posts
reserved for future posts
reserved for future posts

can you put that adapted to abot, pls?
i try to travel with abot, but

yield return RouteElementMarkerNext.ClickMenuEntryByRegexPattern(bot, "Jump through stargate");

doesn’t make the left click on jump.
thank you :slight_smile:

Later:

Im dumbest, i found a method is not clean, but is working:

					{
						var MenuEntry =
	memoryMeasurement?.Menu?.FirstOrDefault()
	?.Entry?.FirstOrDefault(candidate => candidate.Text.RegexMatchSuccessIgnoreCase("dock|Jump through stargate"));
						if (null != MenuEntry)
							yield return new BotTask { Effects = new[] { MenuEntry.MouseClick(BotEngine.Motor.MouseButtonIdEnum.Left) } };

						yield return new BotTask { Effects = new[] { RouteElementMarkerNext.MouseClick(BotEngine.Motor.MouseButtonIdEnum.Right) } };

		}
1 Like

Hey Terpla, looks like you have discovered a new bot! Thank you for sharing this with us!
I added a link to this bot to the bot catalog: https://forum.botlab.org/t/bot-65b07837-mining-script-improved-by-terpla-version-tpminingscript-2018-06-21v2/1070

I try to make abot to take the asteroids from infopannel…

if I change retreatbookmark to "asteroid " then he take first menu.

yield return new MenuPathTask
{
RootUIElement = memoryMeasurement?.InfoPanelCurrentSystem?.ListSurroundingsButton,
Bot = Bot,
ListMenuListPriorityEntryRegexPattern = new[] { new[] { retreatBookmark }, new[] { @"dock", ParseStatic.MenuEntryWarpToAtLeafRegexPattern } },
};

but if I put twice;

			ListMenuListPriorityEntryRegexPattern = new[] { new[] { "asteroid" },  new[] { MiningFolderBookmark }, new[] { @"0", ParseStatic.MenuEntryWarpToAtLeafRegexPattern } },

he take the next level on submenu, but he didnt take the randomElement ( he take always first asteroid)

	string[] SetMiningFolderBookmark = { "asteroid" };
				Random rand = new Random();
				int IntDefault(int? entier) => entier ?? 0;
				T RandomElement<T>(IEnumerable<T> seq)
				{ var counted = seq?.ToArray(); if (!(0 < counted?.Length))
						return default(T);
					return counted[rand.Next(counted.Length)];
				}
				String MiningFolderBookmark = RandomElement(SetMiningFolderBookmark);

Also I tried like in beginers mining script ore, but honestly, is more than my skills in programming to do what i want.( to take the asteroid belts for ratting like a second source of rats in same system ). Can you help me?

I think this is normal behavior for this code.

string[] SetMiningFolderBookmark = { "asteroid" };

In this string you create an array of one element. Random of one element is always this element …

In my mining script i have many bookmarks in array, then the random makes sense

string[] SetMiningSiteBookmark = new[] {
    "mine1",
    "mine2",
    "mine3",
    "mine4",
    };

bool InitiateWarpToRandomMiningBookmark() =>
    InitiateDockToOrWarpToLocationInSolarSystemMenu(RandomElement(SetMiningSiteBookmark));

Random selects one of the bookmarks and sends it to the InitiateDockToOrWarpToLocationInSolarSystemMenu function.

If you look at how the code of the default script works, you can see another algorithm:

bool InitiateWarpToMiningSite() =>
    InitiateDockToOrWarpToLocationInSolarSystemMenu("asteroid belts", PickNextMiningSiteFromSystemMenu);

We send “asteroid belts” as submenuLabel and a function that selects the “random” asteroid belt.

bool InitiateDockToOrWarpToLocationInSolarSystemMenu(
    string submenuLabel,
    Func<IReadOnlyList<MemoryStruct.IMenuEntry>, MemoryStruct.IMenuEntry> pickPreferredDestination = null)
{      

    var listSurroundingsButton = Measurement?.InfoPanelCurrentSystem?.ListSurroundingsButton;   // looking our first button 
    Sanderling.MouseClickRight(listSurroundingsButton); // and click it

    var submenuEntry = Measurement?.Menu?.FirstOrDefault()?.EntryFirstMatchingRegexPattern("^" + submenuLabel + "$", RegexOptions.IgnoreCase); // looking our "asteroid belts" button
    Sanderling.MouseClickLeft(submenuEntry);      // and click it

    var submenu = Measurement?.Menu?.ElementAtOrDefault(1); // looking array of asteroids (see picture)
    var destinationMenuEntry = pickPreferredDestination?.Invoke(submenu?.Entry?.ToList()) ?? submenu?.Entry?.FirstOrDefault(); // we call the function and pass it an array of asteroids as a parameter

      // ...............
 }

image

In the default script, this function is called PickNextMiningSiteFromSystemMenu. You can see how it works (selects the asteroids visited), but with the same success we could call RandomElement function.

In the A-Bot program, the MenuPathTask task is used in several places and if we change it using random, then in this places there will also be random values.
Therefore, you must first get an array of asteroids (like Menu?.ElementAtOrDefault(1) in Sanderling), select a random one and use it in the MenuPathTask task.

This is what comes to mind without thinking. But it’s late night and I’ll go to bed.
Think, try different options, ask questions if you come to a standstill. If everything is bad, then we’ll figure something out.

1 Like

dont hurry and be care with your health :slight_smile: take your time.
so i already reverse engineering the code for picking the asteroids from asteroid belts folder ( Menu?.ElementAtOrDefault(1) in Sanderling) before crying for help :))
Its a pain in the ass with the abot, btw, for one with my lvl.
The result is always the same: “not best type found for implicitly-typed array” for random and for menu?element AtOrddefault.
I believe the problem come from the menupathtask, because he didnt read if its a folder and inside is a new array of “asteroids”. so he read first menu (0). In the second script ( the one with random) is the same.
Ofcourse, I can put all asteroids in the menu (0) but… I have 36 asteroid belts and 6 chars who play in the same system. sometime I do rats ( but i have 5 anomaly at max) but mining make the rats come to asteroids, so is a good source :slight_smile:
I will keep testing, maybe I’ll find a way

I did not investigate the structure of the Menu itself, it may be necessary to work with some other field (Entry or something else). It is necessary to watch the Measurement in debug mode.

tomorow i will try to put the regular one (warp from folder asteroid belts) in place, i will update my results

One step at time :))
from the last time, i tried and tried …many times, until I isolated the code into one single script. And all time it took only the first asteroid. Still, the original script is working ( tested many nights).

And look at my discovery:
Chronologically, After the first click to warp at the first asteroid, the script became busy with other things ( host logs lines 70-85, he make measurements for modules. So he has time to update in memory the visited locations because he enter in warp in almost same time.
But with an vni/capsule, and without any other things to do, he try again the warp at the same spot, ( first asteroid) at max 1 sec . Surprise, because he discover the menu isnt the same, ( no warp text at first asteroid),he declare "

Host.Log("no suitable menu entry found on '" + destinationMenuEntry?.Text + "'");
	return true;

and does not register/update the location in visited locations

So I put an delay of 8 sec at line 557 in original script

	if (null != maneuverMenuEntry)
	{
		Host.Log("initiating '" + maneuverMenuEntry.Text + "' on '" + destinationMenuEntry?.Text + "'");
		Sanderling.MouseClickLeft(maneuverMenuEntry);
		Host.Delay(8000);// this one is too big , of course2-3 sec is enough, i think
		return false;
	}

and … is working, he travel like a boss from one asteroid to another.

Now, he take the asteroids one after another in order ( first, the second etc etc until 38 in my case) but i believe you can use the random code and if the new asteroids is already in memory from visited locations, he can take another one, at random. When you filled all places , you wipe the memory and you start from scratch.
For now Im happy with sandy script of fighting

3 Likes

another ones :))

in framework i cannot make him to orbit around targetselected ( current target) or any object. If i put a void to do that from overview around an object, it take too much time and the other tasks take place. Even so, its a timing thing, in time i can resolve that.
what it didnt work is the obrit using the keyboard.
I put the lines :slight_smile:

if (Broken?.Length > 0)
{
Sanderling.KeyDown(orbitKeyCode);
Sanderling.MouseClickLeft(Broken.FirstOrDefault());
Sanderling.KeyUp(orbitKeyCode);
ActivateAfterburnerExecute();
//Orbit(celestialOrbit);	
}
else
{
Sanderling.KeyDown(orbitKeyCode);
	Host.Log("press w");
Sanderling.MouseClickLeft(ListRatOverviewEntry?.FirstOrDefault(entry => !((entry?.MeTargeted ?? false) || (entry?.MeTargeting ?? false))));
				
	Host.Log("click target");
Sanderling.KeyUp(orbitKeyCode);
Host.Log("press w");
//ActivateAfterburnerExecute();
/*
Sanderling.KeyDown(VirtualKeyCode.VK_W);
Sanderling.MouseClickLeft(Broken.FirstOrDefault());
Sanderling.KeyUp(VirtualKeyCode.VK_W);
ActivateAfterburnerExecute();*/
}

I tried in both variants ( you see the code between /* */). and in fact he didnt make the click on object and sometime he didnt push the “w” ( i dont remember wich one is good or not)

I get to the next point: to do warp in A-bot to asteroids, like in framework.
So, i tried to combine the abot way of warp and to take the asteroids from infobutton.

				Queue<string> visitedLocations = new Queue<string>();
				MemoryStruct.IMenuEntry PickNextMiningSiteFromSystemMenu(IReadOnlyList<MemoryStruct.IMenuEntry> availableMenuEntries)
				{
					//Host.Log("I am seeing " + availableMenuEntries?.Count.ToString() + " mining sites to choose from.");

					var nextSite =
						availableMenuEntries
						?.OrderBy(menuEntry => visitedLocations.ToList().IndexOf(menuEntry?.Text))
						?.FirstOrDefault();

					//Host.Log("I pick '" + nextSite?.Text + "' as next mining site, based on the intent to rotate through the mining sites and recorded previous locations.");
					return nextSite;
				}

........ 
					yield return new MenuPathTask
					{
						RootUIElement = memoryMeasurement?.InfoPanelCurrentSystem?.ListSurroundingsButton,
						Bot = bot,
						ListMenuListPriorityEntryRegexPattern = new[] { new[] { "asteroid belts" }, new[] { PickNextMiningSiteFromSystemMenu/* ???? how ???? ToString*/ }, new[] { @"0", ParseStatic.MenuEntryWarpToAtLeafRegexPattern } },
					};

In fact I need PickNextMiningSiteFromSystemMenu which is a method to string. I tried to to that using an variable
var pickNextMiningSiteFromSystemMenu = ??
but with my knowledges i cannot do that. from what i found over the net, i have to transform him to object and this one to string. I believe its like that. Even if i try with random method it says is not no best type found for implicitly-typed array.
So Im stucked :d

What i wanna do in reality, well, on the one side to use framewok because is more fiable, the Abot is a pain in the ass with inventory, stoping the bot, you dont have an delay time, and more things. I good to do simple tasks,

is a complex structure. It is necessary to examine its fields and find the necessary ones.
If you need only string value from this structure, so maybe you should use some field like Text.

	yield return new MenuPathTask
					{
						RootUIElement = memoryMeasurement?.InfoPanelCurrentSystem?.ListSurroundingsButton,
						Bot = bot,
						ListMenuListPriorityEntryRegexPattern = new[] { new[] { "asteroid belts" }, new[] { PickNextMiningSiteFromSystemMenu(visitedLocations/*or whatever it is called?*/)?.Text ?? string.Empty }, new[] { @"0", ParseStatic.MenuEntryWarpToAtLeafRegexPattern } },
					};

In your example, var uses lazy asses when they do not want to write the variable type :slight_smile:

it says : no best type found for implicitly-typed array.

and VS guide me to implement:


		private string PickNextMiningSiteFromSystemMenu(Queue<string> visitedLocations)
		{
			throw new NotImplementedException();
		}

		private string PickNextMiningSiteFromSystemMenu(string v)
		{
			throw new NotImplementedException();
		}

		public IEnumerable<MotionParam> Effects => null;
	}

I tried with an string , is working and if i put var blabla = “skdjfh” is working

he do the same with randoms , so i supose in the end he take only string

[quote=“Terpla, post:19, topic:953”]
In your example, var uses lazy asses when they do not want to write the variable type :slight_smile:
[/quote] i saw, sanderling doesn’t love lazy peoples :d :))