Files
Viano-Core/src/server/game/World/ClientlessPerceptionMgr.cpp
T
Nico b14c864814
clang PCH / build (push) Canceled after 0s
GCC PCH / build (push) Canceled after 0s
Implemented travel graph
2026-10-13 19:39:53 +00:00

302 lines
10 KiB
C++

#include "ClientlessPerceptionMgr.h"
#include "CellImpl.h"
#include "ClientlessPlayerMgr.h"
#include "Creature.h"
#include "GridNotifiersImpl.h"
#include "Player.h"
#include "SharedDefines.h"
#include "World.h"
#include "WorldSession.h"
#include <algorithm>
#include <list>
#include <sstream>
namespace
{
constexpr float DEFAULT_SCAN_RADIUS = 40.0f;
constexpr uint32 SCAN_INTERVAL_MS = 1000;
ClientlessEntityRelation RelationFromReaction(ReputationRank reaction)
{
if (reaction <= REP_HOSTILE)
return ClientlessEntityRelation::Hostile;
if (reaction >= REP_FRIENDLY)
return ClientlessEntityRelation::Friendly;
return ClientlessEntityRelation::Neutral;
}
}
ClientlessPerceptionMgr* ClientlessPerceptionMgr::instance()
{
static ClientlessPerceptionMgr instance;
return &instance;
}
char const* ClientlessPerceptionMgr::TypeName(ClientlessEntityType type)
{
switch (type)
{
case ClientlessEntityType::Creature: return "Creature";
case ClientlessEntityType::Player: return "Player";
}
return "Unknown";
}
char const* ClientlessPerceptionMgr::RelationName(ClientlessEntityRelation relation)
{
switch (relation)
{
case ClientlessEntityRelation::Unknown: return "unknown";
case ClientlessEntityRelation::Friendly: return "friendly";
case ClientlessEntityRelation::Neutral: return "neutral";
case ClientlessEntityRelation::Hostile: return "hostile";
}
return "unknown";
}
std::string ClientlessPerceptionMgr::CapabilitiesToString(uint64 capabilities)
{
std::ostringstream out;
bool first = true;
auto add = [&](uint64 flag, char const* name)
{
if ((capabilities & flag) == 0)
return;
if (!first)
out << ',';
out << name;
first = false;
};
add(CLIENTLESS_CAP_VENDOR, "vendor");
add(CLIENTLESS_CAP_TRAINER, "trainer");
add(CLIENTLESS_CAP_FLIGHTMASTER, "flightmaster");
add(CLIENTLESS_CAP_BANKER, "banker");
add(CLIENTLESS_CAP_INNKEEPER, "innkeeper");
add(CLIENTLESS_CAP_AUCTIONEER, "auctioneer");
add(CLIENTLESS_CAP_QUESTGIVER, "questgiver");
add(CLIENTLESS_CAP_GOSSIP, "gossip");
add(CLIENTLESS_CAP_REPAIR, "repair");
add(CLIENTLESS_CAP_BATTLEMASTER, "battlemaster");
add(CLIENTLESS_CAP_PETITIONER, "petitioner");
add(CLIENTLESS_CAP_SPIRITHEALER, "spirithealer");
add(CLIENTLESS_CAP_SPIRITGUIDE, "spiritguide");
add(CLIENTLESS_CAP_CRITTER, "critter");
add(CLIENTLESS_CAP_CLIENTLESS, "clientless");
return first ? "-" : out.str();
}
ClientlessEntityRelation ClientlessPerceptionMgr::DetermineRelation(Player* observer, Player* target)
{
if (!observer || !target)
return ClientlessEntityRelation::Unknown;
return RelationFromReaction(observer->GetReactionTo(target));
}
ClientlessEntityRelation ClientlessPerceptionMgr::DetermineRelation(Player* observer, Creature* target)
{
if (!observer || !target)
return ClientlessEntityRelation::Unknown;
return RelationFromReaction(observer->GetReactionTo(target));
}
bool ClientlessPerceptionMgr::ScanNow(Player* player, float radius)
{
if (!player || !player->IsInWorld())
return false;
radius = std::max(1.0f, std::min(radius, 100.0f));
uint32 const characterGuid = player->GetGUID().GetCounter();
std::list<WorldObject*> objects;
Trinity::AllWorldObjectsInRange check(player, radius);
Trinity::WorldObjectListSearcher<Trinity::AllWorldObjectsInRange> searcher(player, objects, check);
Cell::VisitAllObjects(player, searcher, radius);
ClientlessPerceptionSnapshot snapshot;
snapshot.Radius = radius;
snapshot.ScanAgeMs = 0;
for (WorldObject* object : objects)
{
if (!object)
continue;
if (Creature* creature = object->ToCreature())
{
ClientlessPerceivedEntity entity;
entity.Type = ClientlessEntityType::Creature;
entity.Guid = creature->GetGUID().GetCounter();
entity.Entry = creature->GetEntry();
entity.Name = creature->GetName();
entity.Distance = player->GetDistance(creature);
entity.MapId = creature->GetMapId();
entity.PositionX = creature->GetPositionX();
entity.PositionY = creature->GetPositionY();
entity.PositionZ = creature->GetPositionZ();
entity.Orientation = creature->GetOrientation();
entity.Level = creature->getLevel();
entity.Health = creature->GetHealth();
entity.MaxHealth = creature->GetMaxHealth();
entity.Alive = creature->IsAlive();
entity.InLineOfSight = player->IsWithinLOSInMap(creature);
entity.Attackable = player->IsValidAttackTarget(creature);
entity.Relation = DetermineRelation(player, creature);
uint64 capabilities = CLIENTLESS_CAP_NONE;
if (creature->IsVendor()) capabilities |= CLIENTLESS_CAP_VENDOR;
if (creature->IsTrainer()) capabilities |= CLIENTLESS_CAP_TRAINER;
if (creature->IsTaxi()) capabilities |= CLIENTLESS_CAP_FLIGHTMASTER;
if (creature->IsBanker()) capabilities |= CLIENTLESS_CAP_BANKER;
if (creature->IsInnkeeper()) capabilities |= CLIENTLESS_CAP_INNKEEPER;
if (creature->IsAuctioner()) capabilities |= CLIENTLESS_CAP_AUCTIONEER;
if (creature->IsQuestGiver()) capabilities |= CLIENTLESS_CAP_QUESTGIVER;
if (creature->IsGossip()) capabilities |= CLIENTLESS_CAP_GOSSIP;
if (creature->IsArmorer()) capabilities |= CLIENTLESS_CAP_REPAIR;
if (creature->IsBattleMaster()) capabilities |= CLIENTLESS_CAP_BATTLEMASTER;
if (creature->IsGuildMaster()) capabilities |= CLIENTLESS_CAP_PETITIONER;
if (creature->IsSpiritHealer()) capabilities |= CLIENTLESS_CAP_SPIRITHEALER;
if (creature->IsSpiritGuide()) capabilities |= CLIENTLESS_CAP_SPIRITGUIDE;
if (creature->IsCritter()) capabilities |= CLIENTLESS_CAP_CRITTER;
entity.Capabilities = capabilities;
snapshot.Entities.push_back(std::move(entity));
continue;
}
if (Player* otherPlayer = object->ToPlayer())
{
if (otherPlayer == player)
continue;
ClientlessPerceivedEntity entity;
entity.Type = ClientlessEntityType::Player;
entity.Guid = otherPlayer->GetGUID().GetCounter();
entity.Name = otherPlayer->GetName();
entity.Distance = player->GetDistance(otherPlayer);
entity.MapId = otherPlayer->GetMapId();
entity.PositionX = otherPlayer->GetPositionX();
entity.PositionY = otherPlayer->GetPositionY();
entity.PositionZ = otherPlayer->GetPositionZ();
entity.Orientation = otherPlayer->GetOrientation();
entity.Level = otherPlayer->getLevel();
entity.Health = otherPlayer->GetHealth();
entity.MaxHealth = otherPlayer->GetMaxHealth();
entity.Alive = otherPlayer->IsAlive();
entity.InLineOfSight = player->IsWithinLOSInMap(otherPlayer);
entity.Attackable = player->IsValidAttackTarget(otherPlayer);
entity.Relation = DetermineRelation(player, otherPlayer);
if (sClientlessPlayerMgr->IsManaged(entity.Guid))
entity.Capabilities |= CLIENTLESS_CAP_CLIENTLESS;
snapshot.Entities.push_back(std::move(entity));
}
}
std::sort(snapshot.Entities.begin(), snapshot.Entities.end(),
[](ClientlessPerceivedEntity const& a, ClientlessPerceivedEntity const& b)
{
return a.Distance < b.Distance;
});
Record& record = _records[characterGuid];
record.Snapshot = std::move(snapshot);
record.ScanTimerMs = SCAN_INTERVAL_MS;
return true;
}
ClientlessPerceptionSnapshot const* ClientlessPerceptionMgr::GetSnapshot(uint32 characterGuid) const
{
auto itr = _records.find(characterGuid);
return itr != _records.end() ? &itr->second.Snapshot : nullptr;
}
bool ClientlessPerceptionMgr::Matches(
ClientlessPerceivedEntity const& entity,
ClientlessPerceptionQuery const& query)
{
if (query.FilterType && entity.Type != query.Type)
return false;
if (query.FilterRelation && entity.Relation != query.Relation)
return false;
if (query.RequiredCapabilities &&
(entity.Capabilities & query.RequiredCapabilities) != query.RequiredCapabilities)
return false;
if (query.ForbiddenCapabilities &&
(entity.Capabilities & query.ForbiddenCapabilities) != 0)
return false;
if (query.RequireAlive && !entity.Alive)
return false;
if (query.RequireAttackable && !entity.Attackable)
return false;
if (query.RequireLineOfSight && !entity.InLineOfSight)
return false;
if (query.MaxDistance > 0.0f && entity.Distance > query.MaxDistance)
return false;
return true;
}
ClientlessPerceivedEntity const* ClientlessPerceptionMgr::FindNearest(
uint32 characterGuid,
ClientlessPerceptionQuery const& query) const
{
ClientlessPerceptionSnapshot const* snapshot = GetSnapshot(characterGuid);
if (!snapshot)
return nullptr;
for (ClientlessPerceivedEntity const& entity : snapshot->Entities)
if (Matches(entity, query))
return &entity;
return nullptr;
}
void ClientlessPerceptionMgr::Update(uint32 diff)
{
for (auto const& [characterGuid, session] : sWorld->GetAllClientlessSessions())
{
if (!session || !sClientlessPlayerMgr->IsManaged(characterGuid))
continue;
Player* player = session->GetPlayer();
if (!player || !player->IsInWorld())
continue;
Record& record = _records[characterGuid];
record.Snapshot.ScanAgeMs += diff;
if (record.ScanTimerMs > diff)
{
record.ScanTimerMs -= diff;
continue;
}
ScanNow(
player,
record.Snapshot.Radius > 0.0f
? record.Snapshot.Radius
: DEFAULT_SCAN_RADIUS);
}
}