/* * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2 * Copyright (C) 2008-2016 TrinityCore * Copyright (C) 2005-2009 MaNGOS */ #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" 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; CreatureTextRange TextRange; uint32 BroadcastTextId; }; struct CreatureTextLocale { StringVector 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 UNORDERED_MAP CreatureTextHolder; // groups for a creature by groupid typedef UNORDERED_MAP CreatureTextMap; // all creatures by entry typedef std::map LocaleCreatureTextMap; //used for handling non-repeatable random texts typedef std::vector CreatureTextRepeatIds; typedef UNORDERED_MAP CreatureTextRepeatGroup; typedef UNORDERED_MAP CreatureTextRepeatMap;//guid based class CreatureTextMgr { friend class ACE_Singleton; CreatureTextMgr() { } public: ~CreatureTextMgr() { } void LoadCreatureTexts(); void LoadCreatureTextLocales(); CreatureTextMap const& GetTextMap() const { return mTextMap; } void SendSound(Creature* source, uint32 sound, ChatMsg msgType, WorldObject const* whisperTarget, CreatureTextRange range, TeamId teamId, bool gmOnly); 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 = NULL, ChatMsg msgType = CHAT_MSG_ADDON, Language language = LANG_ADDON, CreatureTextRange range = TEXT_RANGE_NORMAL, uint32 sound = 0, TeamId teamId = TEAM_NEUTRAL, bool gmOnly = false, Player* srcPlr = NULL); bool TextExist(uint32 sourceEntry, uint8 textGroup); std::string GetLocalizedChatString(uint32 entry, uint8 gender, uint8 textGroup, uint32 id, LocaleConstant locale) const; template void SendChatPacket(WorldObject* source, Builder const& builder, ChatMsg msgType, WorldObject const* whisperTarget = NULL, CreatureTextRange range = TEXT_RANGE_NORMAL, TeamId teamId = TEAM_NEUTRAL, bool gmOnly = false) const; private: CreatureTextRepeatIds GetRepeatGroup(Creature* source, uint8 textGroup); void SetRepeatId(Creature* source, uint8 textGroup, uint8 id); void SendNonChatPacket(WorldObject* source, WorldPacket* data, ChatMsg msgType, WorldObject const* whisperTarget, CreatureTextRange range, TeamId teamId, bool gmOnly) const; float GetRangeForChatType(ChatMsg msgType) const; CreatureTextMap mTextMap; CreatureTextRepeatMap mTextRepeatMap; LocaleCreatureTextMap mLocaleTextMap; }; #define sCreatureTextMgr ACE_Singleton::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) { if (_packetCache[i]) delete _packetCache[i]->first; delete _packetCache[i]; } } void operator()(Player* player) { LocaleConstant loc_idx = player->GetSession()->GetSessionDbLocaleIndex(); WorldPacket* messageTemplate; size_t whisperGUIDpos; // create if not cached yet if (!_packetCache[loc_idx]) { messageTemplate = new WorldPacket(); whisperGUIDpos = _builder(messageTemplate, loc_idx); ASSERT(messageTemplate->GetOpcode() != MSG_NULL_ACTION); _packetCache[loc_idx] = new std::pair(messageTemplate, whisperGUIDpos); } else { messageTemplate = _packetCache[loc_idx]->first; whisperGUIDpos = _packetCache[loc_idx]->second; } WorldPacket data(*messageTemplate); switch (_msgType) { case CHAT_MSG_MONSTER_WHISPER: case CHAT_MSG_RAID_BOSS_WHISPER: data.put(whisperGUIDpos, player->GetGUID()); break; default: break; } player->SendDirectMessage(&data); } private: std::vector* > _packetCache; Builder const& _builder; ChatMsg _msgType; }; template void CreatureTextMgr::SendChatPacket(WorldObject* source, Builder const& builder, ChatMsg msgType, WorldObject const* whisperTarget /*= NULL*/, CreatureTextRange range /*= TEXT_RANGE_NORMAL*/, TeamId teamId /*= TEAM_NEUTRAL*/, bool gmOnly /*= false*/) const { if (!source) return; CreatureTextLocalizer localizer(builder, msgType); switch (msgType) { 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 && (teamId == TEAM_NEUTRAL || itr->GetSource()->GetTeamId() == teamId) && (!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 && (teamId == TEAM_NEUTRAL || itr->GetSource()->GetTeamId() == teamId) && (!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 ((teamId == TEAM_NEUTRAL || itr->GetSource()->GetTeamId() == teamId) && (!gmOnly || itr->GetSource()->IsGameMaster())) localizer(itr->GetSource()); return; } case TEXT_RANGE_WORLD: { SessionMap const& smap = sWorld->GetAllSessions(); for (SessionMap::const_iterator itr = smap.begin(); itr != smap.end(); ++itr) if (Player* player = itr->second->GetPlayer()) if ((teamId == TEAM_NEUTRAL || player->GetTeamId() == teamId) && (!gmOnly || player->IsGameMaster())) localizer(player); return; } case TEXT_RANGE_NORMAL: default: break; } float dist = GetRangeForChatType(msgType); // xinef: hack for boss emote if (msgType == CHAT_MSG_RAID_BOSS_EMOTE && source->GetMap()->IsDungeon()) dist = 250.0f; Trinity::PlayerDistWorker > worker(source, dist, localizer); source->VisitNearbyWorldObject(dist, worker); } #endif