The Harvester trait and MoveAdjacentTo activity called the pathfinder but had a single source and multiple targets. The pathfinder interface only allows for the opposite: multiple sources and a single target. To work around this they would swap the inputs. This works in most cases but not all cases. One aspect of asymmetry is that an actor may move out of an inaccessible source cell, but not onto an inaccessible target cell.
Searches that involved an inaccessible source cell and that applied this swapping method would therefore fail to return a path, when a valid path was possible. Although a rare case, once good way to reproduce is to use a production building that spawns actors on inaccessible cells around it, such as the RA naval yard. A move order uses the pathfinder correctly and the unit will move out. Using a force attack causes the unit to use the broken "swapped" mechanism in MoveAdjacentTo and it will be stuck.
This asymmetry has been longstanding but the pathfinding infrastructure only sporadically accounted for it. It is now documented and applied consistently. Create a new overload on the pathfinder trait that allows a single source and multiple targets, so callers have an overload that does what they need and won't be tempted to swap the positions and run into this issue.
Internally, this requires us to teach Locomotor to ignore the self actor when performing movement cost checks for these "in reverse" searches so the unit doesn't consider the cell blocked by itself.
The cache in Locomotor that is populated via the UpdateCellBlocking method disagreed with the non-cached logic of IsBlockedBy when dealing with Mobile actors. The cache determined an actor to be moving if it was both movable and had horizontal movement types. IsBlockedBy determined an actor to be moving if it had horizontal movement types, but did not check if it was movable. This difference in checks could allow a mobile trait that was disabled or paused and which had horizontal movement to be treated differently be the two methods. UpdateCellBlocking would consider it not moving due to the disabled/paused trait. IsBlockedBy would consider it moving as it didn't care about the disabled/paused state of the trait.
Now, we unify the two methods to consider a mobile trait that is disabled/paused as not moving. This prevents HierarchicalPathFinder from crashing on the inconsistent state, i.e. when asked to path search through a cell of a mobile unit which has disabled or paused movement, but which has horizontal movement types from prior movement.
Set an actor moving along several waypoints whilst the /path-debug command is active, then deselect the actor. Each waypoint will add more information to the debugging overlay until it crashes with "Maximum two records permitted." This resolves the crash by no longer adding new debugging information once the actor is deselected.
- Add prefixes to all message keys to provide context
- Use messages with attributes for some UI elements (dropdowns, dialogs, checkboxes, menus)
- Rename some class fields for consistency with translation keys
The Locomotor IsMoving check was allowing us to consider another actor that moving as not a blocker. However for some reason it also considered the actor trying to path being mobile as sufficient for this check to pass which did not make sense. We remove that extra check and inline the method.
This was a regression from 4a609bbee8 which changed the method from IsMovingInMyDirection (which required the lookup of the mobile trait) to just IsMoving. It should have removed the lookup as not required.
This fixes a crash in HPF which was considered the location as blocked when Locomotor considered it unblocked because the logic was not aligned. Removing this check aligns the logic and resolves the crash.
Using the local pathfinder, you could not find a path to an unreachable destination cell, but it was possible to find a path from an unreachable source cell if there was a reachable cells adjacent to it.
The hierarchical pathfinder did not have this behaviour and considering an unreachable source cell to block attempts to find a path.
Now, we unify the pathfinders to use a consistent behaviour, allowing paths from unreachable source cells to be found.