/*
* Copyright (C) 2008-2014 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"
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;
};
enum CreatureTextRange
{
TEXT_RANGE_NORMAL = 0,
TEXT_RANGE_AREA = 1,
TEXT_RANGE_ZONE = 2,
TEXT_RANGE_MAP = 3,
TEXT_RANGE_WORLD = 4
};
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 std::unordered_map CreatureTextHolder; //groups for a creature by groupid
typedef std::unordered_map CreatureTextMap; //all creatures by entry
typedef std::map LocaleCreatureTextMap;
//used for handling non-repeatable random texts
typedef std::vector CreatureTextRepeatIds;
typedef std::unordered_map CreatureTextRepeatGroup;
typedef std::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, Team team, 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, Team team = TEAM_OTHER, 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, Team team = TEAM_OTHER, 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, Team team, 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*/, Team team /*= TEAM_OTHER*/, 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 && (!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 (player->GetSession() && (!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);
source->VisitNearbyWorldObject(dist, worker);
}
#endif