aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatan Shukry <matanshukry@gmail.com>2021-03-30 02:27:50 +0300
committerShauren <shauren.trinity@gmail.com>2021-05-03 23:53:24 +0200
commit3b578fc6b95bfc812962fcef7930d8646016d184 (patch)
treed881852521169f47a2db2cdaaa0eb0fb19f8ddaf
parentfb66575d38d2ba7ffc24c29824fa75d7019de549 (diff)
Core/Chat: Load chat language data from db2 and implement serverside chat translations
-rw-r--r--src/server/game/Chat/LanguageMgr.cpp234
-rw-r--r--src/server/game/Chat/LanguageMgr.h100
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp5
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp9
-rw-r--r--src/server/game/DataStores/DB2Stores.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp70
-rw-r--r--src/server/game/Entities/Player/Player.h4
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp46
-rw-r--r--src/server/game/Globals/ObjectMgr.h10
-rw-r--r--src/server/game/Handlers/ChatHandler.cpp29
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h2
-rw-r--r--src/server/game/Server/WorldSession.h2
-rw-r--r--src/server/game/Spells/SpellMgr.cpp4
-rw-r--r--src/server/game/Texts/ChatTextBuilder.h1
-rw-r--r--src/server/game/Texts/CreatureTextMgr.cpp3
-rw-r--r--src/server/game/World/World.cpp10
-rw-r--r--src/server/scripts/Commands/cs_learn.cpp9
17 files changed, 450 insertions, 89 deletions
diff --git a/src/server/game/Chat/LanguageMgr.cpp b/src/server/game/Chat/LanguageMgr.cpp
new file mode 100644
index 00000000000..acbd76f0a56
--- /dev/null
+++ b/src/server/game/Chat/LanguageMgr.cpp
@@ -0,0 +1,234 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "LanguageMgr.h"
+#include "DB2Stores.h"
+#include "Log.h"
+#include "SpellInfo.h"
+#include "SpellMgr.h"
+#include "Timer.h"
+
+#include <sstream>
+
+LanguageMgr::LanguageMgr() : _langsMap(), _wordsMap() { }
+
+LanguageMgr::~LanguageMgr()
+{
+ _langsMap.clear();
+ _wordsMap.clear();
+}
+
+LanguageMgr* LanguageMgr::instance()
+{
+ static LanguageMgr instance;
+ return &instance;
+}
+
+void LanguageMgr::LoadSpellEffectLanguage(SpellEffectEntry const* spellEffect)
+{
+ ASSERT(spellEffect && spellEffect->Effect == SPELL_EFFECT_LANGUAGE);
+
+ uint32 languageId = uint32(spellEffect->EffectMiscValue[0]);
+ auto iter = _langsMap.find(languageId);
+ if (iter == _langsMap.end())
+ {
+ TC_LOG_WARN("languages.spell", "LoadSpellEffectLanguage called on Spell %u with language %u which does not exist in Language.db2!",
+ spellEffect->SpellID, languageId);
+ return;
+ }
+ LanguageDesc& desc = iter->second;
+ desc.SpellId = spellEffect->SpellID;
+}
+
+uint32 LanguageMgr::GetSpellLanguage(uint32 spellId) const
+{
+ if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE))
+ {
+ SpellEffectInfoVector const& effects = spellInfo->GetEffects();
+ if (effects.size() != 1 || effects[0]->Effect != SPELL_EFFECT_LANGUAGE)
+ TC_LOG_WARN("languages.spell", "Invalid language spell %u. Expected 1 effect with SPELL_EFFECT_LANGUAGE", spellId);
+ else
+ return effects[0]->MiscValue;
+ }
+ return 0;
+}
+
+bool LanguageMgr::IsRelevantLanguageSkill(SkillLineEntry const* skillLineEntry) const
+{
+ if (!skillLineEntry)
+ return false;
+
+ SkillRaceClassInfoEntry const* entry = sDB2Manager.GetAvailableSkillRaceClassInfo(skillLineEntry->ID);
+ return entry != nullptr;
+}
+
+void LanguageMgr::LoadLanguages()
+{
+ uint32 oldMSTime = getMSTime();
+
+ // Load languages from Languages.db2. Just the id, we don't need the name
+ for (LanguagesEntry const* langEntry : sLanguagesStore)
+ _langsMap.emplace(langEntry->ID, LanguageDesc());
+
+ // Add the languages used in code in case they don't exist
+ _langsMap.emplace(LANG_UNIVERSAL, LanguageDesc());
+ _langsMap.emplace(LANG_ADDON, LanguageDesc());
+ _langsMap.emplace(LANG_ADDON_LOGGED, LanguageDesc());
+
+ // Log load time
+ TC_LOG_INFO("server.loading", ">> Loaded %u languages in %u ms", uint32(_langsMap.size()), GetMSTimeDiffToNow(oldMSTime));
+}
+
+void LanguageMgr::LoadLanguagesSkills()
+{
+ uint32 oldMSTime = getMSTime();
+
+ uint32 count = 0;
+ for (SkillLineEntry const* skillLineEntry : sSkillLineStore)
+ {
+ if (skillLineEntry->CategoryID != SKILL_CATEGORY_LANGUAGES)
+ continue;
+
+ if (!IsRelevantLanguageSkill(skillLineEntry))
+ continue;
+
+ std::vector<SkillLineAbilityEntry const*> const* skills = sDB2Manager.GetSkillLineAbilitiesBySkill(skillLineEntry->ID);
+
+ // We're expecting only 1 skill
+ if (skills->size() != 1)
+ TC_LOG_WARN("server.loading", "Found language skill line with %u spells. Expected 1. Will use 1st if available", uint32(skills->size()));
+
+ if (SkillLineAbilityEntry const* ability = skills->empty() ? nullptr : skills->at(0))
+ {
+ if (uint32 languageId = GetSpellLanguage(ability->Spell))
+ {
+ auto iter = _langsMap.find(languageId);
+ if (iter == _langsMap.cend())
+ TC_LOG_WARN("server.loading", "Spell %u has language %u, which doesn't exist in Languages.db2", ability->Spell, languageId);
+ else
+ {
+ iter->second.SpellId = ability->Spell;
+ iter->second.SkillId = skillLineEntry->ID;
+ ++count;
+ }
+ }
+ }
+ }
+
+ // Languages that don't have skills will be added in SpellMgr::LoadSpellInfoStore() (e.g. LANG_ZOMBIE, LANG_SHATH_YAR)
+
+ // Log load time
+ TC_LOG_INFO("server.loading", ">> Loaded %u languages skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+}
+
+void LanguageMgr::LoadLanguagesWords()
+{
+ uint32 oldMSTime = getMSTime();
+
+ uint32 wordsNum = 0;
+ for (LanguageWordsEntry const* wordEntry : sLanguageWordsStore)
+ {
+ uint8 length = std::min(18U, uint32(strlen(wordEntry->Word)));
+
+ WordKey key = WordKey(wordEntry->LanguageID, length);
+
+ auto result = _wordsMap.insert(std::make_pair(key, WordList()));
+ result.first->second.push_back(wordEntry->Word);
+ ++wordsNum;
+ }
+
+ // log load time
+ TC_LOG_INFO("server.loading", ">> Loaded %u word groups from %u words in %u ms", uint32(_wordsMap.size()), wordsNum, GetMSTimeDiffToNow(oldMSTime));
+}
+
+LanguageMgr::WordList const* LanguageMgr::FindWordGroup(uint32 language, uint32 wordLen) const
+{
+ WordsMap::const_iterator iter = _wordsMap.find(WordKey(language, wordLen));
+ if (iter != _wordsMap.end())
+ return &(iter->second);
+ return nullptr;
+}
+
+std::string LanguageMgr::Translate(std::string const& msg, uint32 sourcePlayerLanguage) const
+{
+ std::stringstream result;
+ Tokenizer tokens(msg, ' ');
+ bool first = true;
+ for (char const* str : tokens)
+ {
+ const char* nextPart = str;
+ uint32 wordLen = std::min(18U, (uint32)strlen(str));
+ LanguageMgr::WordList const* wordGroup = FindWordGroup(sourcePlayerLanguage, wordLen);
+ if (!wordGroup)
+ nextPart = "";
+ else
+ {
+ uint32 wordHash = SStrHash(str, true);
+ uint8 idxInsideGroup = wordHash % wordGroup->size();
+ nextPart = wordGroup->at(idxInsideGroup);
+ }
+
+ if (first)
+ first = false;
+ else
+ result << " ";
+
+ result << nextPart;
+ }
+ return result.str();
+}
+
+static char upper_backslash(char c) { return c == '/' ? '\\' : toupper(c); }
+
+static uint32 const s_hashtable[16] = {
+ 0x486E26EE, 0xDCAA16B3, 0xE1918EEF, 0x202DAFDB,
+ 0x341C7DC7, 0x1C365303, 0x40EF2D37, 0x65FD5E49,
+ 0xD6057177, 0x904ECE93, 0x1C38024F, 0x98FD323B,
+ 0xE3061AE7, 0xA39B0FA1, 0x9797F25F, 0xE4444563,
+};
+
+uint32 LanguageMgr::SStrHash(char const* string, bool caseInsensitive, uint32 seed) const
+{
+ ASSERT(string);
+
+ uint32 shift = 0xEEEEEEEE;
+ while (*string) {
+ char c = *string++;
+
+ if (caseInsensitive)
+ c = upper_backslash(c);
+
+ seed = (s_hashtable[c >> 4] - s_hashtable[c & 0xF]) ^ (shift + seed);
+ shift = c + seed + 33 * shift + 3;
+ }
+
+ return seed ? seed : 1;
+}
+
+bool LanguageMgr::IsLanguageExist(uint32 languageId) const
+{
+ auto iter = _langsMap.find(languageId);
+ return iter != _langsMap.cend();
+}
+
+LanguageDesc const* LanguageMgr::GetLanguageDescById(uint32 languageId) const
+{
+ auto iter = _langsMap.find(languageId);
+ if (iter == _langsMap.cend())
+ return nullptr;
+ return &(iter->second);
+}
diff --git a/src/server/game/Chat/LanguageMgr.h b/src/server/game/Chat/LanguageMgr.h
new file mode 100644
index 00000000000..57e5a6428eb
--- /dev/null
+++ b/src/server/game/Chat/LanguageMgr.h
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LANGUAGE_MGR_H
+#define _LANGUAGE_MGR_H
+
+#include "Define.h"
+#include "Hash.h"
+#include "SharedDefines.h"
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+struct LanguageDesc
+{
+ uint32 SpellId;
+ uint32 SkillId;
+};
+
+struct SkillLineEntry;
+struct SpellEffectEntry;
+
+class TC_GAME_API LanguageMgr
+{
+ friend class World;
+ friend class SpellMgr;
+
+ typedef std::pair<uint32, uint8> WordKey;
+ typedef std::vector<char const*> WordList;
+ typedef std::unordered_map<WordKey, WordList> WordsMap;
+
+ typedef std::unordered_map<uint32, LanguageDesc> LanguagesMap;
+
+ // Constructors
+ private:
+ LanguageMgr();
+ ~LanguageMgr();
+
+ // Accessors (const or static functions)
+ public:
+ static LanguageMgr* instance();
+
+ //
+ std::string Translate(std::string const& msg, uint32 sourcePlayerLanguage) const;
+
+ uint32 SStrHash(char const* string, bool caseInsensitive, uint32 seed = 0x7FED7FED) const;
+
+ bool IsLanguageExist(uint32 languageId) const;
+ LanguageDesc const* GetLanguageDescById(uint32 languageId) const;
+
+ /* Calls a callback for each available language.
+ * Callback signature: bool callback(uint32 lang, LanguageDesc const& languageDesc)
+ */
+ template <typename T>
+ bool ForEachLanguage(T callback)
+ {
+ for (LanguagesMap::value_type const& pair : _langsMap)
+ if (!callback(pair.first, pair.second))
+ return false;
+ return true;
+ }
+
+ private:
+ /* Create assosiation between language and spell id
+ * Language is taken from effect value.
+ * Assuming Effect == SPELL_EFFECT_LANGUAGE
+ */
+ void LoadSpellEffectLanguage(SpellEffectEntry const* spellEffect);
+
+ void LoadLanguagesWords();
+ void LoadLanguages();
+ void LoadLanguagesSkills();
+
+ bool IsRelevantLanguageSkill(SkillLineEntry const* skillLineEntry) const;
+
+ uint32 GetSpellLanguage(uint32 spellId) const;
+
+ WordList const* FindWordGroup(uint32 language, uint32 wordLen) const;
+
+ LanguagesMap _langsMap;
+ WordsMap _wordsMap;
+};
+
+#define sLanguageMgr LanguageMgr::instance()
+
+#endif
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index 848feec494d..bdd97316759 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -26,6 +26,7 @@
#include "Group.h"
#include "InstanceScript.h"
#include "Item.h"
+#include "LanguageMgr.h"
#include "LFGMgr.h"
#include "Log.h"
#include "LootMgr.h"
@@ -2624,9 +2625,9 @@ bool ConditionMgr::IsPlayerMeetingCondition(Player const* player, PlayerConditio
if (condition->LanguageID)
{
- if (LanguageDesc const* lang = GetLanguageDescByID(condition->LanguageID))
+ if (LanguageDesc const* langDesc = sLanguageMgr->GetLanguageDescById(condition->LanguageID))
{
- int32 languageSkill = player->GetSkillValue(lang->skill_id);
+ int32 languageSkill = player->GetSkillValue(langDesc->SkillId);
if (!languageSkill && player->HasAuraTypeWithMiscvalue(SPELL_AURA_COMPREHEND_LANGUAGE, condition->LanguageID))
languageSkill = 300;
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp
index 81e848bb87d..de9f0c1e0df 100644
--- a/src/server/game/DataStores/DB2Stores.cpp
+++ b/src/server/game/DataStores/DB2Stores.cpp
@@ -2782,6 +2782,15 @@ std::vector<SkillLineAbilityEntry const*> const* DB2Manager::GetSkillLineAbiliti
return Trinity::Containers::MapGetValuePtr(_skillLineAbilitiesBySkillupSkill, skillId);
}
+SkillRaceClassInfoEntry const* DB2Manager::GetAvailableSkillRaceClassInfo(uint32 skill) const
+{
+ auto bounds = _skillRaceClassInfoBySkill.equal_range(skill);
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
+ if (itr->second->Availability == 1)
+ return itr->second;
+ return nullptr;
+}
+
SkillRaceClassInfoEntry const* DB2Manager::GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_)
{
auto bounds = _skillRaceClassInfoBySkill.equal_range(skill);
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index 47ff813c03d..e442d5c0297 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -404,6 +404,7 @@ public:
std::vector<SkillLineEntry const*> const* GetSkillLinesForParentSkill(uint32 parentSkillId) const;
std::vector<SkillLineAbilityEntry const*> const* GetSkillLineAbilitiesBySkill(uint32 skillId) const;
SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_);
+ SkillRaceClassInfoEntry const* GetAvailableSkillRaceClassInfo(uint32 skill) const;
std::vector<SpecializationSpellsEntry const*> const* GetSpecializationSpells(uint32 specId) const;
bool IsSpecSetMember(int32 specSetId, uint32 specId) const;
static bool IsValidSpellFamiliyName(SpellFamilyNames family);
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 8a8ec028fa1..b18439244db 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -72,7 +72,7 @@
#include "InstanceScript.h"
#include "ItemPackets.h"
#include "KillRewarder.h"
-#include "Language.h"
+#include "LanguageMgr.h"
#include "LFGMgr.h"
#include "Log.h"
#include "LootItemStorage.h"
@@ -21966,9 +21966,58 @@ void Player::Say(std::string const& text, Language language, WorldObject const*
std::string _text(text);
sScriptMgr->OnPlayerChat(this, CHAT_MSG_SAY, language, _text);
- WorldPackets::Chat::Chat packet;
- packet.Initialize(CHAT_MSG_SAY, language, this, this, _text);
- SendMessageToSetInRange(packet.Write(), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true);
+ SendChatMessageToSetInRange(CHAT_MSG_SAY, language, std::move(_text), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY));
+}
+
+struct ChatMessageDistDelivererCustomizer
+{
+ // params
+ ChatMsg Type;
+ ::Language Language;
+ WorldObject const* Sender;
+ WorldObject const* Receiver;
+ std::string Text;
+
+ // caches
+ WorldPackets::Chat::Chat UntranslatedPacket;
+ mutable Optional<WorldPackets::Chat::Chat> TranslatedPacket;
+ uint32 LanguageSkillId;
+
+ ChatMessageDistDelivererCustomizer(ChatMsg chatType, ::Language language, WorldObject const* sender, WorldObject const* receiver, std::string message)
+ : Type(chatType), Language(language), Sender(sender), Receiver(receiver), Text(std::move(message)), LanguageSkillId(0)
+ {
+ UntranslatedPacket.Initialize(Type, Language, Sender, Receiver, Text);
+ UntranslatedPacket.Write();
+ if (LanguageDesc const* languageDesc = sLanguageMgr->GetLanguageDescById(language))
+ LanguageSkillId = languageDesc->SkillId;
+ }
+
+ WorldPacket const* operator()(Player* player) const
+ {
+ if (player->CanUnderstandLanguageSkillId(LanguageSkillId))
+ return UntranslatedPacket.GetRawPacket();
+
+ if (!TranslatedPacket)
+ {
+ TranslatedPacket.emplace();
+ TranslatedPacket->Initialize(Type, Language, Sender, Receiver, sLanguageMgr->Translate(Text, Language));
+ TranslatedPacket->Write();
+ }
+
+ return TranslatedPacket->GetRawPacket();
+ }
+};
+
+void Player::SendChatMessageToSetInRange(ChatMsg chatMsg, Language language, std::string&& text, float range)
+{
+ ChatMessageDistDelivererCustomizer customizer(chatMsg, language, this, this, std::move(text));
+
+ // Send to self
+ SendDirectMessage(customizer.UntranslatedPacket.GetRawPacket());
+
+ // Send to players
+ Trinity::MessageDistDeliverer<ChatMessageDistDelivererCustomizer> notifier(this, std::move(customizer), range);
+ Cell::VisitWorldObjects(this, notifier, range);
}
void Player::Say(uint32 textId, WorldObject const* target /*= nullptr*/)
@@ -21981,9 +22030,7 @@ void Player::Yell(std::string const& text, Language language, WorldObject const*
std::string _text(text);
sScriptMgr->OnPlayerChat(this, CHAT_MSG_YELL, language, _text);
- WorldPackets::Chat::Chat packet;
- packet.Initialize(CHAT_MSG_YELL, language, this, this, _text);
- SendMessageToSetInRange(packet.Write(), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true);
+ SendChatMessageToSetInRange(CHAT_MSG_YELL, language, std::move(_text), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL));
}
void Player::Yell(uint32 textId, WorldObject const* target /*= nullptr*/)
@@ -22032,14 +22079,14 @@ void Player::Whisper(std::string const& text, Language language, Player* target,
sScriptMgr->OnPlayerChat(this, CHAT_MSG_WHISPER, language, _text, target);
WorldPackets::Chat::Chat packet;
- packet.Initialize(CHAT_MSG_WHISPER, Language(language), this, this, _text);
+ packet.Initialize(CHAT_MSG_WHISPER, language, this, this, _text);
target->SendDirectMessage(packet.Write());
// rest stuff shouldn't happen in case of addon message
if (isAddonMessage)
return;
- packet.Initialize(CHAT_MSG_WHISPER_INFORM, Language(language), target, target, _text);
+ packet.Initialize(CHAT_MSG_WHISPER_INFORM, language, target, target, _text);
SendDirectMessage(packet.Write());
if (!isAcceptWhispers() && !IsGameMaster() && !target->IsGameMaster())
@@ -22073,6 +22120,11 @@ void Player::Whisper(uint32 textId, Player* target, bool /*isBossWhisper = false
target->SendDirectMessage(packet.Write());
}
+bool Player::CanUnderstandLanguageSkillId(uint32 langSkillId) const
+{
+ return IsGameMaster() || (langSkillId && HasSkill(langSkillId));
+}
+
Item* Player::GetMItem(ObjectGuid::LowType id)
{
ItemMap::const_iterator itr = mMitems.find(id);
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 5d66e6d451c..b664a7d6b62 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1166,6 +1166,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void Whisper(uint32 textId, Player* target, bool isBossWhisper = false) override;
void WhisperAddon(std::string const& text, std::string const& prefix, bool isLogged, Player* receiver);
+ bool CanUnderstandLanguageSkillId(uint32 langSkillId) const;
+
/*********************************************************/
/*** STORAGE SYSTEM ***/
/*********************************************************/
@@ -2000,6 +2002,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool own_team_only) const;
void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const override;
+ void SendChatMessageToSetInRange(ChatMsg chatMsg, Language lanugageId, std::string&& text, float range);
+
Corpse* GetCorpse() const;
void SpawnCorpseBones(bool triggerSave = true);
Corpse* CreateCorpse();
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 66512ae370e..380993963bc 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -176,52 +176,6 @@ ExtendedPlayerName ExtractExtendedPlayerName(std::string const& name)
return ExtendedPlayerName(name, "");
}
-LanguageDesc lang_description[LANGUAGES_COUNT] =
-{
- { LANG_ADDON, 0, 0 },
- { LANG_ADDON_LOGGED, 0, 0 },
- { LANG_UNIVERSAL, 0, 0 },
- { LANG_ORCISH, 669, SKILL_LANGUAGE_ORCISH },
- { LANG_DARNASSIAN, 671, SKILL_LANGUAGE_DARNASSIAN },
- { LANG_TAURAHE, 670, SKILL_LANGUAGE_TAURAHE },
- { LANG_DWARVISH, 672, SKILL_LANGUAGE_DWARVEN },
- { LANG_COMMON, 668, SKILL_LANGUAGE_COMMON },
- { LANG_DEMONIC, 815, SKILL_LANGUAGE_DEMON_TONGUE },
- { LANG_TITAN, 816, SKILL_LANGUAGE_TITAN },
- { LANG_THALASSIAN, 813, SKILL_LANGUAGE_THALASSIAN },
- { LANG_DRACONIC, 814, SKILL_LANGUAGE_DRACONIC },
- { LANG_KALIMAG, 265462, SKILL_LANGUAGE_OLD_TONGUE },
- { LANG_GNOMISH, 7340, SKILL_LANGUAGE_GNOMISH },
- { LANG_TROLL, 7341, SKILL_LANGUAGE_TROLL },
- { LANG_GUTTERSPEAK, 17737, SKILL_LANGUAGE_FORSAKEN },
- { LANG_DRAENEI, 29932, SKILL_LANGUAGE_DRAENEI },
- { LANG_ZOMBIE, 265467, 0 },
- { LANG_GNOMISH_BINARY, 265460, 0 },
- { LANG_GOBLIN_BINARY, 265461, 0 },
- { LANG_WORGEN, 69270, SKILL_LANGUAGE_GILNEAN },
- { LANG_GOBLIN, 69269, SKILL_LANGUAGE_GOBLIN },
- { LANG_PANDAREN_NEUTRAL, 108127, SKILL_LANGUAGE_PANDAREN_NEUTRAL },
- { LANG_PANDAREN_ALLIANCE, 108130, 0 },
- { LANG_PANDAREN_HORDE, 108131, 0 },
- { LANG_SPRITE, 265466, 0 },
- { LANG_SHATH_YAR, 265465, 0 },
- { LANG_NERGLISH, 265464, 0 },
- { LANG_MOONKIN, 265463, 0 },
- { LANG_SHALASSIAN, 262439, SKILL_LANGUAGE_SHALASSIAN },
- { LANG_THALASSIAN_2, 262454, SKILL_LANGUAGE_THALASSIAN_2 }
-};
-
-LanguageDesc const* GetLanguageDescByID(uint32 lang)
-{
- for (uint8 i = 0; i < LANGUAGES_COUNT; ++i)
- {
- if (uint32(lang_description[i].lang_id) == lang)
- return &lang_description[i];
- }
-
- return nullptr;
-}
-
bool SpellClickInfo::IsFitToRequirements(Unit const* clicker, Unit const* clickee) const
{
Player const* playerClicker = clicker->ToPlayer();
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 2ab408eb9d9..66d2f3e4b89 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -918,16 +918,6 @@ struct ExtendedPlayerName
ExtendedPlayerName ExtractExtendedPlayerName(std::string const& name);
-struct LanguageDesc
-{
- Language lang_id;
- uint32 spell_id;
- uint32 skill_id;
-};
-
-TC_GAME_API extern LanguageDesc lang_description[LANGUAGES_COUNT];
-LanguageDesc const* GetLanguageDescByID(uint32 lang);
-
enum EncounterCreditType : uint8
{
ENCOUNTER_CREDIT_KILL_CREATURE = 0,
diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp
index ad0c2435685..97ce6ab32a5 100644
--- a/src/server/game/Handlers/ChatHandler.cpp
+++ b/src/server/game/Handlers/ChatHandler.cpp
@@ -30,6 +30,7 @@
#include "Guild.h"
#include "GuildMgr.h"
#include "Language.h"
+#include "LanguageMgr.h"
#include "Log.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
@@ -76,17 +77,17 @@ void WorldSession::HandleChatMessageOpcode(WorldPackets::Chat::ChatMessage& chat
return;
}
- HandleChatMessage(type, chatMessage.Language, chatMessage.Text);
+ HandleChatMessage(type, Language(chatMessage.Language), chatMessage.Text);
}
void WorldSession::HandleChatMessageWhisperOpcode(WorldPackets::Chat::ChatMessageWhisper& chatMessageWhisper)
{
- HandleChatMessage(CHAT_MSG_WHISPER, chatMessageWhisper.Language, chatMessageWhisper.Text, chatMessageWhisper.Target);
+ HandleChatMessage(CHAT_MSG_WHISPER, Language(chatMessageWhisper.Language), chatMessageWhisper.Text, chatMessageWhisper.Target);
}
void WorldSession::HandleChatMessageChannelOpcode(WorldPackets::Chat::ChatMessageChannel& chatMessageChannel)
{
- HandleChatMessage(CHAT_MSG_CHANNEL, chatMessageChannel.Language, chatMessageChannel.Text, chatMessageChannel.Target);
+ HandleChatMessage(CHAT_MSG_CHANNEL, Language(chatMessageChannel.Language), chatMessageChannel.Text, chatMessageChannel.Target);
}
void WorldSession::HandleChatMessageEmoteOpcode(WorldPackets::Chat::ChatMessageEmote& chatMessageEmote)
@@ -94,7 +95,7 @@ void WorldSession::HandleChatMessageEmoteOpcode(WorldPackets::Chat::ChatMessageE
HandleChatMessage(CHAT_MSG_EMOTE, LANG_UNIVERSAL, chatMessageEmote.Text);
}
-void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg, std::string target /*= ""*/)
+void WorldSession::HandleChatMessage(ChatMsg type, Language lang, std::string msg, std::string target /*= ""*/)
{
Player* sender = GetPlayer();
@@ -106,14 +107,14 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg,
}
// prevent talking at unknown language (cheating)
- LanguageDesc const* langDesc = GetLanguageDescByID(lang);
+ LanguageDesc const* langDesc = sLanguageMgr->GetLanguageDescById(lang);
if (!langDesc)
{
SendNotification(LANG_UNKNOWN_LANGUAGE);
return;
}
- if (langDesc->skill_id != 0 && !sender->HasSkill(langDesc->skill_id))
+ if (langDesc->SkillId != 0 && !sender->HasSkill(langDesc->SkillId))
{
// also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language)
Unit::AuraEffectList const& langAuras = sender->GetAuraEffectsByType(SPELL_AURA_COMPREHEND_LANGUAGE);
@@ -166,7 +167,7 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg,
// but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used)
Unit::AuraEffectList const& ModLangAuras = sender->GetAuraEffectsByType(SPELL_AURA_MOD_LANGUAGE);
if (!ModLangAuras.empty())
- lang = ModLangAuras.front()->GetMiscValue();
+ lang = Language(ModLangAuras.front()->GetMiscValue());
}
if (!CanSpeak())
@@ -217,7 +218,7 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg,
return;
}
- sender->Say(msg, Language(lang));
+ sender->Say(msg, lang);
break;
}
case CHAT_MSG_EMOTE:
@@ -247,7 +248,7 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg,
return;
}
- sender->Yell(msg, Language(lang));
+ sender->Yell(msg, lang);
break;
}
case CHAT_MSG_WHISPER:
@@ -291,7 +292,7 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg,
(HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS) && !sender->isAcceptWhispers() && !sender->IsInWhisperWhiteList(receiver->GetGUID())))
sender->AddWhisperWhiteList(receiver->GetGUID());
- GetPlayer()->Whisper(msg, Language(lang), receiver);
+ GetPlayer()->Whisper(msg, lang, receiver);
break;
}
case CHAT_MSG_PARTY:
@@ -311,7 +312,7 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg,
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
WorldPackets::Chat::Chat packet;
- packet.Initialize(ChatMsg(type), Language(lang), sender, nullptr, msg);
+ packet.Initialize(ChatMsg(type), lang, sender, nullptr, msg);
group->BroadcastPacket(packet.Write(), false, group->GetMemberGroup(GetPlayer()->GetGUID()));
break;
}
@@ -353,7 +354,7 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg,
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
WorldPackets::Chat::Chat packet;
- packet.Initialize(ChatMsg(type), Language(lang), sender, nullptr, msg);
+ packet.Initialize(ChatMsg(type), lang, sender, nullptr, msg);
group->BroadcastPacket(packet.Write(), false);
break;
}
@@ -367,7 +368,7 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg,
WorldPackets::Chat::Chat packet;
//in battleground, raid warning is sent only to players in battleground - code is ok
- packet.Initialize(CHAT_MSG_RAID_WARNING, Language(lang), sender, nullptr, msg);
+ packet.Initialize(CHAT_MSG_RAID_WARNING, lang, sender, nullptr, msg);
group->BroadcastPacket(packet.Write(), false);
break;
}
@@ -401,7 +402,7 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg,
sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
WorldPackets::Chat::Chat packet;
- packet.Initialize(ChatMsg(type), Language(lang), sender, nullptr, msg);
+ packet.Initialize(ChatMsg(type), lang, sender, nullptr, msg);
group->BroadcastPacket(packet.Write(), false);
break;
}
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 1874b9ab921..e401b044c4b 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -1068,8 +1068,6 @@ enum Language
LANG_VULPERA = 285
};
-#define LANGUAGES_COUNT 31
-
enum TeamId
{
TEAM_ALLIANCE = 0,
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index df5610e96a7..3a84425e49c 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -1493,7 +1493,7 @@ class TC_GAME_API WorldSession
void HandleChatMessageOpcode(WorldPackets::Chat::ChatMessage& chatMessage);
void HandleChatMessageWhisperOpcode(WorldPackets::Chat::ChatMessageWhisper& chatMessageWhisper);
void HandleChatMessageChannelOpcode(WorldPackets::Chat::ChatMessageChannel& chatMessageChannel);
- void HandleChatMessage(ChatMsg type, uint32 lang, std::string msg, std::string target = "");
+ void HandleChatMessage(ChatMsg type, Language lang, std::string msg, std::string target = "");
void HandleChatAddonMessageOpcode(WorldPackets::Chat::ChatAddonMessage& chatAddonMessage);
void HandleChatAddonMessageTargetedOpcode(WorldPackets::Chat::ChatAddonMessageTargeted& chatAddonMessageTargeted);
void HandleChatAddonMessage(ChatMsg type, std::string prefix, std::string text, bool isLogged, std::string target = "");
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 76b7aa34161..801f7b06fc6 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -23,6 +23,7 @@
#include "Containers.h"
#include "DB2Stores.h"
#include "DatabaseEnv.h"
+#include "LanguageMgr.h"
#include "Log.h"
#include "MotionMaster.h"
#include "ObjectMgr.h"
@@ -2485,6 +2486,9 @@ void SpellMgr::LoadSpellInfoStore()
if (summonProperties->Slot == SUMMON_SLOT_MINIPET && summonProperties->Flags & SUMMON_PROP_FLAG_COMPANION)
if (BattlePetSpeciesEntry const* battlePetSpecies = Trinity::Containers::MapGetValuePtr(battlePetSpeciesByCreature, effect->EffectMiscValue[0]))
mBattlePets[effect->SpellID] = battlePetSpecies;
+
+ if (effect->Effect == SPELL_EFFECT_LANGUAGE)
+ sLanguageMgr->LoadSpellEffectLanguage(effect);
}
for (SpellAuraOptionsEntry const* auraOptions : sSpellAuraOptionsStore)
diff --git a/src/server/game/Texts/ChatTextBuilder.h b/src/server/game/Texts/ChatTextBuilder.h
index df24bbed18e..d5304e0c09e 100644
--- a/src/server/game/Texts/ChatTextBuilder.h
+++ b/src/server/game/Texts/ChatTextBuilder.h
@@ -19,7 +19,6 @@
#define __CHATTEXT_BUILDER_H
#include "Common.h"
-#include "ChatPackets.h"
#include "SharedDefines.h"
#include <string>
diff --git a/src/server/game/Texts/CreatureTextMgr.cpp b/src/server/game/Texts/CreatureTextMgr.cpp
index 5bd16c95c20..430af3e1a55 100644
--- a/src/server/game/Texts/CreatureTextMgr.cpp
+++ b/src/server/game/Texts/CreatureTextMgr.cpp
@@ -24,6 +24,7 @@
#include "DatabaseEnv.h"
#include "DB2Stores.h"
#include "GridNotifiersImpl.h"
+#include "LanguageMgr.h"
#include "Log.h"
#include "MiscPackets.h"
#include "ObjectMgr.h"
@@ -81,7 +82,7 @@ void CreatureTextMgr::LoadCreatureTexts()
}
}
- if (!GetLanguageDescByID(temp.lang))
+ if (!sLanguageMgr->IsLanguageExist(temp.lang))
{
TC_LOG_ERROR("sql.sql", "CreatureTextMgr: Entry %u, Group %u in table `creature_text` using Language %u but Language does not exist.", temp.creatureId, temp.groupId, uint32(temp.lang));
temp.lang = LANG_UNIVERSAL;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index d7a35ef9af3..bbcfce3ed97 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -59,6 +59,7 @@
#include "InstanceSaveMgr.h"
#include "IPLocation.h"
#include "Language.h"
+#include "LanguageMgr.h"
#include "LFGMgr.h"
#include "Log.h"
#include "LootItemStorage.h"
@@ -1755,6 +1756,12 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Initializing PlayerDump tables...");
PlayerDump::InitializeTables();
+ TC_LOG_INFO("server.loading", "Loading languages...");
+ sLanguageMgr->LoadLanguages();
+
+ TC_LOG_INFO("server.loading", "Loading languages words...");
+ sLanguageMgr->LoadLanguagesWords();
+
TC_LOG_INFO("server.loading", "Loading SpellInfo store...");
sSpellMgr->LoadSpellInfoStore();
@@ -1776,6 +1783,9 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading SpellInfo immunity infos...");
sSpellMgr->LoadSpellInfoImmunities();
+ TC_LOG_INFO("server.loading", "Loading languages skills...");
+ sLanguageMgr->LoadLanguagesSkills();
+
TC_LOG_INFO("server.loading", "Loading PetFamilySpellsStore Data...");
sSpellMgr->LoadPetFamilySpellsStore();
diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp
index c9f78eb61e2..b67c1427f63 100644
--- a/src/server/scripts/Commands/cs_learn.cpp
+++ b/src/server/scripts/Commands/cs_learn.cpp
@@ -26,6 +26,7 @@ EndScriptData */
#include "Chat.h"
#include "DB2Stores.h"
#include "Language.h"
+#include "LanguageMgr.h"
#include "ObjectMgr.h"
#include "Pet.h"
#include "Player.h"
@@ -288,9 +289,11 @@ public:
static bool HandleLearnAllLangCommand(ChatHandler* handler, char const* /*args*/)
{
- for (LanguageDesc const& langDesc : lang_description)
- if (uint32 langSpellId = langDesc.spell_id)
- handler->GetSession()->GetPlayer()->LearnSpell(langSpellId, false);
+ sLanguageMgr->ForEachLanguage([handler](uint32 /*lang*/, LanguageDesc const& languageDesc)
+ {
+ handler->GetSession()->GetPlayer()->LearnSpell(languageDesc.SpellId, false);
+ return true;
+ });
handler->SendSysMessage(LANG_COMMAND_LEARN_ALL_LANG);
return true;