Implement PhaseFlags "ObjectsVisible "

Objects in this phase should be visible from others phases but should be interactible
This commit is contained in:
Traesh
2026-04-08 01:15:22 +02:00
parent 20ac3e4a8f
commit 3399d8911b
10 changed files with 130 additions and 13 deletions
@@ -4548,7 +4548,7 @@ GuidUnorderedSet const* GameObject::GetInsidePlayers() const
bool GameObject::MeetsInteractCondition(Player const* user) const
{
return ConditionMgr::IsPlayerMeetingCondition(user, m_goInfo->GetConditionID1());
return IsInPhaseForInteraction(user) && ConditionMgr::IsPlayerMeetingCondition(user, m_goInfo->GetConditionID1());
}
std::unordered_map<ObjectGuid, GameObject::PerPlayerState>& GameObject::GetOrCreatePerPlayerStates()
@@ -992,6 +992,17 @@ bool WorldObject::CanNeverSee(WorldObject const* obj, bool ignorePhaseShift /*=
return GetMap() != obj->GetMap() || (!ignorePhaseShift && !InSamePhase(obj));
}
bool WorldObject::IsInPhaseForInteraction(WorldObject const* obj) const
{
if (!obj)
return false;
if (obj->IsPlayer() && obj->ToPlayer()->IsGameMaster())
return true;
return InSamePhase(obj) && GetPhaseShift().CanInteractWith(obj->GetPhaseShift());
}
bool WorldObject::CanDetect(WorldObject const* obj, bool implicitDetect, bool checkAlert) const
{
WorldObject const* seer = this;
+1
View File
@@ -341,6 +341,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
{
return GetPhaseShift().CanSee(obj->GetPhaseShift());
}
bool IsInPhaseForInteraction(WorldObject const* obj) const;
static bool InSamePhase(WorldObject const* a, WorldObject const* b)
{
return a && b && a->InSamePhase(b);
@@ -341,9 +341,12 @@ class ViewerDependentValue<UF::UnitData::FlagsTag>
public:
using value_type = UF::UnitData::FlagsTag::value_type;
static value_type GetValue(UF::UnitData const* unitData, Player const* receiver, Unit const* /*unit*/)
static value_type GetValue(UF::UnitData const* unitData, Player const* receiver, Unit const* unit)
{
value_type flags = unitData->Flags;
if (!unit->IsInPhaseForInteraction(receiver))
flags |= UNIT_FLAG_UNINTERACTIBLE;
// Gamemasters should be always able to interact with units - remove uninteractible flag
if (receiver->IsGameMaster())
flags &= ~UNIT_FLAG_UNINTERACTIBLE;
@@ -441,6 +444,9 @@ public:
static value_type GetValue(UF::UnitData const* unitData, Player const* receiver, Unit const* unit)
{
value_type interactSpellId = unitData->InteractSpellID;
if (!unit->IsInPhaseForInteraction(receiver))
return 0;
if (unitData->NpcFlags & UNIT_NPC_FLAG_SPELLCLICK && !interactSpellId)
{
// this field is not set if there are multiple available spellclick spells
@@ -473,7 +479,9 @@ public:
value_type npcFlag = unitData->NpcFlags;
if (npcFlag)
{
if ((!unit->IsInteractionAllowedInCombat() && unit->IsInCombat())
if (!unit->IsInPhaseForInteraction(receiver))
npcFlag = 0;
else if ((!unit->IsInteractionAllowedInCombat() && unit->IsInCombat())
|| (!unit->IsInteractionAllowedWhileHostile() && unit->IsHostileTo(receiver)))
npcFlag = 0;
else if (Creature const* creature = unit->ToCreature())
@@ -500,7 +508,9 @@ public:
value_type npcFlag = unitData->NpcFlags2;
if (npcFlag)
{
if ((!unit->IsInteractionAllowedInCombat() && unit->IsInCombat())
if (!unit->IsInPhaseForInteraction(receiver))
npcFlag = 0;
else if ((!unit->IsInteractionAllowedInCombat() && unit->IsInCombat())
|| (!unit->IsInteractionAllowedWhileHostile() && unit->IsHostileTo(receiver)))
npcFlag = 0;
}
@@ -2019,6 +2019,9 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, NPCFlags npcFl
if (!creature->IsWithinDistInMap(this, creature->GetCombatReach() + 4.0f))
return nullptr;
if (!creature->IsInPhaseForInteraction(this))
return nullptr;
return creature;
}
@@ -2058,6 +2061,9 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const
return nullptr;
}
if (!go->IsInPhaseForInteraction(this))
return nullptr;
return go;
}
@@ -25583,6 +25589,54 @@ void Player::UpdateTriggerVisibility()
SendDirectMessage(&packet);
}
void Player::UpdateVisibleObjectPhaseInteractions()
{
if (m_clientGUIDs.empty() || !IsInWorld())
return;
UpdateData udata(GetMapId());
WorldPacket packet;
for (ObjectGuid visibleObjectGuid : m_clientGUIDs)
{
if (visibleObjectGuid.IsCreatureOrVehicle())
{
Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, visibleObjectGuid);
if (!creature)
continue;
UF::ObjectData::Base objMask;
UF::UnitData::Base unitMask;
unitMask.MarkChanged(&UF::UnitData::Flags);
unitMask.MarkChanged(&UF::UnitData::Flags2);
if (creature->m_unitData->NpcFlags)
unitMask.MarkChanged(&UF::UnitData::NpcFlags);
if (creature->m_unitData->NpcFlags2)
unitMask.MarkChanged(&UF::UnitData::NpcFlags2);
if (creature->m_unitData->InteractSpellID || creature->m_unitData->NpcFlags & UNIT_NPC_FLAG_SPELLCLICK)
unitMask.MarkChanged(&UF::UnitData::InteractSpellID);
creature->BuildValuesUpdateForPlayerWithMask(&udata, objMask.GetChangesMask(), unitMask.GetChangesMask(), this, false);
}
else if (visibleObjectGuid.IsAnyTypeGameObject())
{
GameObject* go = GetMap()->GetGameObject(visibleObjectGuid);
if (!go)
continue;
UF::ObjectData::Base objMask;
UF::GameObjectData::Base goMask;
objMask.MarkChanged(&UF::ObjectData::DynamicFlags);
go->BuildValuesUpdateForPlayerWithMask(&udata, objMask.GetChangesMask(), goMask.GetChangesMask(), this, false);
}
}
if (!udata.HasData())
return;
udata.BuildPacket(&packet);
SendDirectMessage(&packet);
}
void Player::SendInitialVisiblePackets(WorldObject* target) const
{
auto sendVignette = [](Vignettes::VignetteData const& vignette, Player const* where)
@@ -32606,6 +32660,7 @@ std::string Player::GetCharacterSelectOutfit() const
void Player::OnPhaseChange()
{
Unit::OnPhaseChange();
UpdateVisibleObjectPhaseInteractions();
GetMap()->UpdatePersonalPhasesForPlayer(this);
}
+1
View File
@@ -2759,6 +2759,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void UpdateVisibilityOf(WorldObject* target);
void UpdateVisibilityOf(Trinity::IteratorPair<WorldObject**> targets);
void UpdateTriggerVisibility();
void UpdateVisibleObjectPhaseInteractions();
template<class T>
void UpdateVisibilityOf(T* target, UpdateData& data, std::set<WorldObject*>& visibleNow);
+9
View File
@@ -12556,6 +12556,15 @@ float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, S
void Unit::OnPhaseChange()
{
ForceUpdateFieldChange(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::Flags));
ForceUpdateFieldChange(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::Flags2));
if (m_unitData->NpcFlags)
ForceUpdateFieldChange(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::NpcFlags));
if (m_unitData->NpcFlags2)
ForceUpdateFieldChange(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::NpcFlags2));
if (m_unitData->InteractSpellID || m_unitData->NpcFlags & UNIT_NPC_FLAG_SPELLCLICK)
ForceUpdateFieldChange(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::InteractSpellID));
}
void Unit::UpdateObjectVisibility(bool forced)
+20 -2
View File
@@ -101,6 +101,7 @@ void PhaseShift::ClearPhases()
NonCosmeticReferences = 0;
CosmeticReferences = 0;
PersonalReferences = 0;
ObjectsVisibleReferences = 0;
DefaultReferences = 0;
UpdateUnphasedFlag();
}
@@ -114,9 +115,9 @@ bool PhaseShift::CanSee(PhaseShift const& other) const
if (Flags.HasFlag(PhaseShiftFlags::Inverse) && other.Flags.HasFlag(PhaseShiftFlags::Inverse))
return true;
PhaseFlags excludePhasesWithFlag = PhaseFlags::None;
PhaseFlags excludePhasesWithFlag = PhaseFlags::ObjectsVisible;
if (Flags.HasFlag(PhaseShiftFlags::NoCosmetic) && other.Flags.HasFlag(PhaseShiftFlags::NoCosmetic))
excludePhasesWithFlag = PhaseFlags::Cosmetic;
excludePhasesWithFlag |= PhaseFlags::Cosmetic;
if (!Flags.HasFlag(PhaseShiftFlags::Inverse) && !other.Flags.HasFlag(PhaseShiftFlags::Inverse))
{
@@ -152,6 +153,21 @@ bool PhaseShift::CanSee(PhaseShift const& other) const
return checkInversePhaseShift(other, *this);
}
bool PhaseShift::CanInteractWith(PhaseShift const& other) const
{
if (!HasObjectsVisiblePhase())
return true;
ObjectGuid ownerGuid = PersonalGuid;
ObjectGuid otherPersonalGuid = other.PersonalGuid;
return Trinity::Containers::Intersects(Phases.begin(), Phases.end(), other.Phases.begin(), other.Phases.end(),
[&ownerGuid, &otherPersonalGuid](PhaseRef const& myPhase, PhaseRef const&)
{
return myPhase.Flags.HasFlag(PhaseFlags::ObjectsVisible)
&& (!myPhase.Flags.HasFlag(PhaseFlags::Personal) || ownerGuid == otherPersonalGuid);
});
}
void PhaseShift::ModifyPhasesReferences(PhaseContainer::iterator itr, int32 references)
{
itr->References += references;
@@ -160,6 +176,8 @@ void PhaseShift::ModifyPhasesReferences(PhaseContainer::iterator itr, int32 refe
{
if (itr->Flags.HasFlag(PhaseFlags::Cosmetic))
CosmeticReferences += references;
if (itr->Flags.HasFlag(PhaseFlags::ObjectsVisible))
ObjectsVisibleReferences += references;
else if (itr->Id != DEFAULT_PHASE)
NonCosmeticReferences += references;
else
+7 -3
View File
@@ -43,9 +43,10 @@ enum class PhaseShiftFlags : uint32
enum class PhaseFlags : uint16
{
None = 0x0,
Cosmetic = 0x1,
Personal = 0x2
None = 0x0,
Cosmetic = 0x1,
Personal = 0x2,
ObjectsVisible = 0x4
};
DEFINE_ENUM_FLAG(PhaseShiftFlags);
@@ -114,8 +115,10 @@ public:
void ClearPhases();
bool CanSee(PhaseShift const& other) const;
bool CanInteractWith(PhaseShift const& other) const;
bool HasPersonalPhase() const;
bool HasObjectsVisiblePhase() const { return ObjectsVisibleReferences > 0; }
protected:
friend class PhasingHandler;
@@ -132,6 +135,7 @@ protected:
int32 NonCosmeticReferences = 0;
int32 CosmeticReferences = 0;
int32 PersonalReferences = 0;
int32 ObjectsVisibleReferences = 0;
int32 DefaultReferences = 0;
bool IsDbPhaseShift = false;
};
+12 -4
View File
@@ -25,6 +25,7 @@
#include "Map.h"
#include "MapUtils.h"
#include "MiscPackets.h"
#include "GameObject.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "PartyPackets.h"
@@ -48,16 +49,20 @@ PhaseShift const AlwaysVisible = []
inline PhaseFlags GetPhaseFlags(uint32 phaseId)
{
PhaseFlags flags = PhaseFlags::None;
if (PhaseEntry const* phase = sPhaseStore.LookupEntry(phaseId))
{
if (phase->GetFlags().HasFlag(PhaseEntryFlags::Cosmetic))
return PhaseFlags::Cosmetic;
flags |= PhaseFlags::Cosmetic;
if (phase->GetFlags().HasFlag(PhaseEntryFlags::Personal))
return PhaseFlags::Personal;
flags |= PhaseFlags::Personal;
if (phase->GetFlags().HasFlag(PhaseEntryFlags::ObjectsAreVisible))
flags |= PhaseFlags::ObjectsVisible;
}
return PhaseFlags::None;
return flags;
}
}
@@ -549,7 +554,8 @@ void PhasingHandler::InitDbPhaseShift(PhaseShift& phaseShift, uint8 phaseUseFlag
for (uint32 phaseInGroup : *phasesInGroup)
phaseShift.AddPhase(phaseInGroup, GetPhaseFlags(phaseInGroup), nullptr);
if (phaseShift.Phases.empty() || phaseShift.HasPhase(DEFAULT_PHASE))
if (phaseShift.Phases.empty() || phaseShift.HasPhase(DEFAULT_PHASE)
|| std::ranges::all_of(phaseShift.Phases, [](PhaseShift::PhaseRef const& phase) { return phase.Flags.HasFlag(PhaseFlags::ObjectsVisible); }))
{
if (flags.HasFlag(PhaseShiftFlags::Inverse))
flags |= PhaseShiftFlags::InverseUnphased;
@@ -697,6 +703,8 @@ void PhasingHandler::UpdateVisibilityIfNeeded(WorldObject* object, bool updateVi
{
if (Player* player = object->ToPlayer())
SendToPlayer(player);
else if (GameObject* gameObject = object->ToGameObject())
gameObject->UpdateDynamicFlagsForNearbyPlayers();
if (updateVisibility)
{