feat(Core/Conditions): Add more support for object visibility (#25415)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
sogladev 2026-04-12 22:56:33 +02:00 committed by GitHub
parent 62eb179385
commit 70ea8ab00b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 152 additions and 20 deletions

View file

@ -836,7 +836,7 @@ uint32 Condition::GetMaxAvailableConditionTargets()
case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION:
case CONDITION_SOURCE_TYPE_NPC_VENDOR:
case CONDITION_SOURCE_TYPE_SPELL_PROC:
case CONDITION_SOURCE_TYPE_CREATURE_VISIBILITY:
case CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY:
return 2;
default:
break;
@ -975,12 +975,12 @@ bool ConditionMgr::CanHaveSourceGroupSet(ConditionSourceType sourceType) const
{
return (sourceType == CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE ||
sourceType == CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_GOSSIP_MENU || sourceType == CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION || sourceType == CONDITION_SOURCE_TYPE_VEHICLE_SPELL || sourceType == CONDITION_SOURCE_TYPE_GOSSIP_HELLO ||
sourceType == CONDITION_SOURCE_TYPE_SPELL_IMPLICIT_TARGET || sourceType == CONDITION_SOURCE_TYPE_SPELL_CLICK_EVENT || sourceType == CONDITION_SOURCE_TYPE_SMART_EVENT || sourceType == CONDITION_SOURCE_TYPE_NPC_VENDOR || sourceType == CONDITION_SOURCE_TYPE_PLAYER_LOOT_TEMPLATE);
sourceType == CONDITION_SOURCE_TYPE_SPELL_IMPLICIT_TARGET || sourceType == CONDITION_SOURCE_TYPE_SPELL_CLICK_EVENT || sourceType == CONDITION_SOURCE_TYPE_SMART_EVENT || sourceType == CONDITION_SOURCE_TYPE_NPC_VENDOR || sourceType == CONDITION_SOURCE_TYPE_PLAYER_LOOT_TEMPLATE || sourceType == CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY);
}
bool ConditionMgr::CanHaveSourceIdSet(ConditionSourceType sourceType) const
{
return (sourceType == CONDITION_SOURCE_TYPE_SMART_EVENT);
return (sourceType == CONDITION_SOURCE_TYPE_SMART_EVENT || sourceType == CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY);
}
ConditionList ConditionMgr::GetConditionsForNotGroupedEntry(ConditionSourceType sourceType, uint32 entry)
@ -1073,6 +1073,43 @@ ConditionList ConditionMgr::GetConditionsForNpcVendorEvent(uint32 creatureId, ui
return cond;
}
ConditionList ConditionMgr::GetConditionsForObjectVisibility(const WorldObject* object) const
{
ConditionList cond;
if (!object->IsCreature() && !object->IsGameObject())
return cond;
uint32 entry = object->GetEntry();
uint32 sourceGroup = object->IsGameObject() ? 1 : 0;
auto itrBucket = ObjectVisibilityConditionStore.find(std::make_pair(entry, sourceGroup));
if (itrBucket == ObjectVisibilityConditionStore.end())
return cond;
uint32 guid = object->IsGameObject() ? object->ToGameObject()->GetSpawnId() : object->ToCreature()->GetSpawnId();
auto const& sourceIdConditions = itrBucket->second;
auto itrGuid = sourceIdConditions.find(guid);
if (itrGuid != sourceIdConditions.end())
{
cond.insert(cond.end(), itrGuid->second.begin(), itrGuid->second.end());
LOG_DEBUG("condition", "GetConditionsForObjectVisibility: found guid-level conditions for sourceGroup {} entry {} guid {}", sourceGroup, entry, guid);
}
else
{
auto itrEntry = sourceIdConditions.find(0);
if (itrEntry != sourceIdConditions.end())
{
cond.insert(cond.end(), itrEntry->second.begin(), itrEntry->second.end());
LOG_DEBUG("condition", "GetConditionsForObjectVisibility: found entry-level conditions for sourceGroup {} entry {}", sourceGroup, entry);
}
}
return cond;
}
void ConditionMgr::LoadConditions(bool isReload)
{
uint32 oldMSTime = getMSTime();
@ -1223,7 +1260,8 @@ void ConditionMgr::LoadConditions(bool isReload)
cond->ErrorTextId = 0;
}
if (cond->SourceGroup || cond->SourceType == CONDITION_SOURCE_TYPE_PLAYER_LOOT_TEMPLATE)
if (cond->SourceGroup || cond->SourceType == CONDITION_SOURCE_TYPE_PLAYER_LOOT_TEMPLATE
|| cond->SourceType == CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY)
{
bool valid = false;
// handle grouped conditions
@ -1304,6 +1342,13 @@ void ConditionMgr::LoadConditions(bool isReload)
++count;
continue;
}
case CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY:
{
ObjectVisibilityConditionStore[std::make_pair(uint32(cond->SourceEntry), cond->SourceGroup)][cond->SourceId].push_back(cond);
valid = true;
++count;
continue; // do not add to AllocatedMemoryStore to avoid double-deleting
}
case CONDITION_SOURCE_TYPE_PLAYER_LOOT_TEMPLATE:
{
valid = addToLootTemplate(cond, LootTemplates_Player.GetLootForConditionFill(cond->SourceGroup));
@ -1866,6 +1911,68 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond)
}
break;
}
case CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY:
{
if (cond->SourceGroup > 1)
{
LOG_ERROR("sql.sql", "CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY has invalid SourceGroup {} for SourceEntry {}, expected 0 (creature) or 1 (gameobject)", cond->SourceGroup, cond->SourceEntry);
return false;
}
if (cond->SourceEntry <= 0)
{
LOG_ERROR("sql.sql", "CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY has invalid SourceEntry {}, expected a positive entry id.", cond->SourceEntry);
return false;
}
if (cond->SourceGroup == 0 && !sObjectMgr->GetCreatureTemplate(uint32(cond->SourceEntry)))
{
LOG_ERROR("sql.sql", "CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY points to non-existing creature entry {}, skipped.", cond->SourceEntry);
return false;
}
if (cond->SourceGroup == 1 && !sObjectMgr->GetGameObjectTemplate(uint32(cond->SourceEntry)))
{
LOG_ERROR("sql.sql", "CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY points to non-existing gameobject entry {}, skipped.", cond->SourceEntry);
return false;
}
if (cond->SourceId)
{
if (cond->SourceGroup == 0)
{
CreatureData const* data = sObjectMgr->GetCreatureData(cond->SourceId);
if (!data)
{
LOG_ERROR("sql.sql", "CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY points to non-existing creature guid {}, skipped.", cond->SourceId);
return false;
}
if (data->id1 != uint32(cond->SourceEntry))
{
LOG_ERROR("sql.sql", "CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY has creature guid {} that does not match SourceEntry {}, skipped.", cond->SourceId, cond->SourceEntry);
return false;
}
}
else
{
GameObjectData const* data = sObjectMgr->GetGameObjectData(cond->SourceId);
if (!data)
{
LOG_ERROR("sql.sql", "CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY points to non-existing gameobject guid {}, skipped.", cond->SourceId);
return false;
}
if (data->id != uint32(cond->SourceEntry))
{
LOG_ERROR("sql.sql", "CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY has gameobject guid {} that does not match SourceEntry {}, skipped.", cond->SourceId, cond->SourceEntry);
return false;
}
}
}
break;
}
case CONDITION_SOURCE_TYPE_GOSSIP_MENU:
case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION:
case CONDITION_SOURCE_TYPE_SMART_EVENT:
@ -2593,6 +2700,21 @@ void ConditionMgr::Clean()
NpcVendorConditionContainerStore.clear();
for (auto& itr : ObjectVisibilityConditionStore)
{
for (auto& sourceIdConds : itr.second)
{
for (Condition* cond : sourceIdConds.second)
delete cond;
sourceIdConds.second.clear();
}
itr.second.clear();
}
ObjectVisibilityConditionStore.clear();
// this is a BIG hack, feel free to fix it if you can figure out the ConditionMgr ;)
for (std::list<Condition*>::const_iterator itr = AllocatedMemoryStore.begin(); itr != AllocatedMemoryStore.end(); ++itr) delete *itr;

