mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 19:03:43 +00:00
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>
53 lines
2.4 KiB
C++
53 lines
2.4 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <optional>
|
|
#include <span>
|
|
#include <vector>
|
|
|
|
#include "NiPoint3.h"
|
|
|
|
class dpEntity;
|
|
|
|
/**
|
|
* Walls the characters the server walks (enemies, NPCs, pets on MovementAI) cannot cross. The server moves them along
|
|
* navmesh paths without collision, so a wall that should stop them only does if their paths stop at it:
|
|
* - Objects that carve the AI navmesh (navmesh_carver in their level config, read by the client's
|
|
* LWOBasePhysComponent::LoadConfigData 0x00c495c9 next to add_to_navmesh and carver_only): nothing walked by the
|
|
* server may go where they are. Many are carver_only too, which the client never loads, as the walls around the
|
|
* Avant Gardens Sentinel camp.
|
|
* - Solid objects whose collision group touches the movers but not players (e.g. group 18, "FV - Enemy Blocking
|
|
* Volume" and the Nimbus Station pet ranch's "PR - Pet Blocker"): the client lets players through them, so they are
|
|
* only there to stop NPCs.
|
|
*/
|
|
struct dpMovementBlocker {
|
|
const dpEntity* entity{};
|
|
// The collision filter it blocks with (see dpCollisionFilter): 0 blocks every mover
|
|
uint32_t filter{};
|
|
};
|
|
|
|
namespace dpMovementBlockers {
|
|
// Paths are tested this far above the ground a mover walks on, so a wall sitting on uneven ground still catches them
|
|
constexpr float STEP_HEIGHT = 1.0f;
|
|
// How far before a wall a stopped path ends
|
|
constexpr float STOP_DISTANCE = 1.0f;
|
|
|
|
/**
|
|
* The filter a physics object blocks movers with, or none when it doesn't block them: 0 for a navmesh carver,
|
|
* its group for a solid object only enemies (not players) collide with.
|
|
*/
|
|
[[nodiscard]] std::optional<uint32_t> BlockingFilter(bool navmeshCarver, bool solid, uint32_t collisionGroup);
|
|
|
|
/**
|
|
* Where a mover with the given collision filter first walks into one of the blockers, going along the segment from
|
|
* a to b, as a fraction of the way (0 to 1). Only boxes block; a mover that starts inside one can walk out.
|
|
*/
|
|
[[nodiscard]] std::optional<float> FirstHit(std::span<const dpMovementBlocker> blockers, const NiPoint3& a, const NiPoint3& b, uint32_t moverFilter);
|
|
|
|
/**
|
|
* A path (the points after start) cut where it first walks into a blocker: it ends STOP_DISTANCE short of the wall.
|
|
* Returns the path unchanged when it crosses none.
|
|
*/
|
|
[[nodiscard]] std::vector<NiPoint3> ClampPath(std::span<const dpMovementBlocker> blockers, const NiPoint3& start, std::vector<NiPoint3> path, uint32_t moverFilter);
|
|
};
|