Prioritize Stockpiles

Like I’ve said, you’re currently looking at this the wrong way around. Items don’t search for stockpiles. Stockpiles search for items. Think of it as some sort of pathfinding liquid: It spreads evenly out from a stockpile and every item it touches is checked for its availability (“Do my filters match this entity?”). If it does, the worker will carry this item to the stockpile - so only after it was decided which item is to be carried, it’s actually figuring out the way from the item to the stockpile.

That makes a lot of sense, as I’ve said above. Imagine you have 3 stockpiles but a hundred items. Three stockpiles searching for items equals three pathfinders doing work. If the items were to search a stockpile, we had a hundred different pathfinders doing different pathfindings - which would be infeasible.

So: Items do not have a destination. Stockpiles are really just greedy bastards with a first-come-first-taken mentality. That makes it difficult to have any sort of priorisation done.

Example: Let’s say we have two stockpiles, Lefty and Righty. And an item.

Let’s say that lefty has a high priority of 6, and righty a low priority of 1. They both could stock the oak log.

With the current system, there’s no way to determine in which stockpile the log will end up in. Because whatever stockpile’s pathfinder first registers the oak log will claim the log for itself. That could be lefty. That could be righty.

So how could a priority system work? It’s difficult. It can’t work this way, but let’s assume that both pathfinders were running at the same time and with the same speed. Obviously, righty would find the log first, because it’s closer. Lefty has no chance to even say “Hey, that log is more important to me”, because by the time it finds the log, it’s already claimed by righty. The pathfinder does, for obvious reason, ignore entities that have been claimed, so the log is not considered a valid entity for lefty.

There’s a possible fix, which is even semi-nice. It’s a radical change from what we have currently, however, and it might even improve performance a lot, assuming a few things are done properly in the engine/game:

A service would take care and keep track of stockpiles (and items). Whenever an item is dropped onto the ground, the service will check some frames later if the item is still unreserved (to avoid messing with entities that are picked up immediately/soonish). If it is, it’s checking the item against all stockpiles, creating a list of stockpiles that could fit this item. This does not involve any pathfinding at all; it’s a simple check which stockpiles could be considered.

Once it has the list, we could start sorting it. For example, by stockpile priority. Or stockpile priority / air distance. Or whatever heuristic might seems appropriate. That way, we would end up with one stockpile that has “won” the census by being the most appropriate one.

If there was no eligible stockpile, the item could be added to a “reserve list”. Whenever any stockpile is modified or one is removed/added, every entity in the reserve list is considered to be “dropped” again, therefore querying all stockpiles again.

At this point, we have both a stockpile (the destination) and the item that needs to go there. We can now tweak the AI even further by doing a BFS-search from the item and find the closest (free) worker to move the entity. This has the advantage that hearthlings no longer run across the whole map when there’s somebody closer by; because it would always pick the closest worker.

So what we would gain:

  • Customizable stockpiles: Priorities, quantities and all these things could become possible, even on an per-entity basis (i.e. fine chairs have a priority of 100, normal chairs are not stocked). By plugging lua into this, you could have filters and priorities that are as fine as you want them to be. It would also be possible to have “roundabouts” stockpiles; i.e. “I take oak logs with priority 100 until I have 20 of them; then I don’t take any more”.
  • Customizable items: This whole process could be a bidirectional thing. Stockpiles filter and prioritise themselves, but the item can pick too. For example, you could define a region and everything inside that region is taken to stockpile X, no matter what the stockpiles themselves want (for example, have all stuff from your mines put into some intermediary stockpile at the entrance to avoid hearthlings running across the whole town because there’s a designated stockpile elsewhere). This picks up @Atralane’s idea of tagging items.
  • Improved performance: Because there is no continuous pathfinding involved anymore, the game can use these resources for other things.
  • Improved AI: Items are taken by the closest worker, not the one whose PF found it first. Therefore, we’re reducing the overall walking and standing around in odd places.

This is based on some assumptions:

  • There is a proper callback to find out when an entity was dropped into the world. One can trace the terrain’s entity_container to find out when children have been added, which could work. However, workbenches do not add the items to the terrain but rather the workbench, and the PF can/is doing recursive searches; so our service would need to do that too (i.e. instead of just tracing the terrain, it needs to trace every workbench too, and every other entity that can act as container).
  • The stockpile components need to inform the service whenever a filter is changed. If the filter is expanded (i.e. less entities types may be stocked), all now unfitting entities must be reported as “dropped”. If the filter is reduced, the service must check if there are reserve entities that could be stocked. The same applies to removing/adding stockpiles.
  • For the closest-AI approach, there would have to be a way to find out if a worker was currently “idle”. I suppose there could be, because you could simply check for idle actions (i.e. if your current action descends from stonehearth:idle). There would also need to be a way to dispatch tasks to entities directly. I suppose that’s all stuff the task scheduler does, so in the very worst case that could be recycled.

It sounds like fun to do, actually.

7 Likes