/* * Copyright (C) 2008-2017 TrinityCore * * 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 . */ #ifndef TRINITY_CREATURE_TEXT_MGR_H #define TRINITY_CREATURE_TEXT_MGR_H #include "Creature.h" #include "GridNotifiers.h" #include "ObjectAccessor.h" #include "SharedDefines.h" #include "Opcodes.h" #include "Group.h" #include "Packets/ChatPackets.h" #include "World.h" enum CreatureTextRange { TEXT_RANGE_NORMAL = 0, TEXT_RANGE_AREA = 1, TEXT_RANGE_ZONE = 2, TEXT_RANGE_MAP = 3, TEXT_RANGE_WORLD = 4 }; struct CreatureTextEntry { uint32 entry; uint8 group; uint8 id; std::string text; ChatMsg type; Language lang; float probability; Emote emote; uint32 duration; uint32 sound; uint32 BroadcastTextId; CreatureTextRange TextRange; }; struct CreatureTextLocale { std::vector Text; }; struct CreatureTextId { CreatureTextId(uint32 e, uint32 g, uint32 i) : entry(e), textGroup(g), textId(i) { } bool operator<(CreatureTextId const& right) const { return memcmp(this, &right, sizeof(CreatureTextId)) < 0; } uint32 entry; uint32 textGroup; uint32 textId; }; typedef std::vector CreatureTextGroup; // texts in a group typedef std::unordered_map CreatureTextHolder; // groups for a creature by groupid typedef std::unordered_map CreatureTextMap; // all creatures by entry typedef std::map LocaleCreatureTextMap; class TC_GAME_API CreatureTextMgr { private: CreatureTextMgr() { } ~CreatureTextMgr() { } public: static CreatureTextMgr* instance(); void LoadCreatureTexts(); void LoadCreatureTextLocales(); CreatureTextMap const& GetTextMap() const { return mTextMap; } static void SendSound(Creature* source, uint32 sound, ChatMsg msgType, WorldObject const* whisperTarget = nullptr, CreatureTextRange range = TEXT_RANGE_NORMAL, Team team = TEAM_OTHER, bool gmOnly = false); static void SendEmote(Unit* source, uint32 emote); //if sent, returns the 'duration' of the text else 0 if error uint32 SendChat(Creature* source, uint8 textGroup, WorldObject const* whisperTarget = nullptr, ChatMsg msgType = CHAT_MSG_ADDON, Language language = LANG_ADDON, CreatureTextRange range = TEXT_RANGE_NORMAL, uint32 sound = 0, Team team = TEAM_OTHER, bool gmOnly = false, Player* srcPlr = nullptr); bool TextExist(uint32 sourceEntry, uint8 textGroup) const; std::string GetLocalizedChatString(uint32 entry, uint8 gender, uint8 textGroup, uint32 id, LocaleConstant locale) const; template static void SendChatPacket(WorldObject* source, Builder const& builder, ChatMsg msgType, WorldObject const* whisperTarget = nullptr, CreatureTextRange range = TEXT_RANGE_NORMAL, Team team = TEAM_OTHER, bool gmOnly = false); private: CreatureTextRepeatIds GetRepeatGroup(Creature* source, uint8 textGroup) const; void SetRepeatId(Creature* source, uint8 textGroup, uint8 id); static void SendNonChatPacket(WorldObject* source, WorldPacket const* data, ChatMsg msgType, WorldObject const* whisperTarget, CreatureTextRange range, Team team, bool gmOnly); static float GetRangeForChatType(ChatMsg msgType); CreatureTextMap mTextMap; LocaleCreatureTextMap mLocaleTextMap; }; #define sCreatureTextMgr CreatureTextMgr::instance() template class CreatureTextLocalizer { public: CreatureTextLocalizer(Builder const& builder, ChatMsg msgType) : _builder(builder), _msgType(msgType) { _packetCache.resize(TOTAL_LOCALES, NULL); } ~CreatureTextLocalizer() { for (size_t i = 0; i < _packetCache.size(); ++i) delete _packetCache[i]; } void operator()(Player const* player) const { LocaleConstant loc_idx = player->GetSession()->GetSessionDbLocaleIndex(); WorldPackets::Chat::Chat* messageTemplate; // create if not cached yet if (!_packetCache[loc_idx]) { messageTemplate = static_cast(_builder(loc_idx)); messageTemplate->Write(); _packetCache[loc_idx] = messageTemplate; } else messageTemplate = _packetCache[loc_idx]; switch (_msgType) { case CHAT_MSG_MONSTER_WHISPER: case CHAT_MSG_RAID_BOSS_WHISPER: { WorldPackets::Chat::Chat message(*messageTemplate); message.SetReceiver(player, loc_idx); player->SendDirectMessage(message.Write()); return; } default: break; } player->SendDirectMessage(messageTemplate->GetRawPacket()); } private: mutable std::vector _packetCache; Builder const& _builder; ChatMsg _msgType; }; template void CreatureTextMgr::SendChatPacket(WorldObject* source, Builder const& builder, ChatMsg msgType, WorldObject const* whisperTarget /*= nullptr*/, CreatureTextRange range /*= TEXT_RANGE_NORMAL*/, Team team /*= TEAM_OTHER*/, bool gmOnly /*= false*/) { if (!source) return; CreatureTextLocalizer localizer(builder, msgType); switch (msgType) { case CHAT_MSG_MONSTER_PARTY: { if (!whisperTarget) return; if (Player const* whisperPlayer = whisperTarget->ToPlayer()) if (Group const* group = whisperPlayer->GetGroup()) group->BroadcastWorker(localizer); return; } case CHAT_MSG_MONSTER_WHISPER: case CHAT_MSG_RAID_BOSS_WHISPER: { if (range == TEXT_RANGE_NORMAL) // ignores team and gmOnly { if (!whisperTarget || whisperTarget->GetTypeId() != TYPEID_PLAYER) return; localizer(const_cast(whisperTarget->ToPlayer())); return; } break; } default: break; } switch (range) { case TEXT_RANGE_AREA: { uint32 areaId = source->GetAreaId(); Map::PlayerList const& players = source->GetMap()->GetPlayers(); for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) if (itr->GetSource()->GetAreaId() == areaId && (!team || Team(itr->GetSource()->GetTeam()) == team) && (!gmOnly || itr->GetSource()->IsGameMaster())) localizer(itr->GetSource()); return; } case TEXT_RANGE_ZONE: { uint32 zoneId = source->GetZoneId(); Map::PlayerList const& players = source->GetMap()->GetPlayers(); for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) if (itr->GetSource()->GetZoneId() == zoneId && (!team || Team(itr->GetSource()->GetTeam()) == team) && (!gmOnly || itr->GetSource()->IsGameMaster())) localizer(itr->GetSource()); return; } case TEXT_RANGE_MAP: { Map::PlayerList const& players = source->GetMap()->GetPlayers(); for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) if ((!team || Team(itr->GetSource()->GetTeam()) == team) && (!gmOnly || itr->GetSource()->IsGameMaster())) localizer(itr->GetSource()); return; } case TEXT_RANGE_WORLD: { SessionMap const& smap = sWorld->GetAllSessions(); for (SessionMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter) if (Player* player = iter->second->GetPlayer()) if ((!team || Team(player->GetTeam()) == team) && (!gmOnly || player->IsGameMaster())) localizer(player); return; } case TEXT_RANGE_NORMAL: default: break; } float dist = GetRangeForChatType(msgType); Trinity::PlayerDistWorker> worker(source, dist, localizer); Cell::VisitWorldObjects(source, worker, dist); } #endif