Unit Z position and creature/involuntary player movement.

There’s no TL;DR I’m afraid. If you’re interested in this subject read on. I think it’ll be interesting for those that have an interest in creature movement and Z position issues.

I’ve been looking into this, since while testing the dynamic spawn changes I was levelling a warlock and found the minion to be falling through the floor far more than we should be accepting. In my initial investigation I came across something that I feel is obviously wrong and has been since the beginning of TC time.

https://github.com/r00ty-tc/TrinityCore/commit/10550fe8cff1fb91c8084b623fe243a6340ea52d

Now, this change fixes a lot of the cases this happens. The original code seems to be based on the premise that if the object’s z position is already under z then it’s a problem. Whether or not that’s true is actually irrelevant though. Since this function is more often than not used to find the floor Z in a position some distance from the starting position. Where often the selected position would be below ground but we want to follow the contour above. In my mind, in every case that matters we should be using map and vmap floor level and accept whichever is closest to our original Z.

Now, onto my next line of thinking. Quite often the system is fooled by a second layer of ground very close to the one we want. Best example for this is Sunstrider Isle. Where the mana wyrms are roaming on and off the platform. Often they fall through. The reason here is quite obvious. That the ground level continues under the platform. When moving from normal ground level to the position on the platform the floor Z that is closest to that original Z is the under floor level. The solution came from a discussion with Treeston on this issue. Where a good way to solve this, again not for all cases, but most is to use the mmap tile z position, where available. This lookup take a minuscule amount of time compared to a full path lookup (very much fraction of a ms, we could probably time it in microseconds or less). Using this floor Z as a basis then finding the floor from there (with the usual +2.0f) fixed things for most places.

However, again there’s some exceptions. In Deathholme (Ghostlands) for example there’s some places where mmap Z is 4-5 below the surface. Resolving this moved onto a new idea for resolving height.

The functions to derive true floor Z have a limitation, which is quite obvious when you think about wow textures. That is, for the floor you’ll only get a reliable hit looking down (since looking up you either have no texture, or worse you’ll hit the bottom of the platform not the top). So traditionally we look 2.0f above the Z we want and just hope there’s nothing higher than that. Except there is. Now, seeing as we have a good starting point (either previous position Z or mmap Z) rather than a fixed 2.0f. I think we should start searching both ways. So, look down from initial z (mmap or previous). Get first strike. Then look from +8,0f (so far I’ve not seen anything with this much difference). If the value is the same as the search from initial z. Then we search no more. If there is another value, we search from that point -1.0f until we reach the first search value or a better match (a z closer to the initial z). In this case, in most cases it’s one extra height call (rays are costly). Only where the detail is complex will more than 1 extra call be needed.

At this point we’ll have a very good getHeight function. If there’s situations where it returns bad Z values, it’s likely the movement generators checks for LoS will catch it anyway. If not, we need to look into LoS checking on the movement destination. This wouldn’t be nice but, it’s a next step if we still find problems.

After making the above changes, I found charge worked in ALL KIND of places it would usually fail. In fact, I simply couldn’t get charge to fail and I couldn’t make the minion fall underground. This is almost mission accomplished.

Then I tried fear on player. Now, in most cases where this used to fail I found it was resolved (Dalaran inside buildings etc). My thinking is that is was choosing a Z below ground and thus not seeing the walls from the perspective. I’m not sure what was really happening. But it seems to resolve them. The problem still arises where there is mountainous terrain. Here’s where I think things go wrong.

Fear, chooses a point in a random direct at a random distance (up to a specific limit). The problem is, the path finding finds paths for creatures and creature pathfinding seems to be accept paths where a player should not be able to run. My example area was The Supulcher. The mountains around there, it will let you path up to silly heights. Worse, the pathfinding is not set to straight line. So it will happily make a path AROUND the mountain. Except it seems that when feared it does not use that path, but rather goes direct (AKA through the mountain). There’s also a lot of weird animation that happens and I think it’s mainly because movements for a player are sent to the client, which the client plain and simply does not like. Not I tried this with charge, the charge pathed around the mountain correctly. Fear chose the same path and went through the mountain. I think it’s client overriding server given path. But I’ll need to debug actual packets to be sure of that.

