feat(physics): movement blockers the server's movers can't walk through

Enemies are moved along navmesh paths without collision, so a wall only stops
them if their path stops at it. dpWorld now keeps a list of such walls, each
with the collision filter it blocks with, and cuts a path where it first walks
into one (a little short of the wall; a mover inside one can walk out).

Which objects block comes from data (dpMovementBlockers::BlockingFilter):
- navmesh carvers (navmesh_carver in the level config, which the client reads
  with add_to_navmesh and carver_only) block every mover;
- solid objects whose collision group touches enemies and not players (group 18,
  e.g. "FV - Enemy Blocking Volume", "PR - Pet Blocker") block what that group
  touches.

dpShapeBox gains SegmentEntry, a segment test against the rotated box.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 17:37:37 -05:00
parent 69ee6b0bc6
commit 425b913848
10 changed files with 305 additions and 0 deletions

View File

@@ -22,6 +22,8 @@ namespace {
std::vector<dpEntity*> m_StaticEntities;
std::vector<dpEntity*> m_DynamicEntites;
bool phys_spatial_partitioning = true;
std::vector<dpMovementBlocker> m_MovementBlockers;
};
void dpWorld::Initialize(unsigned int zoneID, bool generateNewNavMesh) {
@@ -84,6 +86,8 @@ void dpWorld::Shutdown() {
delete m_NavMesh;
m_NavMesh = nullptr;
}
m_MovementBlockers.clear();
}
bool dpWorld::IsLoaded() {
@@ -187,3 +191,20 @@ bool dpWorld::ShouldUseSP(uint32_t zoneID) {
return false;
}
void dpWorld::AddMovementBlocker(const dpEntity* entity, const uint32_t filter) {
if (!entity) return;
m_MovementBlockers.push_back({ entity, filter });
}
void dpWorld::RemoveMovementBlocker(const dpEntity* entity) {
std::erase_if(m_MovementBlockers, [entity](const dpMovementBlocker& blocker) { return blocker.entity == entity; });
}
std::span<const dpMovementBlocker> dpWorld::GetMovementBlockers() {
return m_MovementBlockers;
}
std::vector<NiPoint3> dpWorld::ClampPath(const NiPoint3& start, std::vector<NiPoint3> path, const uint32_t moverFilter) {
return dpMovementBlockers::ClampPath(m_MovementBlockers, start, std::move(path), moverFilter);
}