View file

@ -152,7 +152,7 @@ enum ConditionSourceType
CONDITION_SOURCE_TYPE_GRAVEYARD = 27, // don't use on 3.3.5a
CONDITION_SOURCE_TYPE_PLAYER_LOOT_TEMPLATE = 28,
CONDITION_SOURCE_TYPE_CREATURE_RESPAWN = 29,
CONDITION_SOURCE_TYPE_CREATURE_VISIBILITY = 30,
CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY = 30,
CONDITION_SOURCE_TYPE_MAX = 31 // placeholder
};
@ -198,7 +198,7 @@ struct Condition
ConditionSourceType SourceType; //SourceTypeOrReferenceId
uint32 SourceGroup;
int32 SourceEntry;
uint32 SourceId; // So far, only used in CONDITION_SOURCE_TYPE_SMART_EVENT
uint32 SourceId; // Used in CONDITION_SOURCE_TYPE_SMART_EVENT and CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY
uint32 ElseGroup;
ConditionTypes ConditionType; //ConditionTypeOrReference
uint32 ConditionValue1;
@ -242,6 +242,7 @@ typedef std::map<ConditionSourceType, ConditionTypeContainer> ConditionContainer
typedef std::map<uint32, ConditionTypeContainer> CreatureSpellConditionContainer;
typedef std::map<uint32, ConditionTypeContainer> NpcVendorConditionContainer;
typedef std::map<std::pair<int32, uint32 /*SAI source_type*/>, ConditionTypeContainer> SmartEventConditionContainer;
typedef std::map<std::pair<uint32 /*SourceEntry*/, uint32 /*SourceGroup*/>, std::map<uint32 /*SourceId*/, ConditionList>> ObjectVisibilityConditionContainer;
typedef std::map<uint32, ConditionList> ConditionReferenceContainer;//only used for references
@ -269,6 +270,7 @@ public:
ConditionList GetConditionsForSmartEvent(int32 entryOrGuid, uint32 eventId, uint32 sourceType);
ConditionList GetConditionsForVehicleSpell(uint32 creatureId, uint32 spellId);
ConditionList GetConditionsForNpcVendorEvent(uint32 creatureId, uint32 itemId);
ConditionList GetConditionsForObjectVisibility(const WorldObject* object) const;
private:
bool isSourceTypeValid(Condition* cond);
@ -281,12 +283,13 @@ private:
void Clean(); // free up resources
std::list<Condition*> AllocatedMemoryStore; // some garbage collection :)
ConditionContainer ConditionStore;
ConditionReferenceContainer ConditionReferenceStore;
CreatureSpellConditionContainer VehicleSpellConditionStore;
CreatureSpellConditionContainer SpellClickEventConditionStore;
NpcVendorConditionContainer NpcVendorConditionContainerStore;
SmartEventConditionContainer SmartEventConditionStore;
ConditionContainer ConditionStore;
ConditionReferenceContainer ConditionReferenceStore;
CreatureSpellConditionContainer VehicleSpellConditionStore;
CreatureSpellConditionContainer SpellClickEventConditionStore;
NpcVendorConditionContainer NpcVendorConditionContainerStore;
SmartEventConditionContainer SmartEventConditionStore;
ObjectVisibilityConditionContainer ObjectVisibilityConditionStore;
};
#define sConditionMgr ConditionMgr::instance()