I’m still investigating this area. But, the solution seems convoluted and I think would require more CPU. Hence it would need to be on a parameter. First change, path generation using straight line mode. Next change is that the path is already split into 10 steps. The checks on these 10 steps include a check on player climbable (45-50deg limit) on the mmap Z (not texture Z). Stopping at the point before that happens.

This resolves the problems most of the time. But, still some issues remain.

So, mainly this post is a bit of a result of my research in the area and maybe a chance for some people that have maybe been here before to point me away from any direction I’m going that is wasting time.

So far, amazing work.

Before you start spending time investigating relatively niche (visual) bugs with Fear movement, however, would you mind making a PR for the results so far, which seem like they will fix a wide variety of really frustrating bugs in current pathing. The sooner we get this out there for testing (and eventual merging), the better.

Yeah, I need to clean up my repo branch for this, which is full of failed experiments to mainly condense the above findings into a solid single commit. I’ll try to do that this evening.

Please include some kind of unit tests or at least a list of coordinates where it failed before and where you had to modify something to get the proper height.

As for pathfinding valid paths you could play with values modified at https://github.com/TrinityCore/TrinityCore/commit/93a68a66c3009ea86569076ea05a39ce87dfb78d .

Yeah I was actually looking at the commit a short while ago. The thing is, I am not sure if creatures should be bound by the same limits. But for sure, the pathing goes beyond player allowed limits and as such I’m fairly certain it’s actually the client misbehaving (or rather behaving unexpectedly at the unexpected spline points fed to it). You can see it’s about to happen for creatures and players when it looks like they’re walking on the same spot or the movement speed slows on the client side.

I also think (I need to find total proof, only anecdotal evidence) that the creature pathing sometimes offends the client too. Yep I’ll define all the testing spots I’ve been using (and will be asking for some more trouble spots, so that normal behaviour can be observed everywhere).

Now, for basic stuff like fear since it’s always a straight line and we only want to go as far as is allowed, we can actually analyze the points in the path and detect when the mmap tile goes beyond the allowed limit and stop before it reached that point. For mind control where we may path to another player. I am not quite sure what would happen. I suspect similar weird results… I’ll add it to my list of things to try.

There’s actually still a lot of test cases I need to resolve for fear alone.

After playing a bit with this idea. The core “bugfix” I think still stands. It seems to correct most bad situations. Although a lot still exist after.

However, my plan to use mmaps doesn’t pan out in some situations. There’s also some very odd things that happen with pet pathing that I’ve not been able to resolve without making many vmap calls. The cost is too much, and I think I need to go back to the drawing board and think of another way.

It might be that movement generators need a rethink in general. One thing that seems to be happening is, a path is generated. Start and end position normalized perfectly but it generated a middle position which just will not have Z updated correct. Unless I “force” favour a higher level choice over lower, I know we do that already but it seems like a hack. Now, this will of course break other situations where there should be a lower choice. I thought of some ideas, we check the terrain ahead, is it rising or falling. It’s not hard to do that. But, costly in CPU to be doing that for every pathed movement everywhere in game. So, as I say I’m abandoning this for now. With a plan to revisit a more overall plan later.

mmap Z is the most unreliable Z, you can see that loading mmtiles in recastdemo.

Yeah, I was using it more as a hint. That is, in order to pull the attention away from other nearby “floor” levels that don’t have a navmesh existence (well shouldn’t). Then use normal methods to derive the correct map/vmap/dynamictree floor level. In most places it gave an improvement. But, for sure there’s places it doesn’t help at all (and I found at least one spot where it makes things worse).

I do still have some ideas, but they mostly require a lot more work than I have time to put in right now. I also fear they’ll just add too much CPU burden to be worthwhile.