2010-10-07 13:38:35 +02:00
/*
* Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
*
* 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/>.
2010-09-25 18:47:03 +02:00
*/
#include "Common.h"
#include "DatabaseEnv.h"
#include "SQLStorage.h"
#include "CreatureTextMgr.h"
#include "ProgressBar.h"
#include "ObjectMgr.h"
void CreatureTextMgr :: LoadCreatureTexts ()
{
mTextMap . clear (); // for reload case
2010-09-26 20:48:25 +02:00
mTextRepeatMap . clear (); //reset all currently used temp texts
2010-09-25 18:47:03 +02:00
PreparedStatement * stmt = WorldDatabase . GetPreparedStatement ( WORLD_LOAD_CRETEXT );
PreparedQueryResult result = WorldDatabase . Query ( stmt );
2010-09-25 22:03:57 +02:00
2010-09-25 18:47:03 +02:00
if ( ! result )
{
barGoLink bar ( 1 );
bar . step ();
sLog . outString ();
sLog . outString ( ">> Loaded 0 Creature Texts. DB table `creature_texts` is empty." );
return ;
}
2010-09-25 22:03:57 +02:00
2010-09-25 18:47:03 +02:00
barGoLink bar ( result -> GetRowCount ());
uint32 TextCount = 0 ;
uint32 CreatureCount = 0 ;
do
{
bar . step ();
Field * fields = result -> Fetch ();
CreatureTextEntry temp ;
2010-09-29 23:26:28 +02:00
temp . entry = fields [ 0 ]. GetUInt32 ();
2010-09-25 18:47:03 +02:00
temp . group = fields [ 1 ]. GetUInt8 ();
temp . id = fields [ 2 ]. GetUInt8 ();
temp . text = fields [ 3 ]. GetString ();
temp . type = ChatType ( fields [ 4 ]. GetUInt8 ());
temp . lang = Language ( fields [ 5 ]. GetUInt8 ());
temp . probability = fields [ 6 ]. GetFloat ();
temp . emote = Emote ( fields [ 7 ]. GetUInt32 ());
temp . duration = fields [ 8 ]. GetUInt32 ();
temp . sound = fields [ 9 ]. GetUInt32 ();
if ( temp . sound )
{
if ( ! sSoundEntriesStore . LookupEntry ( temp . sound )){
sLog . outErrorDb ( "CreatureTextMgr: Entry %u, Group %u in table `creature_texts` has Sound %u but sound does not exist." , temp . entry , temp . group , temp . sound );
temp . sound = 0 ;
}
}
if ( ! GetLanguageDescByID ( temp . lang ))
{
sLog . outErrorDb ( "CreatureTextMgr: Entry %u, Group %u in table `creature_texts` using Language %u but Language does not exist." , temp . entry , temp . group , uint32 ( temp . lang ));
temp . lang = LANG_UNIVERSAL ;
}
2010-09-29 23:26:28 +02:00
if ( temp . type >= CHAT_TYPE_ZONE_YELL ) //CHAT_TYPE_ZONE_YELL and more will be used later
2010-09-25 18:47:03 +02:00
{
sLog . outErrorDb ( "CreatureTextMgr: Entry %u, Group %u in table `creature_texts` has Type %u but this Chat Type does not exist." , temp . entry , temp . group , uint32 ( temp . type ));
temp . type = CHAT_TYPE_SAY ;
}
if ( temp . emote )
{
if ( ! sEmotesStore . LookupEntry ( temp . emote ))
{
sLog . outErrorDb ( "CreatureTextMgr: Entry %u, Group %u in table `creature_texts` has Emote %u but emote does not exist." , temp . entry , temp . group , uint32 ( temp . emote ));
temp . emote = EMOTE_ONESHOT_NONE ;
}
}
//entry not yet added, add empty TextHolder (list of groups)
if ( mTextMap . find ( temp . entry ) == mTextMap . end ())
{
++ CreatureCount ;
CreatureTextHolder TextHolder ;
mTextMap [ temp . entry ] = TextHolder ;
}
//group not yet added, add empty TextGroup (list of texts)
if ( mTextMap [ temp . entry ]. find ( temp . group ) == mTextMap [ temp . entry ]. end ())
{
CreatureTextGroup TextGroup ;
mTextMap [ temp . entry ][ temp . group ] = TextGroup ;
}
//add the text into our entry's group
mTextMap [ temp . entry ][ temp . group ]. push_back ( temp );
2010-09-25 22:03:57 +02:00
2010-09-25 18:47:03 +02:00
++ TextCount ;
} while ( result -> NextRow ());
sLog . outString ();
sLog . outString ( ">> Loaded %u Creature Texts for %u Creatures." , TextCount , CreatureCount );
}
2010-09-25 23:52:17 +02:00
2010-09-29 23:26:28 +02:00
uint32 CreatureTextMgr :: SendChat ( Creature * source , uint8 textGroup , uint64 whisperGuid , ChatType msgtype , Language language , TextRange range , uint32 sound )
2010-09-25 23:52:17 +02:00
{
2010-09-26 20:48:25 +02:00
if ( ! source )
2010-09-29 23:26:28 +02:00
return 0 ;
2010-09-26 20:48:25 +02:00
CreatureTextMap :: const_iterator sList = mTextMap . find ( source -> GetEntry ());
if ( sList == mTextMap . end ())
{
sLog . outErrorDb ( "CreatureTextMgr: Could not find Text for Creature(%s) Entry %u in 'creature_text' table. Ignoring." , source -> GetName (), source -> GetEntry ());
2010-09-29 23:26:28 +02:00
return 0 ;
2010-09-26 20:48:25 +02:00
}
CreatureTextHolder TextHolder = ( * sList ). second ;
CreatureTextHolder :: const_iterator itr = TextHolder . find ( textGroup );
if ( itr == TextHolder . end ())
{
sLog . outErrorDb ( "CreatureTextMgr: Could not find TextGroup %u for Creature(%s) GuidLow %u Entry %u. Ignoring." , uint32 ( textGroup ), source -> GetName (), source -> GetGUIDLow (), source -> GetEntry ());
2010-09-29 23:26:28 +02:00
return 0 ;
2010-09-26 20:48:25 +02:00
}
CreatureTextGroup TextGroup = ( * itr ). second ; //has all texts in the group
CreatureTextRepeatIds repeatGroup = GetRepeatGroup ( source , textGroup ); //has all textIDs from the group that were already said
CreatureTextGroup tempGroup ; //will use this to talk after sorting repeatGroup
for ( CreatureTextGroup :: const_iterator giter = TextGroup . begin (); giter != TextGroup . end (); ++ giter )
{
if ( std :: find ( repeatGroup . begin (), repeatGroup . end (), ( * giter ). id ) == repeatGroup . end ())
tempGroup . push_back (( * giter ));
}
if ( tempGroup . empty ())
{
CreatureTextRepeatMap :: iterator mapItr = mTextRepeatMap . find ( source -> GetGUID ());
if ( mapItr != mTextRepeatMap . end ())
{
CreatureTextRepeatGroup :: iterator groupItr = ( * mapItr ). second . find ( textGroup );
( * groupItr ). second . clear ();
}
tempGroup = TextGroup ;
}
uint8 count = 0 ;
float lastChance = - 1 ;
bool isEqualChanced = true ;
float totalChance = 0 ;
for ( CreatureTextGroup :: const_iterator iter = tempGroup . begin (); iter != tempGroup . end (); ++ iter )
{
if ( lastChance >= 0 && lastChance != ( * iter ). probability )
isEqualChanced = false ;
lastChance = ( * iter ). probability ;
totalChance += ( * iter ). probability ;
count ++ ;
}
2010-09-29 23:26:28 +02:00
int32 offset = - 1 ;
if ( ! isEqualChanced )
2010-09-26 20:48:25 +02:00
{
for ( CreatureTextGroup :: const_iterator iter = tempGroup . begin (); iter != tempGroup . end (); ++ iter )
{
uint32 chance = uint32 (( * iter ). probability );
uint32 r = urand ( 0 , 100 );
2010-09-29 23:26:28 +02:00
offset ++ ;
2010-09-26 20:48:25 +02:00
if ( r <= chance )
2010-09-29 23:26:28 +02:00
break ;
2010-09-26 20:48:25 +02:00
}
}
2010-09-29 23:26:28 +02:00
uint32 pos = 0 ;
if ( isEqualChanced || offset < 0 )
pos = urand ( 0 , count - 1 );
else if ( offset >= 0 )
pos = offset ;
CreatureTextGroup :: const_iterator iter = tempGroup . begin () + pos ;
ChatType finalType = ( msgtype == CHAT_TYPE_END ) ? ( * iter ). type : msgtype ;
Language finalLang = ( language == LANG_ADDON ) ? ( * iter ). lang : language ;
uint32 finalSound = sound ? sound : ( * iter ). sound ;
if ( finalSound )
SendSound ( source , finalSound , finalType , whisperGuid , range );
SendChatString ( source , ( * iter ). text . c_str (), finalType , finalLang , whisperGuid , range );
if ( isEqualChanced || ( ! isEqualChanced && totalChance == 100.0f ))
SetRepeatId ( source , textGroup , ( * iter ). id );
if (( * iter ). emote )
SendEmote ( source , ( * iter ). emote );
return ( * iter ). duration ;
}
void CreatureTextMgr :: SendSound ( Creature * source , uint32 sound , ChatType msgtype , uint64 whisperGuid , TextRange range )
{
if ( ! sound || ! source )
return ;
WorldPacket data ( SMSG_PLAY_SOUND , 4 );
data << uint32 ( sound );
SendChatPacket ( & data , source , msgtype , whisperGuid , range );
}
void CreatureTextMgr :: SendEmote ( Creature * source , uint32 emote )
{
source -> HandleEmoteCommand ( emote );
2010-09-26 20:48:25 +02:00
}
void CreatureTextMgr :: SetRepeatId ( Creature * source , uint8 textGroup , uint8 id )
{
if ( ! source )
return ;
if ( mTextRepeatMap . find ( source -> GetGUID ()) == mTextRepeatMap . end ())
{
CreatureTextRepeatGroup TextGroup ;
mTextRepeatMap [ source -> GetGUID ()] = TextGroup ;
}
if ( mTextRepeatMap [ source -> GetGUID ()]. find ( textGroup ) == mTextRepeatMap [ source -> GetGUID ()]. end ())
{
CreatureTextRepeatIds ids ;
mTextRepeatMap [ source -> GetGUID ()][ textGroup ] = ids ;
}
if ( std :: find ( mTextRepeatMap [ source -> GetGUID ()][ textGroup ]. begin (), mTextRepeatMap [ source -> GetGUID ()][ textGroup ]. end (), id ) == mTextRepeatMap [ source -> GetGUID ()][ textGroup ]. end ())
{
mTextRepeatMap [ source -> GetGUID ()][ textGroup ]. push_back ( id );
}
else
sLog . outErrorDb ( "CreatureTextMgr: TextGroup %u for Creature(%s) GuidLow %u Entry %u, id %u already added" , uint32 ( textGroup ), source -> GetName (), source -> GetGUIDLow (), source -> GetEntry (), uint32 ( id ));
}
CreatureTextRepeatIds CreatureTextMgr :: GetRepeatGroup ( Creature * source , uint8 textGroup )
{
ASSERT ( source ); //should never happen
CreatureTextRepeatIds ids ;
CreatureTextRepeatMap :: const_iterator mapItr = mTextRepeatMap . find ( source -> GetGUID ());
if ( mapItr != mTextRepeatMap . end ())
{
CreatureTextRepeatGroup :: const_iterator groupItr = ( * mapItr ). second . find ( textGroup );
if ( groupItr != ( * mapItr ). second . end ())
{
ids = ( * groupItr ). second ;
}
}
return ids ;
}
void CreatureTextMgr :: SendChatString ( WorldObject * source , char const * text , ChatType msgtype , Language language , uint64 whisperGuid , TextRange range ) const
{
if ( ! source )
return ;
2010-09-25 23:52:17 +02:00
WorldPacket data ( SMSG_MESSAGECHAT , 200 );
BuildMonsterChat ( & data , source , msgtype , text , language , whisperGuid ); //build our packet
SendChatPacket ( & data , source , msgtype , whisperGuid , range ); //send our packet
}
void CreatureTextMgr :: BuildMonsterChat ( WorldPacket * data , WorldObject * source , ChatType msgtype , char const * text , Language language , uint64 whisperGuid ) const
{
2010-09-26 20:48:25 +02:00
if ( ! source )
return ;
2010-09-25 23:52:17 +02:00
ChatMsg sendType = CHAT_MSG_MONSTER_SAY ;
switch ( msgtype )
{
case CHAT_TYPE_YELL :
sendType = CHAT_MSG_MONSTER_YELL ;
break ;
case CHAT_TYPE_TEXT_EMOTE :
sendType = CHAT_MSG_MONSTER_EMOTE ;
break ;
case CHAT_TYPE_BOSS_EMOTE :
sendType = CHAT_MSG_RAID_BOSS_EMOTE ;
break ;
case CHAT_TYPE_WHISPER :
if ( whisperGuid )
sendType = CHAT_MSG_MONSTER_WHISPER ;
else
{
sLog . outError ( "CreatureTextMgr: WorldObject(%s) TypeId %u GuidLow %u sent CHAT_TYPE_WHISPER with targetGuid 0. Ignoring." , source -> GetName (), uint32 ( source -> GetTypeId ()), source -> GetGUIDLow ());
return ;
}
break ;
case CHAT_TYPE_BOSS_WHISPER :
if ( whisperGuid )
sendType = CHAT_MSG_RAID_BOSS_WHISPER ;
else
{
sLog . outError ( "CreatureTextMgr: WorldObject(%s) TypeId %u GuidLow %u sent CHAT_TYPE_BOSS_WHISPER with targetGuid 0. Ignoring." , source -> GetName (), uint32 ( source -> GetTypeId ()), source -> GetGUIDLow ());
return ;
}
break ;
case CHAT_TYPE_SAY : //default type
default :
break ;
}
* data << ( uint8 ) sendType ;
* data << ( uint32 ) language ;
* data << ( uint64 ) source -> GetGUID ();
* data << ( uint32 ) 0 ; // 2.1.0
* data << ( uint32 )( strlen ( source -> GetName ()) + 1 );
* data << source -> GetName ();
* data << ( uint64 ) whisperGuid ; // Unit Target
if ( whisperGuid && ! IS_PLAYER_GUID ( whisperGuid )) //can only whisper players
{
sLog . outError ( "CreatureTextMgr: WorldObject(%s) TypeId %u GuidLow %u sent WHISPER msg to Non-Player target. Ignoring." , source -> GetName (), uint32 ( source -> GetTypeId ()), source -> GetGUIDLow ());
return ;
//*data << (uint32)1; // target name length
//*data << (uint8)0; // target name
}
* data << ( uint32 )( strlen ( text ) + 1 );
* data << text ;
* data << ( uint8 ) 0 ; // ChatTag
}
void CreatureTextMgr :: SendChatPacket ( WorldPacket * data , WorldObject * source , ChatType msgtype , uint64 whisperGuid , TextRange range ) const
{
2010-09-26 20:48:25 +02:00
if ( ! source )
return ;
2010-09-25 23:52:17 +02:00
float dist = sWorld . getFloatConfig ( CONFIG_LISTEN_RANGE_SAY );
switch ( msgtype )
{
case CHAT_TYPE_YELL :
dist = sWorld . getFloatConfig ( CONFIG_LISTEN_RANGE_YELL );
break ;
case CHAT_TYPE_BOSS_EMOTE :
case CHAT_TYPE_TEXT_EMOTE :
dist = sWorld . getFloatConfig ( CONFIG_LISTEN_RANGE_TEXTEMOTE );
break ;
case CHAT_TYPE_WHISPER :
case CHAT_TYPE_BOSS_WHISPER :
{
2010-09-29 23:26:28 +02:00
if ( range == TEXT_RANGE_NORMAL )
2010-09-25 23:52:17 +02:00
{
2010-09-29 23:26:28 +02:00
Player * player = sObjectMgr . GetPlayer ( whisperGuid );
if ( ! player || ! player -> GetSession ())
return ;
player -> GetSession () -> SendPacket ( data );
2010-09-25 23:52:17 +02:00
return ;
}
}
2010-09-29 23:26:28 +02:00
break ;
2010-09-25 23:52:17 +02:00
case CHAT_TYPE_SAY : //default dist
default :
break ;
}
switch ( range )
{
case TEXT_RANGE_AREA :
{
uint32 areaId = source -> GetAreaId ();
Map :: PlayerList const & pList = source -> GetMap () -> GetPlayers ();
for ( Map :: PlayerList :: const_iterator itr = pList . begin (); itr != pList . end (); ++ itr )
if ( itr -> getSource () -> GetAreaId () == areaId )
( itr -> getSource ()) -> GetSession () -> SendPacket ( data );
}
return ;
case TEXT_RANGE_ZONE :
{
uint32 zoneId = source -> GetZoneId ();
Map :: PlayerList const & pList = source -> GetMap () -> GetPlayers ();
for ( Map :: PlayerList :: const_iterator itr = pList . begin (); itr != pList . end (); ++ itr )
if ( itr -> getSource () -> GetZoneId () == zoneId )
( itr -> getSource ()) -> GetSession () -> SendPacket ( data );
}
return ;
case TEXT_RANGE_MAP :
{
Map :: PlayerList const & pList = source -> GetMap () -> GetPlayers ();
for ( Map :: PlayerList :: const_iterator itr = pList . begin (); itr != pList . end (); ++ itr )
( itr -> getSource ()) -> GetSession () -> SendPacket ( data );
}
return ;
case TEXT_RANGE_WORLD :
{
sWorld . SendGlobalMessage ( data );
}
return ;
case TEXT_RANGE_NORMAL :
default :
break ;
}
source -> SendMessageToSetInRange ( data , dist , true );
}