[8483] Implement glyph 43361.
[getmangos.git] / src / game / ChatHandler.cpp
blobf6bd8db92c870e987f8bffc09e1723035f03c359
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Common.h"
20 #include "Log.h"
21 #include "WorldPacket.h"
22 #include "WorldSession.h"
23 #include "World.h"
24 #include "Opcodes.h"
25 #include "ObjectMgr.h"
26 #include "Chat.h"
27 #include "Database/DatabaseEnv.h"
28 #include "ChannelMgr.h"
29 #include "Group.h"
30 #include "Guild.h"
31 #include "ObjectAccessor.h"
32 #include "ScriptCalls.h"
33 #include "Player.h"
34 #include "SpellAuras.h"
35 #include "Language.h"
36 #include "Util.h"
37 #include "GridNotifiersImpl.h"
38 #include "CellImpl.h"
40 bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang)
42 if (lang != LANG_ADDON)
44 // strip invisible characters for non-addon messages
45 if(sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
46 stripLineInvisibleChars(msg);
48 if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && GetSecurity() < SEC_MODERATOR
49 && !ChatHandler(this).isValidChatMessage(msg.c_str()))
51 sLog.outError("Player %s (GUID: %u) sent a chatmessage with an invalid link: %s", GetPlayer()->GetName(),
52 GetPlayer()->GetGUIDLow(), msg.c_str());
53 if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
54 KickPlayer();
55 return false;
59 return true;
62 void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
64 uint32 type;
65 uint32 lang;
67 recv_data >> type;
68 recv_data >> lang;
70 if(type >= MAX_CHAT_MSG_TYPE)
72 sLog.outError("CHAT: Wrong message type received: %u", type);
73 return;
76 //sLog.outDebug("CHAT: packet received. type %u, lang %u", type, lang );
78 // prevent talking at unknown language (cheating)
79 LanguageDesc const* langDesc = GetLanguageDescByID(lang);
80 if(!langDesc)
82 SendNotification(LANG_UNKNOWN_LANGUAGE);
83 return;
85 if(langDesc->skill_id != 0 && !_player->HasSkill(langDesc->skill_id))
87 // also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language)
88 Unit::AuraList const& langAuras = _player->GetAurasByType(SPELL_AURA_COMPREHEND_LANGUAGE);
89 bool foundAura = false;
90 for(Unit::AuraList::const_iterator i = langAuras.begin();i != langAuras.end(); ++i)
92 if((*i)->GetModifier()->m_miscvalue == int32(lang))
94 foundAura = true;
95 break;
98 if(!foundAura)
100 SendNotification(LANG_NOT_LEARNED_LANGUAGE);
101 return;
105 if(lang == LANG_ADDON)
107 // Disabled addon channel?
108 if(!sWorld.getConfig(CONFIG_ADDON_CHANNEL))
109 return;
111 // LANG_ADDON should not be changed nor be affected by flood control
112 else
114 // send in universal language if player in .gmon mode (ignore spell effects)
115 if (_player->isGameMaster())
116 lang = LANG_UNIVERSAL;
117 else
119 // send in universal language in two side iteration allowed mode
120 if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT))
121 lang = LANG_UNIVERSAL;
122 else
124 switch(type)
126 case CHAT_MSG_PARTY:
127 case CHAT_MSG_RAID:
128 case CHAT_MSG_RAID_LEADER:
129 case CHAT_MSG_RAID_WARNING:
130 // allow two side chat at group channel if two side group allowed
131 if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP))
132 lang = LANG_UNIVERSAL;
133 break;
134 case CHAT_MSG_GUILD:
135 case CHAT_MSG_OFFICER:
136 // allow two side chat at guild channel if two side guild allowed
137 if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD))
138 lang = LANG_UNIVERSAL;
139 break;
143 // but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used)
144 Unit::AuraList const& ModLangAuras = _player->GetAurasByType(SPELL_AURA_MOD_LANGUAGE);
145 if(!ModLangAuras.empty())
146 lang = ModLangAuras.front()->GetModifier()->m_miscvalue;
149 if (!_player->CanSpeak())
151 std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
152 SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str());
153 return;
156 if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND)
157 GetPlayer()->UpdateSpeakTime();
160 switch(type)
162 case CHAT_MSG_SAY:
163 case CHAT_MSG_EMOTE:
164 case CHAT_MSG_YELL:
166 std::string msg = "";
167 recv_data >> msg;
169 if(msg.empty())
170 break;
172 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
173 break;
175 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
176 return;
178 if(msg.empty())
179 break;
181 if(type == CHAT_MSG_SAY)
182 GetPlayer()->Say(msg, lang);
183 else if(type == CHAT_MSG_EMOTE)
184 GetPlayer()->TextEmote(msg);
185 else if(type == CHAT_MSG_YELL)
186 GetPlayer()->Yell(msg, lang);
187 } break;
189 case CHAT_MSG_WHISPER:
191 std::string to, msg;
192 recv_data >> to;
193 recv_data >> msg;
195 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
196 return;
198 if(msg.empty())
199 break;
201 if(!normalizePlayerName(to))
203 WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1));
204 data<<to;
205 SendPacket(&data);
206 break;
209 Player *player = objmgr.GetPlayer(to.c_str());
210 uint32 tSecurity = GetSecurity();
211 uint32 pSecurity = player ? player->GetSession()->GetSecurity() : SEC_PLAYER;
212 if (!player || (tSecurity == SEC_PLAYER && pSecurity > SEC_PLAYER && !player->isAcceptWhispers()))
214 WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1));
215 data<<to;
216 SendPacket(&data);
217 return;
220 if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && tSecurity == SEC_PLAYER && pSecurity == SEC_PLAYER )
222 uint32 sidea = GetPlayer()->GetTeam();
223 uint32 sideb = player->GetTeam();
224 if( sidea != sideb )
226 WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1));
227 data<<to;
228 SendPacket(&data);
229 return;
233 GetPlayer()->Whisper(msg, lang,player->GetGUID());
234 } break;
236 case CHAT_MSG_PARTY:
238 std::string msg = "";
239 recv_data >> msg;
241 if(msg.empty())
242 break;
244 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
245 break;
247 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
248 return;
250 if(msg.empty())
251 break;
253 // if player is in battleground, he cannot say to battleground members by /p
254 Group *group = GetPlayer()->GetOriginalGroup();
255 // so if player hasn't OriginalGroup and his player->GetGroup() is BG raid, then return
256 if( !group && (!(group = GetPlayer()->GetGroup()) || group->isBGGroup()) )
257 return;
259 WorldPacket data;
260 ChatHandler::FillMessageData(&data, this, CHAT_MSG_PARTY, lang, NULL, 0, msg.c_str(),NULL);
261 group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID()));
263 break;
264 case CHAT_MSG_GUILD:
266 std::string msg = "";
267 recv_data >> msg;
269 if(msg.empty())
270 break;
272 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
273 break;
275 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
276 return;
278 if(msg.empty())
279 break;
281 if (GetPlayer()->GetGuildId())
283 Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
284 if (guild)
285 guild->BroadcastToGuild(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
288 break;
290 case CHAT_MSG_OFFICER:
292 std::string msg = "";
293 recv_data >> msg;
295 if(msg.empty())
296 break;
298 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
299 break;
301 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
302 return;
304 if(msg.empty())
305 break;
307 if (GetPlayer()->GetGuildId())
309 Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
310 if (guild)
311 guild->BroadcastToOfficers(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
313 break;
315 case CHAT_MSG_RAID:
317 std::string msg="";
318 recv_data >> msg;
320 if(msg.empty())
321 break;
323 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
324 break;
326 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
327 return;
329 if(msg.empty())
330 break;
332 // if player is in battleground, he cannot say to battleground members by /ra
333 Group *group = GetPlayer()->GetOriginalGroup();
334 // so if player hasn't OriginalGroup and his player->GetGroup() is BG raid or his group isn't raid, then return
335 if ((!group && !(group = GetPlayer()->GetGroup())) || group->isBGGroup() || !group->isRaidGroup())
336 return;
338 WorldPacket data;
339 ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(),NULL);
340 group->BroadcastPacket(&data, false);
341 } break;
342 case CHAT_MSG_RAID_LEADER:
344 std::string msg="";
345 recv_data >> msg;
347 if(msg.empty())
348 break;
350 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
351 break;
353 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
354 return;
356 if(msg.empty())
357 break;
359 // if player is in battleground, he cannot say to battleground members by /ra
360 Group *group = GetPlayer()->GetOriginalGroup();
361 if ((!group && !(group = GetPlayer()->GetGroup())) || group->isBGGroup() || !group->isRaidGroup())
362 return;
364 WorldPacket data;
365 ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(),NULL);
366 group->BroadcastPacket(&data, false);
367 } break;
368 case CHAT_MSG_RAID_WARNING:
370 std::string msg="";
371 recv_data >> msg;
373 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
374 return;
376 if(msg.empty())
377 break;
379 Group *group = GetPlayer()->GetGroup();
380 if(!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())))
381 return;
383 WorldPacket data;
384 //in battleground, raid warning is sent only to players in battleground - code is ok
385 ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(),NULL);
386 group->BroadcastPacket(&data, false);
387 } break;
389 case CHAT_MSG_BATTLEGROUND:
391 std::string msg="";
392 recv_data >> msg;
394 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
395 return;
397 if(msg.empty())
398 break;
400 //battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
401 Group *group = GetPlayer()->GetGroup();
402 if(!group || !group->isBGGroup())
403 return;
405 WorldPacket data;
406 ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(),NULL);
407 group->BroadcastPacket(&data, false);
408 } break;
410 case CHAT_MSG_BATTLEGROUND_LEADER:
412 std::string msg="";
413 recv_data >> msg;
415 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
416 return;
418 if(msg.empty())
419 break;
421 //battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
422 Group *group = GetPlayer()->GetGroup();
423 if(!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID()))
424 return;
426 WorldPacket data;
427 ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(),NULL);
428 group->BroadcastPacket(&data, false);
429 } break;
431 case CHAT_MSG_CHANNEL:
433 std::string channel = "", msg = "";
434 recv_data >> channel;
436 recv_data >> msg;
438 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
439 return;
441 if(msg.empty())
442 break;
444 if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
446 if(Channel *chn = cMgr->GetChannel(channel,_player))
447 chn->Say(_player->GetGUID(),msg.c_str(),lang);
449 } break;
451 case CHAT_MSG_AFK:
453 std::string msg;
454 recv_data >> msg;
456 if((msg.empty() || !_player->isAFK()) && !_player->isInCombat() )
458 if(!_player->isAFK())
460 if(msg.empty())
461 msg = GetMangosString(LANG_PLAYER_AFK_DEFAULT);
462 _player->afkMsg = msg;
464 _player->ToggleAFK();
465 if(_player->isAFK() && _player->isDND())
466 _player->ToggleDND();
468 } break;
470 case CHAT_MSG_DND:
472 std::string msg;
473 recv_data >> msg;
475 if(msg.empty() || !_player->isDND())
477 if(!_player->isDND())
479 if(msg.empty())
480 msg = GetMangosString(LANG_PLAYER_DND_DEFAULT);
481 _player->dndMsg = msg;
483 _player->ToggleDND();
484 if(_player->isDND() && _player->isAFK())
485 _player->ToggleAFK();
487 } break;
489 default:
490 sLog.outError("CHAT: unknown message type %u, lang: %u", type, lang);
491 break;
495 void WorldSession::HandleEmoteOpcode( WorldPacket & recv_data )
497 if(!GetPlayer()->isAlive())
498 return;
500 uint32 emote;
501 recv_data >> emote;
502 GetPlayer()->HandleEmoteCommand(emote);
505 namespace MaNGOS
507 class EmoteChatBuilder
509 public:
510 EmoteChatBuilder(Player const& pl, uint32 text_emote, uint32 emote_num, Unit const* target)
511 : i_player(pl), i_text_emote(text_emote), i_emote_num(emote_num), i_target(target) {}
513 void operator()(WorldPacket& data, int32 loc_idx)
515 char const* nam = i_target ? i_target->GetNameForLocaleIdx(loc_idx) : NULL;
516 uint32 namlen = (nam ? strlen(nam) : 0) + 1;
518 data.Initialize(SMSG_TEXT_EMOTE, (20+namlen));
519 data << i_player.GetGUID();
520 data << (uint32)i_text_emote;
521 data << i_emote_num;
522 data << (uint32)namlen;
523 if( namlen > 1 )
524 data.append(nam, namlen);
525 else
526 data << (uint8)0x00;
529 private:
530 Player const& i_player;
531 uint32 i_text_emote;
532 uint32 i_emote_num;
533 Unit const* i_target;
535 } // namespace MaNGOS
537 void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data )
539 if(!GetPlayer()->isAlive())
540 return;
542 if (!GetPlayer()->CanSpeak())
544 std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
545 SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str());
546 return;
549 uint32 text_emote, emoteNum;
550 uint64 guid;
552 recv_data >> text_emote;
553 recv_data >> emoteNum;
554 recv_data >> guid;
556 EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote);
557 if (!em)
558 return;
560 uint32 emote_anim = em->textid;
562 switch(emote_anim)
564 case EMOTE_STATE_SLEEP:
565 case EMOTE_STATE_SIT:
566 case EMOTE_STATE_KNEEL:
567 case EMOTE_ONESHOT_NONE:
568 break;
569 default:
570 GetPlayer()->HandleEmoteCommand(emote_anim);
571 break;
574 Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
576 CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
578 Cell cell(p);
579 cell.data.Part.reserved = ALL_DISTRICT;
580 cell.SetNoCreate();
582 MaNGOS::EmoteChatBuilder emote_builder(*GetPlayer(), text_emote, emoteNum, unit);
583 MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > emote_do(emote_builder);
584 MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > > emote_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),emote_do);
585 TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > >, WorldTypeMapContainer > message(emote_worker);
586 CellLock<GridReadGuard> cell_lock(cell, p);
587 cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap());
589 GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, text_emote, 0, unit);
591 //Send scripted event call
592 if (unit && unit->GetTypeId()==TYPEID_UNIT && ((Creature*)unit)->AI())
593 ((Creature*)unit)->AI()->ReceiveEmote(GetPlayer(),text_emote);
596 void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data )
598 uint64 iguid;
599 uint8 unk;
600 //sLog.outDebug("WORLD: Received CMSG_CHAT_IGNORED");
602 recv_data >> iguid;
603 recv_data >> unk; // probably related to spam reporting
605 Player *player = objmgr.GetPlayer(iguid);
606 if(!player || !player->GetSession())
607 return;
609 WorldPacket data;
610 ChatHandler::FillMessageData(&data, this, CHAT_MSG_IGNORED, LANG_UNIVERSAL, NULL, GetPlayer()->GetGUID(), GetPlayer()->GetName(),NULL);
611 player->GetSession()->SendPacket(&data);