Core/Chat: Implemented more chat link types

Closes #28946
This commit is contained in:
Shauren
2023-04-28 00:11:24 +02:00
parent 6d4a0ad8eb
commit d78f08aa9e
12 changed files with 286 additions and 4 deletions

View File

@@ -0,0 +1,40 @@
--
-- Table structure for table `battle_pet_ability`
--
DROP TABLE IF EXISTS `battle_pet_ability`;
CREATE TABLE `battle_pet_ability` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`Name` text,
`Description` text,
`IconFileDataID` int(11) NOT NULL DEFAULT '0',
`PetTypeEnum` tinyint(4) NOT NULL DEFAULT '0',
`Cooldown` int(10) unsigned NOT NULL DEFAULT '0',
`BattlePetVisualID` smallint(5) unsigned NOT NULL DEFAULT '0',
`Flags` tinyint(3) unsigned NOT NULL DEFAULT '0',
`VerifiedBuild` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Table structure for table `battle_pet_ability_locale`
--
DROP TABLE IF EXISTS `battle_pet_ability_locale`;
CREATE TABLE `battle_pet_ability_locale` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`locale` varchar(4) NOT NULL,
`Name_lang` text,
`Description_lang` text,
`VerifiedBuild` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`,`locale`,`VerifiedBuild`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
/*!50500 PARTITION BY LIST COLUMNS(locale)
(PARTITION deDE VALUES IN ('deDE') ENGINE = InnoDB,
PARTITION esES VALUES IN ('esES') ENGINE = InnoDB,
PARTITION esMX VALUES IN ('esMX') ENGINE = InnoDB,
PARTITION frFR VALUES IN ('frFR') ENGINE = InnoDB,
PARTITION itIT VALUES IN ('itIT') ENGINE = InnoDB,
PARTITION koKR VALUES IN ('koKR') ENGINE = InnoDB,
PARTITION ptBR VALUES IN ('ptBR') ENGINE = InnoDB,
PARTITION ruRU VALUES IN ('ruRU') ENGINE = InnoDB,
PARTITION zhCN VALUES IN ('zhCN') ENGINE = InnoDB,
PARTITION zhTW VALUES IN ('zhTW') ENGINE = InnoDB) */;

View File

@@ -237,6 +237,13 @@ void HotfixDatabaseConnection::DoPrepareStatements()
PREPARE_LOCALE_STMT(HOTFIX_SEL_BARBER_SHOP_STYLE, "SELECT ID, DisplayName_lang, Description_lang FROM barber_shop_style_locale"
" WHERE (`VerifiedBuild` > 0) = ? AND locale = ?", CONNECTION_SYNCH);
// BattlePetAbility.db2
PrepareStatement(HOTFIX_SEL_BATTLE_PET_ABILITY, "SELECT ID, Name, Description, IconFileDataID, PetTypeEnum, Cooldown, BattlePetVisualID, Flags"
" FROM battle_pet_ability WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_BATTLE_PET_ABILITY, "SELECT MAX(ID) + 1 FROM battle_pet_ability", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_BATTLE_PET_ABILITY, "SELECT ID, Name_lang, Description_lang FROM battle_pet_ability_locale"
" WHERE (`VerifiedBuild` > 0) = ? AND locale = ?", CONNECTION_SYNCH);
// BattlePetBreedQuality.db2
PrepareStatement(HOTFIX_SEL_BATTLE_PET_BREED_QUALITY, "SELECT ID, MaxQualityRoll, StateMultiplier, QualityEnum FROM battle_pet_breed_quality"
" WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);

View File

@@ -154,6 +154,10 @@ enum HotfixDatabaseStatements : uint32
HOTFIX_SEL_BARBER_SHOP_STYLE_MAX_ID,
HOTFIX_SEL_BARBER_SHOP_STYLE_LOCALE,
HOTFIX_SEL_BATTLE_PET_ABILITY,
HOTFIX_SEL_BATTLE_PET_ABILITY_MAX_ID,
HOTFIX_SEL_BATTLE_PET_ABILITY_LOCALE,
HOTFIX_SEL_BATTLE_PET_BREED_QUALITY,
HOTFIX_SEL_BATTLE_PET_BREED_QUALITY_MAX_ID,

View File

@@ -118,6 +118,7 @@ struct SpellInfoVisitor
value_type operator()(Hyperlink<conduit> soulbindConduit) const { return operator()((*soulbindConduit)->SpellID); }
value_type operator()(Hyperlink<enchant> enchant) const { return enchant; }
value_type operator()(Hyperlink<mawpower> mawPower) const { return operator()((*mawPower)->SpellID); }
value_type operator()(Hyperlink<mount> const& mount) const { return mount->Spell; }
value_type operator()(Hyperlink<pvptal> pvpTalent) const { return operator()((*pvpTalent)->SpellID); }
value_type operator()(Hyperlink<spell> spell) const { return spell->Spell; }
value_type operator()(Hyperlink<talent> talent) const { return operator()((*talent)->SpellID); }
@@ -127,7 +128,7 @@ struct SpellInfoVisitor
};
ChatCommandResult Trinity::Impl::ChatCommands::ArgInfo<SpellInfo const*>::TryConsume(SpellInfo const*& data, ChatHandler const* handler, std::string_view args)
{
Variant<Hyperlink<apower>, Hyperlink<conduit>, Hyperlink<enchant>, Hyperlink<mawpower>, Hyperlink<pvptal>, Hyperlink<spell>, Hyperlink<talent>, Hyperlink<trade>, uint32> val;
Variant<Hyperlink<apower>, Hyperlink<conduit>, Hyperlink<enchant>, Hyperlink<mawpower>, Hyperlink<mount>, Hyperlink<pvptal>, Hyperlink<spell>, Hyperlink<talent>, Hyperlink<trade>, uint32> val;
ChatCommandResult result = ArgInfo<decltype(val)>::TryConsume(val, handler, args);
if (!result || (data = val.visit(SpellInfoVisitor())))
return result;

View File

@@ -97,6 +97,14 @@ bool Trinity::Hyperlinks::LinkTags::achievement::StoreTo(AchievementLinkData& va
return (t.TryConsumeTo(val.Criteria[0]) && t.TryConsumeTo(val.Criteria[1]) && t.TryConsumeTo(val.Criteria[2]) && t.TryConsumeTo(val.Criteria[3]) && t.IsEmpty());
}
bool Trinity::Hyperlinks::LinkTags::api::StoreTo(ApiLinkData& val, std::string_view text)
{
HyperlinkDataTokenizer t(text, true);
if (!(t.TryConsumeTo(val.Type) && t.TryConsumeTo(val.Name) && t.TryConsumeTo(val.Parent) && t.IsEmpty()))
return false;
return true;
}
bool Trinity::Hyperlinks::LinkTags::apower::StoreTo(ArtifactPowerLinkData& val, std::string_view text)
{
HyperlinkDataTokenizer t(text);
@@ -134,6 +142,17 @@ bool Trinity::Hyperlinks::LinkTags::battlepet::StoreTo(BattlePetLinkData& val, s
&& t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::battlePetAbil::StoreTo(BattlePetAbilLinkData& val, std::string_view text)
{
HyperlinkDataTokenizer t(text);
uint32 battlePetAbilityId;
if (!t.TryConsumeTo(battlePetAbilityId))
return false;
return (val.Ability = sBattlePetAbilityStore.LookupEntry(battlePetAbilityId))
&& t.TryConsumeTo(val.MaxHealth) && t.TryConsumeTo(val.Power) && t.TryConsumeTo(val.Speed)
&& t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::conduit::StoreTo(SoulbindConduitRankEntry const*& val, std::string_view text)
{
HyperlinkDataTokenizer t(text);
@@ -156,6 +175,32 @@ bool Trinity::Hyperlinks::LinkTags::currency::StoreTo(CurrencyLinkData& val, std
return true;
}
bool Trinity::Hyperlinks::LinkTags::dungeonScore::StoreTo(DungeonScoreLinkData& val, std::string_view text)
{
HyperlinkDataTokenizer t(text);
if (!t.TryConsumeTo(val.Score) || !t.TryConsumeTo(val.Player) || !val.Player.IsPlayer()
|| !t.TryConsumeTo(val.PlayerName) || !t.TryConsumeTo(val.PlayerClass) || !t.TryConsumeTo(val.AvgItemLevel)
|| !t.TryConsumeTo(val.PlayerLevel) || !t.TryConsumeTo(val.RunsThisSeason)
|| !t.TryConsumeTo(val.BestSeasonScore) || !t.TryConsumeTo(val.BestSeasonNumber))
return false;
if (t.IsEmpty())
return true;
for (uint32 i = 0; i < 10; ++i)
{
DungeonScoreLinkData::Dungeon& dungeon = val.Dungeons.emplace_back();
if (!t.TryConsumeTo(dungeon.MapChallengeModeID) || !sMapChallengeModeStore.LookupEntry(dungeon.MapChallengeModeID))
return false;
if (!t.TryConsumeTo(dungeon.CompletedInTime) || !t.TryConsumeTo(dungeon.KeystoneLevel))
return false;
if (t.IsEmpty())
return true;
}
return false;
}
bool Trinity::Hyperlinks::LinkTags::enchant::StoreTo(SpellInfo const*& val, std::string_view text)
{
HyperlinkDataTokenizer t(text);
@@ -359,6 +404,17 @@ bool Trinity::Hyperlinks::LinkTags::mawpower::StoreTo(MawPowerEntry const*& val,
return !!(val = sMawPowerStore.LookupEntry(mawPowerId)) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::mount::StoreTo(MountLinkData& val, std::string_view text)
{
HyperlinkDataTokenizer t(text);
uint32 spellId;
if (!t.TryConsumeTo(spellId) || !((val.Spell = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE))))
return false;
if (!t.TryConsumeTo(val.DisplayId) || !sCreatureDisplayInfoStore.LookupEntry(val.DisplayId))
return false;
return t.TryConsumeTo(val.Customizations) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::pvptal::StoreTo(PvpTalentEntry const*& val, std::string_view text)
{
HyperlinkDataTokenizer t(text);
@@ -400,6 +456,19 @@ bool Trinity::Hyperlinks::LinkTags::talent::StoreTo(TalentEntry const*& val, std
return true;
}
bool Trinity::Hyperlinks::LinkTags::talentbuild::StoreTo(TalentBuildLinkData& val, std::string_view text)
{
HyperlinkDataTokenizer t(text);
uint32 chrSpecializationId;
if (!t.TryConsumeTo(chrSpecializationId))
return false;
if (!(val.Spec = sChrSpecializationStore.LookupEntry(chrSpecializationId)))
return false;
if (!t.TryConsumeTo(val.Level) || !t.TryConsumeTo(val.ImportString))
return false;
return true;
}
bool Trinity::Hyperlinks::LinkTags::trade::StoreTo(TradeskillLinkData& val, std::string_view text)
{
HyperlinkDataTokenizer t(text);

View File

@@ -216,6 +216,23 @@ struct LinkValidator<LinkTags::battlepet>
}
};
template <>
struct LinkValidator<LinkTags::battlePetAbil>
{
static bool IsTextValid(BattlePetAbilLinkData const& data, std::string_view text)
{
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
if (data.Ability->Name[i] == text)
return true;
return false;
}
static bool IsColorValid(BattlePetAbilLinkData const&, HyperlinkColor c)
{
return c == CHAT_LINK_COLOR_BATTLE_PET_ABIL;
}
};
template <>
struct LinkValidator<LinkTags::conduit>
{
@@ -496,15 +513,29 @@ struct LinkValidator<LinkTags::mawpower>
}
};
template <>
struct LinkValidator<LinkTags::mount>
{
static bool IsTextValid(MountLinkData const& data, std::string_view text)
{
return LinkValidator<LinkTags::spell>::IsTextValid(data.Spell, text);
}
static bool IsColorValid(MountLinkData const&, HyperlinkColor c)
{
return c == CHAT_LINK_COLOR_SPELL;
}
};
template <>
struct LinkValidator<LinkTags::outfit>
{
static bool IsTextValid(std::string const&, std::string_view)
static bool IsTextValid(std::string_view, std::string_view)
{
return true;
}
static bool IsColorValid(std::string const&, HyperlinkColor c)
static bool IsColorValid(std::string_view, HyperlinkColor c)
{
return c == CHAT_LINK_COLOR_TRANSMOG;
}
@@ -658,15 +689,20 @@ static bool ValidateLinkInfo(HyperlinkInfo const& info)
{
using namespace LinkTags;
TryValidateAs(achievement);
TryValidateAs(api);
TryValidateAs(apower);
TryValidateAs(azessence);
TryValidateAs(area);
TryValidateAs(areatrigger);
TryValidateAs(battlepet);
TryValidateAs(battlePetAbil);
TryValidateAs(clubFinder);
TryValidateAs(clubTicket);
TryValidateAs(conduit);
TryValidateAs(creature);
TryValidateAs(creature_entry);
TryValidateAs(currency);
TryValidateAs(dungeonScore);
TryValidateAs(enchant);
TryValidateAs(gameevent);
TryValidateAs(gameobject);
@@ -680,6 +716,7 @@ static bool ValidateLinkInfo(HyperlinkInfo const& info)
TryValidateAs(journal);
TryValidateAs(keystone);
TryValidateAs(mawpower);
TryValidateAs(mount);
TryValidateAs(outfit);
TryValidateAs(player);
TryValidateAs(pvptal);
@@ -687,6 +724,7 @@ static bool ValidateLinkInfo(HyperlinkInfo const& info)
TryValidateAs(skill);
TryValidateAs(spell);
TryValidateAs(talent);
TryValidateAs(talentbuild);
TryValidateAs(taxinode);
TryValidateAs(tele);
TryValidateAs(title);

View File

@@ -27,7 +27,9 @@
struct AchievementEntry;
struct ArtifactPowerRankEntry;
struct AzeriteEssenceEntry;
struct BattlePetAbilityEntry;
struct BattlePetSpeciesEntry;
struct ChrSpecializationEntry;
struct CurrencyContainerEntry;
struct CurrencyTypesEntry;
struct GarrAbilityEntry;
@@ -65,6 +67,13 @@ namespace Trinity::Hyperlinks
std::array<uint32, 4> Criteria = { };
};
struct ApiLinkData
{
std::string_view Type;
std::string_view Name;
std::string_view Parent;
};
struct ArtifactPowerLinkData
{
ArtifactPowerRankEntry const* ArtifactPower = nullptr;
@@ -90,6 +99,14 @@ namespace Trinity::Hyperlinks
uint32 DisplayId = 0;
};
struct BattlePetAbilLinkData
{
BattlePetAbilityEntry const* Ability = nullptr;
uint32 MaxHealth = 0;
uint32 Power = 0;
uint32 Speed = 0;
};
struct CurrencyLinkData
{
CurrencyTypesEntry const* Currency = nullptr;
@@ -98,6 +115,28 @@ namespace Trinity::Hyperlinks
CurrencyContainerEntry const* Container = nullptr;
};
struct DungeonScoreLinkData
{
uint32 Score = 0;
ObjectGuid Player;
std::string_view PlayerName;
uint8 PlayerClass = 0;
uint32 AvgItemLevel = 0;
uint8 PlayerLevel = 0;
uint32 RunsThisSeason = 0;
uint32 BestSeasonScore = 0;
uint32 BestSeasonNumber = 0;
struct Dungeon
{
uint32 MapChallengeModeID = 0;
bool CompletedInTime = false;
uint32 KeystoneLevel = 0;
};
std::vector<Dungeon> Dungeons;
};
struct GarrisonFollowerLinkData
{
GarrFollowerEntry const* Follower = nullptr;
@@ -171,6 +210,13 @@ namespace Trinity::Hyperlinks
std::array<uint32, 4> Affix = { };
};
struct MountLinkData
{
SpellInfo const* Spell = nullptr;
uint32 DisplayId = 0;
std::string_view Customizations;
};
struct QuestLinkData
{
::Quest const* Quest = nullptr;
@@ -183,6 +229,13 @@ namespace Trinity::Hyperlinks
GlyphPropertiesEntry const* Glyph = nullptr;
};
struct TalentBuildLinkData
{
ChrSpecializationEntry const* Spec = nullptr;
uint32 Level = 0;
std::string_view ImportString;
};
struct TradeskillLinkData
{
ObjectGuid Owner;
@@ -268,7 +321,9 @@ namespace Trinity::Hyperlinks
make_base_tag(title, uint32);
// client format
make_base_tag(outfit, std::string const&); // some sort of weird base91 derived encoding
make_base_tag(clubFinder, ObjectGuid);
make_base_tag(clubTicket, std::string_view);
make_base_tag(outfit, std::string_view); // some sort of weird base91 derived encoding
#undef make_base_tag
struct TC_GAME_API achievement
@@ -278,6 +333,13 @@ namespace Trinity::Hyperlinks
static bool StoreTo(AchievementLinkData& val, std::string_view text);
};
struct TC_GAME_API api
{
using value_type = ApiLinkData const&;
static constexpr std::string_view tag() { return "api"; }
static bool StoreTo(ApiLinkData& val, std::string_view text);
};
struct TC_GAME_API apower
{
using value_type = ArtifactPowerLinkData const&;
@@ -299,6 +361,13 @@ namespace Trinity::Hyperlinks
static bool StoreTo(BattlePetLinkData& val, std::string_view text);
};
struct TC_GAME_API battlePetAbil
{
using value_type = BattlePetAbilLinkData const&;
static constexpr std::string_view tag() { return "battlePetAbil"; }
static bool StoreTo(BattlePetAbilLinkData& val, std::string_view text);
};
struct TC_GAME_API conduit
{
using value_type = SoulbindConduitRankEntry const*;
@@ -313,6 +382,13 @@ namespace Trinity::Hyperlinks
static bool StoreTo(CurrencyLinkData& val, std::string_view text);
};
struct TC_GAME_API dungeonScore
{
using value_type = DungeonScoreLinkData const&;
static constexpr std::string_view tag() { return "dungeonScore"; }
static bool StoreTo(DungeonScoreLinkData& val, std::string_view text);
};
struct TC_GAME_API enchant
{
using value_type = SpellInfo const*;
@@ -376,6 +452,13 @@ namespace Trinity::Hyperlinks
static bool StoreTo(MawPowerEntry const*& val, std::string_view text);
};
struct TC_GAME_API mount
{
using value_type = MountLinkData const&;
static constexpr std::string_view tag() { return "mount"; }
static bool StoreTo(MountLinkData& val, std::string_view text);
};
struct TC_GAME_API pvptal
{
using value_type = PvpTalentEntry const*;
@@ -404,6 +487,13 @@ namespace Trinity::Hyperlinks
static bool StoreTo(TalentEntry const*& val, std::string_view text);
};
struct TC_GAME_API talentbuild
{
using value_type = TalentBuildLinkData const&;
static constexpr std::string_view tag() { return "talentbuild"; }
static bool StoreTo(TalentBuildLinkData& val, std::string_view text);
};
struct TC_GAME_API trade
{
using value_type = TradeskillLinkData const&;

View File

@@ -635,6 +635,23 @@ struct BarberShopStyleLoadInfo
static constexpr DB2LoadInfo Instance{ Fields, 8, &BarberShopStyleMeta::Instance, HOTFIX_SEL_BARBER_SHOP_STYLE };
};
struct BattlePetAbilityLoadInfo
{
static constexpr DB2FieldMeta Fields[8] =
{
{ false, FT_INT, "ID" },
{ false, FT_STRING, "Name" },
{ false, FT_STRING, "Description" },
{ true, FT_INT, "IconFileDataID" },
{ true, FT_BYTE, "PetTypeEnum" },
{ false, FT_INT, "Cooldown" },
{ false, FT_SHORT, "BattlePetVisualID" },
{ false, FT_BYTE, "Flags" },
};
static constexpr DB2LoadInfo Instance{ Fields, 8, &BattlePetAbilityMeta::Instance, HOTFIX_SEL_BATTLE_PET_ABILITY };
};
struct BattlePetBreedQualityLoadInfo
{
static constexpr DB2FieldMeta Fields[4] =

View File

@@ -77,6 +77,7 @@ DB2Storage<AzeriteUnlockMappingEntry> sAzeriteUnlockMappingStore("Azer
DB2Storage<BankBagSlotPricesEntry> sBankBagSlotPricesStore("BankBagSlotPrices.db2", &BankBagSlotPricesLoadInfo::Instance);
DB2Storage<BannedAddonsEntry> sBannedAddonsStore("BannedAddons.db2", &BannedAddonsLoadInfo::Instance);
DB2Storage<BarberShopStyleEntry> sBarberShopStyleStore("BarberShopStyle.db2", &BarberShopStyleLoadInfo::Instance);
DB2Storage<BattlePetAbilityEntry> sBattlePetAbilityStore("BattlePetAbility.db2", &BattlePetAbilityLoadInfo::Instance);
DB2Storage<BattlePetBreedQualityEntry> sBattlePetBreedQualityStore("BattlePetBreedQuality.db2", &BattlePetBreedQualityLoadInfo::Instance);
DB2Storage<BattlePetBreedStateEntry> sBattlePetBreedStateStore("BattlePetBreedState.db2", &BattlePetBreedStateLoadInfo::Instance);
DB2Storage<BattlePetSpeciesEntry> sBattlePetSpeciesStore("BattlePetSpecies.db2", &BattlePetSpeciesLoadInfo::Instance);
@@ -679,6 +680,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
LOAD_DB2(sBankBagSlotPricesStore);
LOAD_DB2(sBannedAddonsStore);
LOAD_DB2(sBarberShopStyleStore);
LOAD_DB2(sBattlePetAbilityStore);
LOAD_DB2(sBattlePetBreedQualityStore);
LOAD_DB2(sBattlePetBreedStateStore);
LOAD_DB2(sBattlePetSpeciesStore);

View File

@@ -60,6 +60,7 @@ TC_GAME_API extern DB2Storage<AzeritePowerEntry> sAzeritePowe
TC_GAME_API extern DB2Storage<BankBagSlotPricesEntry> sBankBagSlotPricesStore;
TC_GAME_API extern DB2Storage<BannedAddonsEntry> sBannedAddonsStore;
TC_GAME_API extern DB2Storage<BarberShopStyleEntry> sBarberShopStyleStore;
TC_GAME_API extern DB2Storage<BattlePetAbilityEntry> sBattlePetAbilityStore;
TC_GAME_API extern DB2Storage<BattlePetBreedQualityEntry> sBattlePetBreedQualityStore;
TC_GAME_API extern DB2Storage<BattlePetBreedStateEntry> sBattlePetBreedStateStore;
TC_GAME_API extern DB2Storage<BattlePetSpeciesEntry> sBattlePetSpeciesStore;

View File

@@ -448,6 +448,18 @@ struct BarberShopStyleEntry
uint8 Data; // real ID to hair/facial hair
};
struct BattlePetAbilityEntry
{
uint32 ID;
LocalizedString Name;
LocalizedString Description;
int32 IconFileDataID;
int8 PetTypeEnum;
uint32 Cooldown;
uint16 BattlePetVisualID;
uint8 Flags;
};
struct BattlePetBreedQualityEntry
{
uint32 ID;

View File

@@ -5913,6 +5913,7 @@ enum ChatLinkColors : uint32
CHAT_LINK_COLOR_ENCHANT = 0xffffd000, // orange
CHAT_LINK_COLOR_ACHIEVEMENT = 0xffffff00,
CHAT_LINK_COLOR_ARTIFACT_POWER = 0xff71d5ff,
CHAT_LINK_COLOR_BATTLE_PET_ABIL = 0xff4e96f7,
CHAT_LINK_COLOR_GARR_ABILITY = 0xff4e96f7,
CHAT_LINK_COLOR_INSTANCE_LOCK = 0xffff8000,
CHAT_LINK_COLOR_JOURNAL = 0xff66bbff,