aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp14
-rw-r--r--src/server/game/Chat/ChatCommands/ChatCommandArgs.h7
-rw-r--r--src/server/game/Chat/HyperlinkTags.cpp250
-rw-r--r--src/server/game/Chat/Hyperlinks.cpp435
-rw-r--r--src/server/game/Chat/Hyperlinks.h231
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h5
6 files changed, 903 insertions, 39 deletions
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
index 20893d9df90..340a13bd15d 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
+++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
@@ -36,6 +36,20 @@ char const* Trinity::ChatCommands::ArgInfo<AchievementEntry const*>::TryConsume(
return args;
}
+struct CurrencyTypesVisitor
+{
+ using value_type = CurrencyTypesEntry const*;
+ value_type operator()(Hyperlink<currency> currency) const { return currency->Currency; }
+ value_type operator()(uint32 currencyId) const { return sCurrencyTypesStore.LookupEntry(currencyId); }
+};
+char const* Trinity::ChatCommands::ArgInfo<CurrencyTypesEntry const*>::TryConsume(CurrencyTypesEntry const*& data, char const* args)
+{
+ Variant <Hyperlink<currency>, uint32> val;
+ if ((args = CommandArgsConsumerSingle<decltype(val)>::TryConsumeTo(val, args)))
+ data = boost::apply_visitor(CurrencyTypesVisitor(), val);
+ return args;
+}
+
struct GameTeleVisitor
{
using value_type = GameTele const*;
diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
index 110103ebb83..db5deddb1dc 100644
--- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
+++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
@@ -116,6 +116,13 @@ struct TC_GAME_API ArgInfo<AchievementEntry const*>
static char const* TryConsume(AchievementEntry const*&, char const*);
};
+// CurrencyTypesEntry* from numeric id or link
+template <>
+struct TC_GAME_API ArgInfo<CurrencyTypesEntry const*>
+{
+ static char const* TryConsume(CurrencyTypesEntry const*&, char const*);
+};
+
// GameTele* from string name or link
template <>
struct TC_GAME_API ArgInfo<GameTele const*>
diff --git a/src/server/game/Chat/HyperlinkTags.cpp b/src/server/game/Chat/HyperlinkTags.cpp
index 194ad874228..dafe9cc8c73 100644
--- a/src/server/game/Chat/HyperlinkTags.cpp
+++ b/src/server/game/Chat/HyperlinkTags.cpp
@@ -74,13 +74,130 @@ bool Trinity::Hyperlinks::LinkTags::achievement::StoreTo(AchievementLinkData& va
t.TryConsumeTo(val.Criteria[1]) && t.TryConsumeTo(val.Criteria[2]) && t.TryConsumeTo(val.Criteria[3]) && t.IsEmpty();
}
+bool Trinity::Hyperlinks::LinkTags::apower::StoreTo(ArtifactPowerLinkData& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 artifactPowerId;
+ if (!(t.TryConsumeTo(artifactPowerId) && t.TryConsumeTo(val.PurchasedRank) && t.TryConsumeTo(val.CurrentRankWithBonus) && t.IsEmpty()))
+ return false;
+ if (!sArtifactPowerStore.LookupEntry(artifactPowerId))
+ return false;
+ val.ArtifactPower = sDB2Manager.GetArtifactPowerRank(artifactPowerId, std::max<uint8>(val.CurrentRankWithBonus, 1));
+ if (val.ArtifactPower)
+ return false;
+ return true;
+}
+
+bool Trinity::Hyperlinks::LinkTags::azessence::StoreTo(AzeriteEssenceLinkData& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 azeriteEssenceId;
+ if (!t.TryConsumeTo(azeriteEssenceId))
+ return false;
+ return (val.Essence = sAzeriteEssenceStore.LookupEntry(azeriteEssenceId)) && t.TryConsumeTo(val.Rank)
+ && sDB2Manager.GetAzeriteEssencePower(azeriteEssenceId, val.Rank) && t.IsEmpty();
+}
+
+bool Trinity::Hyperlinks::LinkTags::battlepet::StoreTo(BattlePetLinkData& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 battlePetSpeciesId;
+ if (!t.TryConsumeTo(battlePetSpeciesId))
+ return false;
+ return (val.Species = sBattlePetSpeciesStore.LookupEntry(battlePetSpeciesId)) && t.TryConsumeTo(val.Level)
+ && t.TryConsumeTo(val.Quality) && val.Quality < MAX_ITEM_QUALITY
+ && t.TryConsumeTo(val.MaxHealth) && t.TryConsumeTo(val.Power) && t.TryConsumeTo(val.Speed)
+ && t.TryConsumeTo(val.PetGuid) && val.PetGuid.GetHigh() == HighGuid::BattlePet && t.TryConsumeTo(val.DisplayId);
+}
+
+bool Trinity::Hyperlinks::LinkTags::conduit::StoreTo(SoulbindConduitRankEntry const*& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 soulbindConduitId, rank;
+ if (!(t.TryConsumeTo(soulbindConduitId) && t.TryConsumeTo(rank) && t.IsEmpty()))
+ return false;
+ return !!(val = sDB2Manager.GetSoulbindConduitRank(soulbindConduitId, rank));
+}
+
+bool Trinity::Hyperlinks::LinkTags::currency::StoreTo(CurrencyLinkData& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 currencyId;
+ if (!t.TryConsumeTo(currencyId))
+ return false;
+ val.Currency = sCurrencyTypesStore.LookupEntry(currencyId);
+ if (!val.Currency || !t.TryConsumeTo(val.Quantity) || !t.IsEmpty())
+ return false;
+ val.Container = sDB2Manager.GetCurrencyContainerForCurrencyQuantity(currencyId, val.Quantity);
+ return true;
+}
+
bool Trinity::Hyperlinks::LinkTags::enchant::StoreTo(SpellInfo const*& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 spellId;
if (!(t.TryConsumeTo(spellId) && t.IsEmpty()))
return false;
- return (val = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE)) && val->HasAttribute(SPELL_ATTR0_TRADESPELL);
+ return !!(val = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE)) && val->HasAttribute(SPELL_ATTR0_TRADESPELL);
+}
+
+bool Trinity::Hyperlinks::LinkTags::garrfollower::StoreTo(GarrisonFollowerLinkData& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 garrFollowerId;
+ if (!t.TryConsumeTo(garrFollowerId))
+ return false;
+
+ val.Follower = sGarrFollowerStore.LookupEntry(garrFollowerId);
+ if (!val.Follower || !t.TryConsumeTo(val.Quality) || val.Quality >= MAX_ITEM_QUALITY || !t.TryConsumeTo(val.Level) || !t.TryConsumeTo(val.ItemLevel)
+ || !t.TryConsumeTo(val.Abilities[0]) || !t.TryConsumeTo(val.Abilities[1]) || !t.TryConsumeTo(val.Abilities[2]) || !t.TryConsumeTo(val.Abilities[3])
+ || !t.TryConsumeTo(val.Traits[0]) || !t.TryConsumeTo(val.Traits[1]) || !t.TryConsumeTo(val.Traits[2]) || !t.TryConsumeTo(val.Traits[3])
+ || !t.TryConsumeTo(val.Specialization))
+ return false;
+
+ for (uint32 ability : val.Abilities)
+ if (ability && !sGarrAbilityStore.LookupEntry(ability))
+ return false;
+
+ for (uint32 trait : val.Traits)
+ if (trait && !sGarrAbilityStore.LookupEntry(trait))
+ return false;
+
+ if (val.Specialization && !sGarrAbilityStore.LookupEntry(val.Specialization))
+ return false;
+
+ return true;
+}
+
+bool Trinity::Hyperlinks::LinkTags::garrfollowerability::StoreTo(GarrAbilityEntry const*& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 garrAbilityId;
+ if (!t.TryConsumeTo(garrAbilityId))
+ return false;
+ return !!(val = sGarrAbilityStore.LookupEntry(garrAbilityId)) && t.IsEmpty();
+}
+
+bool Trinity::Hyperlinks::LinkTags::garrmission::StoreTo(GarrisonMissionLinkData& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 garrMissionId;
+ if (!t.TryConsumeTo(garrMissionId))
+ return false;
+ return !!(val.Mission = sGarrMissionStore.LookupEntry(garrMissionId)) && t.TryConsumeTo(val.DbID) && t.IsEmpty();
+}
+
+bool Trinity::Hyperlinks::LinkTags::instancelock::StoreTo(InstanceLockLinkData& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ if (!t.TryConsumeTo(val.Owner))
+ return false;
+ uint32 mapId;
+ if (!t.TryConsumeTo(mapId))
+ return false;
+ return !!(val.Map = sMapStore.LookupEntry(mapId))
+ && t.TryConsumeTo(val.Difficulty) && sDB2Manager.GetMapDifficultyData(mapId, Difficulty(val.Difficulty))
+ && t.TryConsumeTo(val.CompletedEncountersMask) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::item::StoreTo(ItemLinkData& val, char const* pos, size_t len)
@@ -143,6 +260,92 @@ bool Trinity::Hyperlinks::LinkTags::item::StoreTo(ItemLinkData& val, char const*
return t.TryConsumeTo(val.Creator) && t.TryConsumeTo(val.UseEnchantId) && t.IsEmpty();
}
+bool Trinity::Hyperlinks::LinkTags::journal::StoreTo(JournalLinkData& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 id;
+ if (!t.TryConsumeTo(val.Type) || !t.TryConsumeTo(id) || !t.TryConsumeTo(val.Difficulty) || !t.IsEmpty())
+ return false;
+ switch (JournalLinkData::Types(val.Type))
+ {
+ case JournalLinkData::Types::Instance:
+ {
+ JournalInstanceEntry const* instance = sJournalInstanceStore.LookupEntry(id);
+ if (!instance)
+ return false;
+ val.ExpectedText = &instance->Name;
+ break;
+ }
+ case JournalLinkData::Types::Encounter:
+ {
+ JournalEncounterEntry const* encounter = sJournalEncounterStore.LookupEntry(id);
+ if (!encounter)
+ return false;
+ val.ExpectedText = &encounter->Name;
+ break;
+ }
+ case JournalLinkData::Types::EncounterSection:
+ {
+ JournalEncounterSectionEntry const* encounterSection = sJournalEncounterSectionStore.LookupEntry(id);
+ if (!encounterSection)
+ return false;
+ val.ExpectedText = &encounterSection->Title;
+ break;
+ }
+ case JournalLinkData::Types::Tier:
+ {
+ JournalTierEntry const* tier = sDB2Manager.GetJournalTier(id);
+ if (!tier)
+ return false;
+ val.ExpectedText = &tier->Name;
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool Trinity::Hyperlinks::LinkTags::keystone::StoreTo(KeystoneLinkData& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 mapChallengeModeId;
+ if (!t.TryConsumeTo(val.ItemId) || !t.TryConsumeTo(mapChallengeModeId) || !t.TryConsumeTo(val.Level)
+ || !t.TryConsumeTo(val.Affix[0]) || !t.TryConsumeTo(val.Affix[1]) || !t.TryConsumeTo(val.Affix[2]) || !t.TryConsumeTo(val.Affix[3])
+ || !t.IsEmpty())
+ return false;
+ val.Map = sMapChallengeModeStore.LookupEntry(mapChallengeModeId);
+ if (!val.Map)
+ return false;
+ ItemTemplate const* item = sObjectMgr->GetItemTemplate(val.ItemId);
+ if (!item || item->GetClass() != ITEM_CLASS_REAGENT || item->GetSubClass() != ITEM_SUBCLASS_KEYSTONE)
+ return false;
+ for (uint32 keystoneAffix : val.Affix)
+ if (keystoneAffix && !sKeystoneAffixStore.LookupEntry(keystoneAffix))
+ return false;
+ return true;
+}
+
+bool Trinity::Hyperlinks::LinkTags::mawpower::StoreTo(MawPowerEntry const*& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 mawPowerId;
+ if (!t.TryConsumeTo(mawPowerId))
+ return false;
+ return !!(val = sMawPowerStore.LookupEntry(mawPowerId)) && t.IsEmpty();
+}
+
+bool Trinity::Hyperlinks::LinkTags::pvptal::StoreTo(PvpTalentEntry const*& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 pvpTalentId;
+ if (!(t.TryConsumeTo(pvpTalentId) && t.IsEmpty()))
+ return false;
+ if (!(val = sPvpTalentStore.LookupEntry(pvpTalentId)))
+ return false;
+ return true;
+}
+
bool Trinity::Hyperlinks::LinkTags::quest::StoreTo(QuestLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
@@ -185,3 +388,48 @@ bool Trinity::Hyperlinks::LinkTags::trade::StoreTo(TradeskillLinkData& val, char
return false;
return true;
}
+
+bool Trinity::Hyperlinks::LinkTags::transmogappearance::StoreTo(ItemModifiedAppearanceEntry const*& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 itemModifiedAppearanceId;
+ if (!t.TryConsumeTo(itemModifiedAppearanceId))
+ return false;
+ return !!(val = sItemModifiedAppearanceStore.LookupEntry(itemModifiedAppearanceId)) && t.IsEmpty();
+}
+
+bool Trinity::Hyperlinks::LinkTags::transmogillusion::StoreTo(SpellItemEnchantmentEntry const*& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 spellItemEnchantmentId;
+ if (!t.TryConsumeTo(spellItemEnchantmentId))
+ return false;
+ return !!(val = sSpellItemEnchantmentStore.LookupEntry(spellItemEnchantmentId))
+ && sDB2Manager.GetTransmogIllusionForEnchantment(spellItemEnchantmentId) && t.IsEmpty();
+}
+
+bool Trinity::Hyperlinks::LinkTags::transmogset::StoreTo(TransmogSetEntry const*& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 transmogSetId;
+ if (!t.TryConsumeTo(transmogSetId))
+ return false;
+ return !!(val = sTransmogSetStore.LookupEntry(transmogSetId)) && t.IsEmpty();
+}
+
+bool Trinity::Hyperlinks::LinkTags::worldmap::StoreTo(WorldMapLinkData& val, char const* pos, size_t len)
+{
+ HyperlinkDataTokenizer t(pos, len);
+ uint32 uiMapId;
+ if (!t.TryConsumeTo(uiMapId))
+ return false;
+ val.UiMap = sUiMapStore.LookupEntry(uiMapId);
+ if (!val.UiMap || !t.TryConsumeTo(val.X) || !t.TryConsumeTo(val.Y))
+ return false;
+ if (t.IsEmpty())
+ return true;
+ val.Z.emplace();
+ if (!t.TryConsumeTo(*val.Z))
+ return false;
+ return t.IsEmpty();
+}
diff --git a/src/server/game/Chat/Hyperlinks.cpp b/src/server/game/Chat/Hyperlinks.cpp
index f66df9b4348..08d1bd37ea3 100644
--- a/src/server/game/Chat/Hyperlinks.cpp
+++ b/src/server/game/Chat/Hyperlinks.cpp
@@ -25,6 +25,7 @@
#include "SharedDefines.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
+#include "StringFormat.h"
#include "World.h"
#include "advstd.h"
@@ -99,6 +100,49 @@ static bool equal_with_len(char const* str1, char const* str2, size_t len)
return !len && !*str1;
}
+static bool IsCreatureNameValid(uint32 creatureId, char const* pos, size_t len)
+{
+ if (CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(creatureId))
+ {
+ CreatureLocale const* locale = sObjectMgr->GetCreatureLocale(creatureId);
+ if (!locale)
+ return equal_with_len(creatureTemplate->Name.c_str(), pos, len);
+
+ for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
+ {
+ std::string const& name = (i == DEFAULT_LOCALE) ? creatureTemplate->Name : locale->Name[i];
+ if (name.empty())
+ continue;
+ if (equal_with_len(name.c_str(), pos, len))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <>
+struct LinkValidator<LinkTags::spell>
+{
+ static bool IsTextValid(SpellLinkData const& data, char const* pos, size_t len)
+ {
+ return IsTextValid(data.Spell, pos, len);
+ }
+
+ static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len((*info->SpellName)[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(SpellLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_SPELL;
+ }
+};
+
template <>
struct LinkValidator<LinkTags::achievement>
{
@@ -119,6 +163,186 @@ struct LinkValidator<LinkTags::achievement>
};
template <>
+struct LinkValidator<LinkTags::apower>
+{
+ static bool IsTextValid(ArtifactPowerLinkData const& data, char const* pos, size_t len)
+ {
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(data.ArtifactPower->SpellID, DIFFICULTY_NONE))
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return false;
+ }
+
+ static bool IsColorValid(ArtifactPowerLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_ARTIFACT_POWER;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::azessence>
+{
+ static bool IsTextValid(AzeriteEssenceLinkData const& data, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(data.Essence->Name[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(AzeriteEssenceLinkData const& data, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[data.Rank + 1];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::battlepet>
+{
+ static bool IsTextValid(BattlePetLinkData const& data, char const* pos, size_t len)
+ {
+ return IsCreatureNameValid(data.Species->CreatureID, pos, len);
+ }
+
+ static bool IsColorValid(BattlePetLinkData const& data, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[data.Quality];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::conduit>
+{
+ static bool IsTextValid(SoulbindConduitRankEntry const* rank, char const* pos, size_t len)
+ {
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(rank->SpellID, DIFFICULTY_NONE))
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return false;
+ }
+
+ static bool IsColorValid(SoulbindConduitRankEntry const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_SPELL;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::currency>
+{
+ static bool IsTextValid(CurrencyLinkData const& data, char const* pos, size_t len)
+ {
+ LocalizedString const* name = data.Container ? &data.Container->ContainerName : &data.Currency->Name;
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len((*name)[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(CurrencyLinkData const& data, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[(data.Container ? data.Container->ContainerQuality : data.Currency->Quality)];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::enchant>
+{
+ static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len)
+ {
+ SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(info->Id);
+ if (bounds.first == bounds.second)
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+
+ SkillLineEntry const* skill = sSkillLineStore.LookupEntry(bounds.first->second->SkillupSkillLineID
+ ? bounds.first->second->SkillupSkillLineID
+ : bounds.first->second->SkillLine);
+ if (!skill)
+ return false;
+
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ {
+ char const* skillName = skill->DisplayName[i];
+ size_t skillLen = strlen(skillName);
+ if (len > skillLen + 2 && // or of form [Skill Name: Spell Name]
+ !strncmp(pos, skillName, skillLen) && !strncmp(pos + skillLen, ": ", 2) &&
+ equal_with_len((*info->SpellName)[i], pos + (skillLen + 2), len - (skillLen + 2)))
+ return true;
+ }
+ return false;
+ }
+
+ static bool IsColorValid(SpellInfo const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_ENCHANT;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::garrfollower>
+{
+ static bool IsTextValid(GarrisonFollowerLinkData const& data, char const* pos, size_t len)
+ {
+ return IsCreatureNameValid(data.Follower->HordeCreatureID, pos, len)
+ || IsCreatureNameValid(data.Follower->AllianceCreatureID, pos, len);
+ }
+
+ static bool IsColorValid(GarrisonFollowerLinkData const& data, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[data.Quality];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::garrfollowerability>
+{
+ static bool IsTextValid(GarrAbilityEntry const* ability, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(ability->Name[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(GarrAbilityEntry const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_GARR_ABILITY;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::garrmission>
+{
+ static bool IsTextValid(GarrisonMissionLinkData const& data, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(data.Mission->Name[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(GarrisonMissionLinkData const&, HyperlinkColor c)
+ {
+ return c == QuestDifficultyColors[2];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::instancelock>
+{
+ static bool IsTextValid(InstanceLockLinkData const& data, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(data.Map->MapName[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(InstanceLockLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_INSTANCE_LOCK;
+ }
+};
+
+template <>
struct LinkValidator<LinkTags::item>
{
static bool IsTextValid(ItemLinkData const& data, char const* pos, size_t len)
@@ -127,17 +351,22 @@ struct LinkValidator<LinkTags::item>
if (!(data.Item->GetFlags3() & ITEM_FLAG3_HIDE_NAME_SUFFIX) && data.Suffix)
suffixStrings = &data.Suffix->Description;
+ return IsTextValid(data.Item, suffixStrings, pos, len);
+ }
+
+ static bool IsTextValid(ItemTemplate const* itemTemplate, LocalizedString const* suffixStrings, char const* pos, size_t len)
+ {
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
{
- std::string name = data.Item->GetName(i);
+ std::string name = itemTemplate->GetName(i);
if (name.empty())
continue;
if (suffixStrings)
{
if (len > name.length() + 1 &&
- (strncmp(name.c_str(), pos, name.length()) == 0) &&
- (*(pos + name.length()) == ' ') &&
- equal_with_len((*suffixStrings)[i], pos + name.length() + 1, len - name.length() - 1))
+ (strncmp(name.c_str(), pos, name.length()) == 0) &&
+ (*(pos + name.length()) == ' ') &&
+ equal_with_len((*suffixStrings)[i], pos + name.length() + 1, len - name.length() - 1))
return true;
}
else if (equal_with_len(name.c_str(), pos, len))
@@ -153,6 +382,52 @@ struct LinkValidator<LinkTags::item>
};
template <>
+struct LinkValidator<LinkTags::journal>
+{
+ static bool IsTextValid(JournalLinkData const& data, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len((*data.ExpectedText)[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(JournalLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_JOURNAL;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::keystone>
+{
+ static bool IsTextValid(KeystoneLinkData const& data, char const* pos, size_t len)
+ {
+ // Skip "Keystone" prefix - not loading GlobalStrings.db2
+ char const* validateStartPos = strstr(pos, ": ");
+ if (!validateStartPos)
+ return false;
+
+ // skip ": " too
+ validateStartPos += 2;
+ size_t validateLen = len - (validateStartPos - pos);
+
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ {
+ std::string expectedText = Trinity::StringFormat("%s (%u)", data.Map->Name[i], data.Level);
+ if (equal_with_len(expectedText.c_str(), validateStartPos, validateLen))
+ return true;
+ }
+ return false;
+ }
+
+ static bool IsColorValid(KeystoneLinkData const&, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[ITEM_QUALITY_EPIC];
+ }
+};
+
+template <>
struct LinkValidator<LinkTags::quest>
{
static bool IsTextValid(QuestLinkData const& data, char const* pos, size_t len)
@@ -183,66 +458,57 @@ struct LinkValidator<LinkTags::quest>
};
template <>
-struct LinkValidator<LinkTags::spell>
+struct LinkValidator<LinkTags::mawpower>
{
- static bool IsTextValid(SpellLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(MawPowerEntry const* mawPower, char const* pos, size_t len)
{
- return IsTextValid(data.Spell, pos, len);
- }
-
- static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len)
- {
- for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len((*info->SpellName)[i], pos, len))
- return true;
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE))
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
return false;
}
- static bool IsColorValid(SpellLinkData const&, HyperlinkColor c)
+ static bool IsColorValid(MawPowerEntry const*, HyperlinkColor c)
{
return c == CHAT_LINK_COLOR_SPELL;
}
};
template <>
-struct LinkValidator<LinkTags::enchant>
+struct LinkValidator<LinkTags::outfit>
{
- static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len)
+ static bool IsTextValid(std::string const&, char const*, size_t)
{
- SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(info->Id);
- if (bounds.first == bounds.second)
- return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return true;
+ }
- SkillLineEntry const* skill = sSkillLineStore.LookupEntry(bounds.first->second->SkillupSkillLineID
- ? bounds.first->second->SkillupSkillLineID
- : bounds.first->second->SkillLine);
- if (!skill)
- return false;
+ static bool IsColorValid(std::string const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_TRANSMOG;
+ }
+};
- for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- {
- char const* skillName = skill->DisplayName[i];
- size_t skillLen = strlen(skillName);
- if (len > skillLen + 2 && // or of form [Skill Name: Spell Name]
- !strncmp(pos, skillName, skillLen) && !strncmp(pos + skillLen, ": ", 2) &&
- equal_with_len((*info->SpellName)[i], pos + (skillLen + 2), len - (skillLen + 2)))
- return true;
- }
+template <>
+struct LinkValidator<LinkTags::pvptal>
+{
+ static bool IsTextValid(PvpTalentEntry const* mawPower, char const* pos, size_t len)
+ {
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE))
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
return false;
}
- static bool IsColorValid(SpellInfo const*, HyperlinkColor c)
+ static bool IsColorValid(PvpTalentEntry const*, HyperlinkColor c)
{
- return c == CHAT_LINK_COLOR_ENCHANT;
+ return c == CHAT_LINK_COLOR_TALENT;
}
};
template <>
struct LinkValidator<LinkTags::talent>
{
- static bool IsTextValid(TalentEntry const* talent, char const* pos, size_t len)
+ static bool IsTextValid(TalentEntry const* mawPower, char const* pos, size_t len)
{
- if (SpellInfo const* info = sSpellMgr->GetSpellInfo(talent->SpellID, DIFFICULTY_NONE))
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE))
return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
return false;
}
@@ -267,6 +533,81 @@ struct LinkValidator<LinkTags::trade>
}
};
+template <>
+struct LinkValidator<LinkTags::transmogappearance>
+{
+ static bool IsTextValid(ItemModifiedAppearanceEntry const* enchantment, char const* pos, size_t len)
+ {
+ if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(enchantment->ItemID))
+ return LinkValidator<LinkTags::item>::IsTextValid(itemTemplate, nullptr, pos, len);
+ return false;
+ }
+
+ static bool IsColorValid(ItemModifiedAppearanceEntry const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_TRANSMOG;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::transmogillusion>
+{
+ static bool IsTextValid(SpellItemEnchantmentEntry const* enchantment, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(enchantment->Name[i], pos, len))
+ return true;
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(enchantment->HordeName[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(SpellItemEnchantmentEntry const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_TRANSMOG;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::transmogset>
+{
+ static bool IsTextValid(TransmogSetEntry const* set, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ {
+ if (ItemNameDescriptionEntry const* itemNameDescription = sItemNameDescriptionStore.LookupEntry(set->ItemNameDescriptionID))
+ {
+ std::string expectedText = Trinity::StringFormat("%s (%s)", set->Name[i], itemNameDescription->Description[i]);
+ if (equal_with_len(expectedText.c_str(), pos, len))
+ return true;
+ }
+ else if (equal_with_len(set->Name[i], pos, len))
+ return true;
+ }
+ return false;
+ }
+
+ static bool IsColorValid(TransmogSetEntry const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_TRANSMOG;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::worldmap>
+{
+ static bool IsTextValid(WorldMapLinkData const&, char const*, size_t)
+ {
+ return true;
+ }
+
+ static bool IsColorValid(WorldMapLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_ACHIEVEMENT;
+ }
+};
+
#define TryValidateAs(tagname) \
{ \
ASSERT(!strcmp(LinkTags::tagname::tag(), #tagname)); \
@@ -288,17 +629,31 @@ struct LinkValidator<LinkTags::trade>
static bool ValidateLinkInfo(HyperlinkInfo const& info)
{
TryValidateAs(achievement);
+ TryValidateAs(apower);
+ TryValidateAs(azessence);
TryValidateAs(area);
TryValidateAs(areatrigger);
+ TryValidateAs(battlepet);
+ TryValidateAs(conduit);
TryValidateAs(creature);
TryValidateAs(creature_entry);
+ TryValidateAs(currency);
TryValidateAs(enchant);
TryValidateAs(gameevent);
TryValidateAs(gameobject);
TryValidateAs(gameobject_entry);
+ TryValidateAs(garrfollower);
+ TryValidateAs(garrfollowerability);
+ TryValidateAs(garrmission);
+ TryValidateAs(instancelock);
TryValidateAs(item);
TryValidateAs(itemset);
+ TryValidateAs(journal);
+ TryValidateAs(keystone);
+ TryValidateAs(mawpower);
+ TryValidateAs(outfit);
TryValidateAs(player);
+ TryValidateAs(pvptal);
TryValidateAs(quest);
TryValidateAs(skill);
TryValidateAs(spell);
@@ -307,6 +662,10 @@ static bool ValidateLinkInfo(HyperlinkInfo const& info)
TryValidateAs(tele);
TryValidateAs(title);
TryValidateAs(trade);
+ TryValidateAs(transmogappearance);
+ TryValidateAs(transmogillusion);
+ TryValidateAs(transmogset);
+ TryValidateAs(worldmap);
return false;
}
diff --git a/src/server/game/Chat/Hyperlinks.h b/src/server/game/Chat/Hyperlinks.h
index c958ce176cf..7108be73372 100644
--- a/src/server/game/Chat/Hyperlinks.h
+++ b/src/server/game/Chat/Hyperlinks.h
@@ -20,17 +20,36 @@
#include "advstd.h"
#include "ObjectGuid.h"
+#include "Optional.h"
#include <string>
#include <utility>
struct AchievementEntry;
+struct ArtifactPowerRankEntry;
+struct AzeriteEssenceEntry;
+struct BattlePetSpeciesEntry;
+struct CurrencyContainerEntry;
+struct CurrencyTypesEntry;
+struct GarrAbilityEntry;
+struct GarrFollowerEntry;
+struct GarrMissionEntry;
struct GlyphPropertiesEntry;
+struct ItemModifiedAppearanceEntry;
struct ItemNameDescriptionEntry;
struct ItemTemplate;
+struct LocalizedString;
+struct MapEntry;
+struct MapChallengeModeEntry;
+struct MawPowerEntry;
+struct PvpTalentEntry;
class Quest;
struct SkillLineEntry;
+struct SoulbindConduitRankEntry;
class SpellInfo;
+struct SpellItemEnchantmentEntry;
struct TalentEntry;
+struct TransmogSetEntry;
+struct UiMapEntry;
namespace Trinity
{
@@ -48,6 +67,64 @@ struct AchievementLinkData
uint32 Criteria[4];
};
+struct ArtifactPowerLinkData
+{
+ ArtifactPowerRankEntry const* ArtifactPower;
+ uint8 PurchasedRank;
+ uint8 CurrentRankWithBonus;
+};
+
+struct AzeriteEssenceLinkData
+{
+ AzeriteEssenceEntry const* Essence;
+ uint8 Rank;
+};
+
+struct BattlePetLinkData
+{
+ BattlePetSpeciesEntry const* Species;
+ uint8 Level;
+ uint8 Quality;
+ uint32 MaxHealth;
+ uint32 Power;
+ uint32 Speed;
+ ObjectGuid PetGuid;
+ uint32 DisplayId;
+};
+
+struct CurrencyLinkData
+{
+ CurrencyTypesEntry const* Currency;
+ int32 Quantity;
+
+ CurrencyContainerEntry const* Container;
+};
+
+struct GarrisonFollowerLinkData
+{
+ GarrFollowerEntry const* Follower;
+ uint32 Quality;
+ uint32 Level;
+ uint32 ItemLevel;
+ uint32 Abilities[4];
+ uint32 Traits[4];
+ uint32 Specialization;
+};
+
+struct GarrisonMissionLinkData
+{
+ GarrMissionEntry const* Mission;
+ uint64 DbID;
+};
+
+struct InstanceLockLinkData
+{
+ ObjectGuid Owner;
+ MapEntry const* Map;
+ uint32 Difficulty;
+ uint32 CompletedEncountersMask;
+};
+
struct ItemLinkData
{
ItemTemplate const* Item;
@@ -73,6 +150,29 @@ struct ItemLinkData
ItemNameDescriptionEntry const* Suffix;
};
+struct JournalLinkData
+{
+ enum class Types : uint8
+ {
+ Instance = 0,
+ Encounter = 1,
+ EncounterSection = 2,
+ Tier = 3
+ };
+
+ uint8 Type;
+ LocalizedString const* ExpectedText;
+ uint32 Difficulty;
+};
+
+struct KeystoneLinkData
+{
+ uint32 ItemId;
+ MapChallengeModeEntry const* Map;
+ uint32 Level;
+ uint32 Affix[4];
+};
+
struct QuestLinkData
{
::Quest const* Quest;
@@ -92,6 +192,14 @@ struct TradeskillLinkData
SkillLineEntry const* Skill;
};
+struct WorldMapLinkData
+{
+ UiMapEntry const* UiMap;
+ uint32 X;
+ uint32 Y;
+ Optional<uint32> Z;
+};
+
namespace LinkTags {
/************************** LINK TAGS ***************************************************\
@@ -138,6 +246,7 @@ namespace LinkTags {
};
#define make_base_tag(ltag, type) struct ltag : public base_tag { using value_type = type; static constexpr char const* tag() { return #ltag; } }
+ // custom formats
make_base_tag(area, uint32);
make_base_tag(areatrigger, uint32);
make_base_tag(creature, ObjectGuid::LowType);
@@ -151,6 +260,9 @@ namespace LinkTags {
make_base_tag(taxinode, uint32);
make_base_tag(tele, uint32);
make_base_tag(title, uint32);
+
+ // client format
+ make_base_tag(outfit, std::string const&); // some sort of weird base91 derived encoding
#undef make_base_tag
struct TC_GAME_API achievement
@@ -160,6 +272,41 @@ namespace LinkTags {
static bool StoreTo(AchievementLinkData& val, char const* pos, size_t len);
};
+ struct TC_GAME_API apower
+ {
+ using value_type = ArtifactPowerLinkData const&;
+ static constexpr char const* tag() { return "apower"; }
+ static bool StoreTo(ArtifactPowerLinkData& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API azessence
+ {
+ using value_type = AzeriteEssenceLinkData const&;
+ static constexpr char const* tag() { return "azessence"; }
+ static bool StoreTo(AzeriteEssenceLinkData& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API battlepet
+ {
+ using value_type = BattlePetLinkData const&;
+ static constexpr char const* tag() { return "battlepet"; }
+ static bool StoreTo(BattlePetLinkData& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API conduit
+ {
+ using value_type = SoulbindConduitRankEntry const*&;
+ static constexpr char const* tag() { return "conduit"; }
+ static bool StoreTo(SoulbindConduitRankEntry const*& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API currency
+ {
+ using value_type = CurrencyLinkData const&;
+ static constexpr char const* tag() { return "currency"; }
+ static bool StoreTo(CurrencyLinkData& val, char const* pos, size_t len);
+ };
+
struct TC_GAME_API enchant
{
using value_type = SpellInfo const*;
@@ -167,6 +314,34 @@ namespace LinkTags {
static bool StoreTo(SpellInfo const*& val, char const* pos, size_t len);
};
+ struct TC_GAME_API garrfollower
+ {
+ using value_type = GarrisonFollowerLinkData const&;
+ static constexpr char const* tag() { return "garrfollower"; }
+ static bool StoreTo(GarrisonFollowerLinkData& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API garrfollowerability
+ {
+ using value_type = GarrAbilityEntry const*&;
+ static constexpr char const* tag() { return "garrfollowerability"; }
+ static bool StoreTo(GarrAbilityEntry const*& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API garrmission
+ {
+ using value_type = GarrisonMissionLinkData const&;
+ static constexpr char const* tag() { return "garrmission"; }
+ static bool StoreTo(GarrisonMissionLinkData& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API instancelock
+ {
+ using value_type = InstanceLockLinkData const&;
+ static constexpr char const* tag() { return "instancelock"; }
+ static bool StoreTo(InstanceLockLinkData& val, char const* pos, size_t len);
+ };
+
struct TC_GAME_API item
{
using value_type = ItemLinkData const&;
@@ -174,6 +349,34 @@ namespace LinkTags {
static bool StoreTo(ItemLinkData& val, char const* pos, size_t len);
};
+ struct TC_GAME_API journal
+ {
+ using value_type = JournalLinkData const&;
+ static constexpr char const* tag() { return "journal"; }
+ static bool StoreTo(JournalLinkData& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API keystone
+ {
+ using value_type = KeystoneLinkData const&;
+ static constexpr char const* tag() { return "keystone"; }
+ static bool StoreTo(KeystoneLinkData& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API mawpower
+ {
+ using value_type = MawPowerEntry const*&;
+ static constexpr char const* tag() { return "mawpower"; }
+ static bool StoreTo(MawPowerEntry const*& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API pvptal
+ {
+ using value_type = PvpTalentEntry const*&;
+ static constexpr char const* tag() { return "pvptal"; }
+ static bool StoreTo(PvpTalentEntry const*& val, char const* pos, size_t len);
+ };
+
struct TC_GAME_API quest
{
using value_type = QuestLinkData const&;
@@ -201,6 +404,34 @@ namespace LinkTags {
static constexpr char const* tag() { return "trade"; }
static bool StoreTo(TradeskillLinkData& val, char const* pos, size_t len);
};
+
+ struct TC_GAME_API transmogappearance
+ {
+ using value_type = ItemModifiedAppearanceEntry const*&;
+ static constexpr char const* tag() { return "transmogappearance"; }
+ static bool StoreTo(ItemModifiedAppearanceEntry const*& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API transmogillusion
+ {
+ using value_type = SpellItemEnchantmentEntry const*&;
+ static constexpr char const* tag() { return "transmogillusion"; }
+ static bool StoreTo(SpellItemEnchantmentEntry const*& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API transmogset
+ {
+ using value_type = TransmogSetEntry const*&;
+ static constexpr char const* tag() { return "transmogset"; }
+ static bool StoreTo(TransmogSetEntry const*& val, char const* pos, size_t len);
+ };
+
+ struct TC_GAME_API worldmap
+ {
+ using value_type = WorldMapLinkData const&;
+ static constexpr char const* tag() { return "worldmap"; }
+ static bool StoreTo(WorldMapLinkData& val, char const* pos, size_t len);
+ };
}
struct HyperlinkColor
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 517f4e085eb..a7445587efb 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -5649,6 +5649,11 @@ enum ChatLinkColors : uint32
CHAT_LINK_COLOR_SPELL = 0xff71d5ff, // bright blue
CHAT_LINK_COLOR_ENCHANT = 0xffffd000, // orange
CHAT_LINK_COLOR_ACHIEVEMENT = 0xffffff00,
+ CHAT_LINK_COLOR_ARTIFACT_POWER = 0xff71d5ff,
+ CHAT_LINK_COLOR_GARR_ABILITY = 0xff4e96f7,
+ CHAT_LINK_COLOR_INSTANCE_LOCK = 0xffff8000,
+ CHAT_LINK_COLOR_JOURNAL = 0xff66bbff,
+ CHAT_LINK_COLOR_TRANSMOG = 0xffff80ff,
};
// Values from ItemPetFood (power of (value-1) used for compare with CreatureFamilyEntry.petDietMask