View file

@ -1770,24 +1770,23 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
if (Player const* player = ToPlayer())
{
if (cObj->IsAIEnabled && !cObj->AI()->CanBeSeen(player))
{
return false;
}
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_VISIBILITY, cObj->GetEntry());
if (!sConditionMgr->IsObjectMeetToConditions((WorldObject*)this, (WorldObject*)obj, conditions))
{
if (!player->CanSeeObjectByVisibilityConditions(obj))
return false;
}
}
}
// Gameobject scripts
if (GameObject const* goObj = obj->ToGameObject())
{
if (ToPlayer() && !goObj->AI()->CanBeSeen(ToPlayer()))
if (Player const* player = ToPlayer())
{
return false;
if (!goObj->AI()->CanBeSeen(player))
return false;
if (!player->CanSeeObjectByVisibilityConditions(obj))
return false;
}
}

View file

@ -14338,6 +14338,13 @@ bool Player::CanSeeSpellClickOn(Creature const* c) const
return false;
}
bool Player::CanSeeObjectByVisibilityConditions(WorldObject const* object) const
{
ConditionList conds = sConditionMgr->GetConditionsForObjectVisibility(object);
ConditionSourceInfo info = ConditionSourceInfo(const_cast<Player*>(this), const_cast<WorldObject*>(object));
return sConditionMgr->IsObjectMeetToConditions(info, conds);
}
/**
* @brief Checks if any vendor option is available in the gossip menu tree for a given creature.
*

View file

@ -2581,6 +2581,7 @@ public:
//bool isActiveObject() const { return true; }
bool CanSeeSpellClickOn(Creature const* creature) const;
[[nodiscard]] bool CanSeeObjectByVisibilityConditions(WorldObject const* object) const;
[[nodiscard]] bool CanSeeVendor(Creature const* creature) const;
[[nodiscard]] bool CanSeeTrainer(Creature const* creature) const;