When a chaser's route finishes calculating, the chaser may already be a
few blocks away from the starting position, thanks to movement inertia.
The path finding code finds the point along the route closest to the
chaser's position.
This calculation only considered the xy coordinates when finding the
closest point. This caused issues whenever the calculated route goes
below the chaser's position (for example, when the chaser is on top of a
bridge and the route circled around to go under the bridge). In this
case, there was a chance that the closest point was the one below the
bridge. This caused the chaser to try to move directly to a directly
inaccessible block.
The fix was to remove the xy() filter so that the closest point
algorithm also considered the z coordinate.
We had a random chance of restarting the route to avoid getting the
chaser stuck. This didn't work properly because the rerouting code was
only triggered if `self.route.is_none()` or if the target moved
significantly, and the random chance branch only set `bearing` to `None`.
This change sets `self.route = None` to trigger rerouting.
- Armor and sneaking have exclusive effects on overall stealth, rather
than armor taking effect only while sneaking.
Gameplay:
- Agents factor in stealth from armor in all cases, not only when sneaking.
- Max stealth takes place when sneaking (final multiplier of `0.7`) and with stealth from armor up to `0.7` (`0.3` multiplier), resulting in a max distance modifier of about `0.5`, approximately what it was previously.
- Min stealth score from armor is now 0 instead of 2.
Internals:
- Stealth getter accounts for sneaking in final calculation, not just
armor.
- Prevents potential division by zero.
- Stealth getter returns value that should be multiplied instead of divided.
- Legitimized stealth as a score between 0 and 1.
Notes:
- FIXME: Someone more familiar with the different armor types may want to adjust their stealths.
- Armor stealths seem to be valued between `0.0` and `1.0`, and I've reinforced this in the code. However, it is possible, particularly for the `Dragonscale` armors, to cumulatively reach a value of `2.0`.
- Refactors `choose_target()`, renaming it and extracting functions with more meaningful names and more correct behavior.
- Adds FOV for agents scanning for hostile targets.