Basssiiie / OpenRCT2-ProxyPather

This plugin lets you quickly cover guide paths with full paths without changing the guests pathfinding.
MIT License
12 stars 0 forks source link

Peeps not staying on proxy paths #87

Open alex-parisi opened 1 week ago

alex-parisi commented 1 week ago

Describe the bug Peeps are no longer sticking to the paths correctly after using the Proxy Pather tool.

To reproduce Steps to reproduce the behavior:

  1. Create a path, and then a bunch of additional non-connected paths around it
  2. Use the ProxyPather tool to proxify all paths.
  3. See problem

Expected behavior Peeps should stay on the original path and not go onto the non-connected paths around it.

Screenshots Video

I'm not sure if this is a bug with the paths generated by the plugin, or if it's a problem with the latest changes in OpenRCT2 v0.4.15 regarding wide paths. Feel free to close this if you think it's the latter.

Basssiiie commented 1 week ago

Hey Alex, thank you for your report. When inspecting the path with the Tile Inspector, are the tiles laid out as expected? (bottom layer is the original path, top layers the fake path)

If that's the case then it sounds indeed like it may be an OpenRCT2 issue. If the tiles are different, the issue may lay somewhere else. I'd like to keep the issue open regardless though, to track it until it fixed in either place. 🙂

alex-parisi commented 1 week ago

@Basssiiie, thanks for the quick reply, and for all of your work so far for the OpenRCT2 community. You're a legend as far as I'm concerned 😄

The tiles are indeed laid out correctly when using the plugin - the top layer is the "visible" fake path and the bottom layer is the real original path. So I'm thinking it's probably due to the latest OpenRCT2 update. I want to try the exact same pathing scenario on an older version and post the results here when I get the chance

Basssiiie commented 1 week ago

Screenshots and a save file where we can check the before and after would be great yeah. And thanks to you as well! I'm glad you like my work. 😄

alex-parisi commented 1 week ago

Here's a .park file that demonstrates the pathing issue. This .park file seems to only work with OpenRCT2 v0.4.15 though, and it won't load into earlier versions - did the save format change? Maybe it's the new track pieces... image Small park pathing bug reproduction

Either way, I'll try to recreate this in v0.4.14 and see if the issue is still presenting.

One thing I've noticed, is that all of the guests who "break" the pathing rule and go onto the non-connected path segments all seem to have a destination, i.e. "Leaving the park", or "Heading to [ride name]". I have never seen a peep that is just wandering end up on a non-connected path.

alex-parisi commented 1 week ago

Here's a .park file that works on v0.4.14:

Small park pathing bug reproduction on v0.4.14

Seems that the pathing problem was on v0.4.14 as well, I can observe the same behavior. Perhaps this goes deeper than originally thought.

I do think the strongest hint we have here is that all of the peeps who go onto non-connected paths have destinations - it seems like pathfinding isn't respecting the layer order of the paths and treats the "proxy" path as a connected path.

alex-parisi commented 1 week ago

From GuestPathfinding.cpp:

do
      {
          if (destTileElement == nullptr)
              break;
          if (destTileElement->BaseHeight != loc.z)
              continue;
          if (destTileElement->GetType() != TileElementType::Path)
              continue;
          found = true;
          if (firstTileElement == nullptr)
          {
              firstTileElement = destTileElement;
          }

          /* Check if this path element is a thin junction.
           * Only 'thin' junctions are remembered in peep.PathfindHistory.
           * NO attempt is made to merge the overlaid path elements and
           * check if the combination is 'thin'!
           * The junction is considered 'thin' simply if any of the
           * overlaid path elements there is a 'thin junction'. */
          isThin = isThin || PathIsThinJunction(destTileElement->AsPath(), loc);

          // Collect the permitted edges of ALL matching path elements at this location.
          permittedEdges |= PathGetPermittedEdges(peep.Is<Staff>(), destTileElement->AsPath());
      } while (!(destTileElement++)->IsLastForTile());

The path tile that's used in pathfinding will be the first path tile returned from MapGetFirstElementAt - is there a guarantee that this will be the path lower in the tile order? I'm not sure how the tiles are managed... If not, there's the potential that the "proxy path", which is higher in the tile order, could be used for pathing.

The exception is that if another path element is present on the tile, its "thinness" is checked. If a path tile has more than two connected edges, it's considered "thin" (kinda seems like the opposite usage of the word?). There's this note in the comments:

            /* Check if this path element is a thin junction.
             * Only 'thin' junctions are remembered in peep.PathfindHistory.
             * NO attempt is made to merge the overlaid path elements and
             * check if the combination is 'thin'!
             * The junction is considered 'thin' simply if any of the
             * overlaid path elements there is a 'thin junction'. */

So here's a potential scenario: image image image

There's also this note:

            /* Use of peep.PathfindHistory[]:
             * When walking to a goal, the peep PathfindHistory stores
             * the last 4 thin junctions that the peep walked through.
             * For each of these 4 thin junctions the peep remembers
             * those edges it has not yet taken.
             * If a peep returns to one of the 4 thin junctions that it
             * remembers, it will only choose from the directions that it
             * did not try yet.
             * This forces to the peep pathfinding to try the "next best"
             * direction after trying the "best" direction(s) and finding
             * that the goal could not be reached. */

So if a peep returns to this tile quickly, it can potentially use unviable directions from the "proxy" path as new directions to try.

There's another note that also potentially points at an issue:

                    /* Fix broken PathfindHistory[i].direction
                     * which have untried directions that are not
                     * currently possible - could be due to pathing
                     * changes or in earlier code .directions was
                     * initialised to 0xF rather than the permitted
                     * edges. */

Summary:

This bug is not due to the ProxyPather plugin, but rather due to the underlying pathing mechanics of OpenRCT2. It seems there are scenarios where the "proxy" path can influence pathfinding and cause peeps to choose non-viable directions to explore.

If it's okay with you, I'd like to keep this issue open until I can at least transfer my findings to the OpenRCT2 repo 😊

Basssiiie commented 1 week ago

Thank you for the further research and the issue on the OpenRCT2 repository. I'd like to keep this one open as well until the one in the OpenRCT2 repo is resolved, as a headsup/reminder. 🙂