Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fix #163
This makes Character.PlaceOnWalkableArea use 1-pixel step, in order to not skip thin areas.
Refactored searching algorithm and moved to Pathfinding::FindNearestWalkablePoint(). This function still accepts search "step" parameter, so in theory we could use it if a need arises.
Renamed original function to FindNearestWalkableAreaForCharacter(). Made it clear which restrictions to the scanning area do we make, according to historical AGS rules (don't move character beyond room edges, etc).
Removed the obscure hard-coded restriction which did not let move character to the topmost 14 pixels of the room. I think that may be related to the Sierra-style Statusbar GUI from the default template.
Optimized scanning algorithm for speed (see explanation below).
Would need to make performance tests to see the actual difference in very big rooms.
Optimization:
The original algorithm took whole room (or rather section limited by edges), and scanned it awhole from left-top to right-bottom. When finding a walkable point, it would calculate a distance from the character, save the nearest found point, and continue. In the end, it would scan all the room regardless, even if it may be certain that there cannot be any point in a smaller radius already. This of course is quite suboptimal.
The new algorithm that I'm trying here does a outwards spiral scanning, checking pixels which lie on a rectangle border, where a rectangle is built with certain radius around the starting position.

That is: it first takes 3x3 rectangle and scans pixels on its border. Then it takes 5x5 rectangle and scans its border. Then it takes 7x7 rectangle, and so forth.
In this algorithm I still keep the original act where it would increment along x axis in the outer loop and increment along y axis in the internal loop. This means that pixels located towards top-left have higher priority among walkable pixels with the same distance. Idk if that matters much, but I kept it like that just in case some old game relies on that (call that a paranoia).
In order to not duplicate work, it actually skips the pixels inside rectangle, because these were already processed when scanning previous rectangles. This is done simply by adjusting internal step: when first and last rect line are scanned, we step by 1 pixel, but when any other line is scanned, we step by radius * 2, which results in only two pixels on the opposite rectangle's side checked.
Here's a illustration:
Whenever a walkable point is found, it calculates a distance to it, checks if that's the smallest found distance, and also reduces the scanning range to that distance, in order to stop early, since we won't be needing any pixel at a larger distance anymore.
NOTE: that it's still necessary to keep testing for some time even if we find a walkable point, because we scan in rectangles and not in circles. Would we scan in circles, we could stop as soon as any walkable pixel is found, but with rectangles we cannot be immediately certain if that pixel is really the closest. For example, a pixel on rectangle's corner is further away from center than a pixel in the middle of a rectangle's edge.