04 June 2018 Tagged: eulorac++

I bought a tinker book last week and today I got to crack that sumbitch open. Now that I can actually level up from crafting I can gainfully explore Foxybot’s craft code.

To kick things off I followed some advice and went with indistinct bark shavings as a starter craft. Now, understand that you can’t just throw the ingredients together; you need a fresh blueprint each time. You can either buy the bps or use someone’s premade bundles.

I bought a small lot of ibs bps at what is (was?) apparently the going rate for bps of 10x adjusted base value. Hey, don’t judge. Nobody’s queueing up to get my coppers at 9x so I went with 10. What else am I gonna do, not craft? It’s hard out here for a pimp.

The ibs recipe calls for shiny rocks and flotsam. I’ve got 5 figures of shiny rock kicking around the root celar but no flotsam. Since I’ve got the basics of exploring down it doesn’t take me long to find more than I can carry.

I only today started crafting, so I’m not exactly hip to all the fancy dance moves of the veteran crafters. I’m still looking up abbreviations and figuring out where to click.

Well, that click figuring didn’t exactly take a long time to get sorted and I quickly handed it off to the bot. But the bot couldn’t pick the ingredients out of the recipe. It thought there was one ingredient called "flotsam,1 shiny rock" and couldn’t find it.

Let’s see, where is the code for this…​ recipe::ParseIngredients()

src/client/foxybot/recipe.cpp(105)
void recipe::ParseIngredients(csString ingredients, bool useMax)
{
-	csString separator(", ");  (1)
+	csString separator(",");   (2)
	csString between("between ");
	size_t pos, nextPos, length;
	csString ingredientName;
	int ingredientCount;
	csString currText;

	pos = 0;

	while (pos < ingredients.Length())
	{
		while (ingredients.GetAt(pos) == ' ') pos++;   //skip leading spaces if any

		nextPos = NextPosInString(ingredients, separator, pos);
		length = nextPos - pos;
		currText = ingredients.Slice(pos, length);

		GetCountAndName(currText, useMax);    (3)
		pos = nextPos+1;
	}

}
1 A comma and a space used to be the separator.
2 Now the separator is just a comma.
3 Passing each ingredient to have its count parsed.

Ok that was a quick fix. After that is applied the bot can separate ingredients but is chopping off the last character of the name, e.g. "Flotsa". Lets have a look at that GetCountAndName() that’s being called at the bottom of the above code.

src/client/foxybot/recipe.cpp(41)
void recipe::GetCountAndName(csString singleIngredient, bool useMax)
{
	csString separator(" and ");         (1)
	csString between("between ");
	csString space(" ");
	size_t pos, nextPos, length;
	csString ingredientName;
	int ingredientCount;
	csString currText;

	pos = singleIngredient.Find(between);
	if (pos < singleIngredient.Length()) (2)
	{
		if (useMax)    (3)
		{
			nextPos = singleIngredient.Find(separator);
			nextPos = nextPos + separator.Length();
			length = singleIngredient.Length() - nextPos;

			currText = singleIngredient.Slice(nextPos, length);
			nextPos = currText.Find(space);

			ingredientCount = atoi(currText.GetData());
			length = currText.Length() - nextPos - 2;
			ingredientName = currText.Slice(nextPos+1, length);
		}
		else           (4)
		{
			nextPos = singleIngredient.Find(separator);
			pos = pos + between.Length();
			length = nextPos - pos;
			currText = singleIngredient.Slice(pos, length);
			ingredientCount = atoi(currText.GetData());

			nextPos = nextPos+separator.Length();
			length = singleIngredient.Length() - nextPos;
			currText = singleIngredient.Slice(nextPos, length);

			pos = currText.Find(space);
			length = currText.Length() - pos-2;
			ingredientName = currText.Slice(pos+1, length);
		}
	}
	else
	{//"precise" recipe
		ingredientCount = atoi(singleIngredient.GetData());
		if (ingredientCount == 0)
		{
			//no ingredient count specified, so everything is ingredient name
			ingredientCount = 1;
			ingredientName = singleIngredient;
		}
		else
		{
			pos = singleIngredient.Find(" ");       (5)
			length = singleIngredient.Length()-pos-2;
			ingredientName = singleIngredient.Slice(pos+1, length);
		}
	}

//	Notify3(LOG_USER, "Ingredient=#%s# Count=%d", ingredientName.GetData(), ingredientCount);
	ingredientsList->Put(ingredientName, ingredientCount);
}
1 Lots of code blocks,
2 that we don’t care about,
3 handling various cases,
4 for more advanced crafters.
5 And one section here at the bottom that we do care about.

I was concerned at first that I might have to muck about in the code that parses the and 's and between 's for the variable count ingredients of the more highly skilled crafters. But as it turns out the one bit at marker 5 in the above code is the source of our problem and doesn’t execute in those other cases.

I modified those three lines at marker 5 to get rid of the off-by-one error.

src/client/foxybot/recipe.cpp(95)
-        pos = singleIngredient.Find(" ");
+        pos = singleIngredient.Find(" ") + 1;
-        length = singleIngredient.Length()-pos-2;
+        length = singleIngredient.Length()-pos;
-        ingredientName = singleIngredient.Slice(pos+1, length);
+        ingredientName = singleIngredient.Slice(pos, length);

After this change everything looks good. But the ingredients are still not being moved over onto the table, eventhough the names and quantities are being parsed correctly.

For this one we need a tweak to the code that does the moving.

src/client/foxybot/botactivity.cpp(1147)
while (iterIngredients.HasNext())
{
        csString itemName;
        int quantity = iterIngredients.Next(itemName);

        char out[1000];
        sprintf(out, "Ingredient %s: %d", itemName.GetData(), quantity);
        OutputMsg(csString(out));

-       from = worldHandler::FindItemSlot(itemName, false);  (1)
+       from = worldHandler::FindItemSlot(itemName, true);   (2)
        if (!from || from->stackCount < quantity)
        {
                OutputMsg(csString("Not enough ingredients for crafting! Bot stopping."));
                Error();
                return;
        }
        else
        {
                worldHandler::MoveItems(from->containerID, from->slot, toContainer, nextEmptySlot, quantity);
                logText = logText+csString(out)+ csString(" ");
        }
        nextEmptySlot = nextEmptySlot + 1;
}
1 FindItemSlot() was not finding the item slot
2 When I pass in true, it finds

And there you have it. The bot will now craft repeatedly, moving ingredients in accordance with the recipe. At least for simple 'precise' recipes it will.

Daniel P. Barron - 2018-06-05 06:21:03

Tested it and it works. Now it needs to figure out to grab stuff from storage as it crafts. Specifically for the heavy stuff.

Mocky - 2018-06-06 17:17:21

That’s a good idea. I’ve added it to the pipeline


Add a Comment