1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2017 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
9 // This program is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU Affero General Public License as
11 // published by the Free Software Foundation, either version 3 of the
12 // License, or (at your option) any later version.
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU Affero General Public License for more details.
19 // You should have received a copy of the GNU Affero General Public License
20 // along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "game_share/generic_xml_msg_mngr.h"
27 #include "game_share/msg_client_server.h"
29 #include "client_chat_manager.h"
30 #include "net_manager.h"
31 #include "nel/gui/group_list.h"
32 #include "interface_v3/interface_manager.h"
33 #include "interface_v3/people_interraction.h"
34 #include "string_manager_client.h"
35 #include "entity_cl.h"
36 #include "nel/gui/action_handler.h"
38 #include "nel/gui/group_editbox.h"
39 #include "permanent_ban.h"
41 #include "nel/gui/ctrl_text_button.h"
42 #include "nel/gui/group_tab.h"
43 #include "string_manager_client.h"
45 #include "game_share/generic_xml_msg_mngr.h"
46 #include "game_share/msg_client_server.h"
47 #include "game_share/chat_group.h"
48 #include "interface_v3/skill_manager.h"
53 using namespace NLMISC
;
60 extern CGenericXmlMsgHeaderManager GenericMsgHeaderMngr
;
61 extern CClientChatManager ChatMngr
;
63 extern CEntityManager EntitiesMngr
;
66 //#ifdef OLD_STRING_SYSTEM
68 //bool CNetworkString::getString (ucstring &result, CClientChatManager *mng) // OLD
70 // result = StaticString + " / ";
71 // for (uint i = 0; i < Args.size(); i++)
74 // result += toString (Args[i]);
76 // CDynamicStringInfos *res = mng->getDynamicDB().getDynamicStringInfos ((uint32)Args[i]);
80 // result += res->Str;
84 // //nlinfo ("%s", result.c_str());
86 // return mng->getString (result, Args, StaticString);
89 //void CNetworkString::setString (const ucstring &staticStringId, CClientChatManager *mng) // OLD
92 // mng->getStaticDB().getInfos(staticStringId, StaticString, bms);
95 ////-----------------------------------------------
98 ////-----------------------------------------------
99 //uint32 CChatDynamicDatabase::add( uint32 index, ucstring& ucstr, vector<bool>& code ) // OLD
101 // nlinfo ("receive dynamic string association '%d' '%s'", index, ucstr.toString().c_str());
103 // map<ucstring, uint32>::iterator itIdx = _StringToIndex.find( ucstr ); // OLD
104 // if( itIdx == _StringToIndex.end() )
106 // map<uint32,CDynamicStringInfos *>::iterator itStr = _Data.find( index );
107 // if( itStr == _Data.end() )
109 // CDynamicStringInfos * dynInfosTmp = new CDynamicStringInfos();
110 // dynInfosTmp->Index = index;
111 // dynInfosTmp->Associated = true;
113 // // display the index number
114 // //dynInfosTmp->Str = toString((uint32)index);
115 // //dynInfosTmp->Str += " ";
116 // dynInfosTmp->Str = ucstr;
118 // if( !code.empty() )
120 // _Huffman.add( ucstr, code );
121 // dynInfosTmp->IsHuffman = true;
125 // dynInfosTmp->IsHuffman = false;
128 // _Data.insert( make_pair(index,dynInfosTmp) );
129 // _StringToIndex.insert( make_pair(ucstr,index) );
135 // // we already insert a fake entry usign getInfos(), now we set the good value
137 // // display the index number
138 // //(*itStr).second->Str = toString((uint32)index);
139 // //(*itStr).second->Str += " ";
140 // (*itStr).second->Str = ucstr;
141 // (*itStr).second->Associated = true;
143 // if( !code.empty() )
145 // _Huffman.add( ucstr, code );
152 // nlwarning("<CChatDynamicDatabase::add> the entry %s already exists",ucstr.toString().c_str());
161 ////-----------------------------------------------
164 ////-----------------------------------------------
165 //void CChatDynamicDatabase::decodeString( ucstring& str, CBitMemStream& bms ) // OLD
167 // _Huffman.getId( str, bms );
169 //} // decodeString //
171 ////-----------------------------------------------
172 //// getDynamicStringInfos
174 ////-----------------------------------------------
175 //CDynamicStringInfos * CChatDynamicDatabase::getDynamicStringInfos( uint32 index )
179 // nldebug("<CChatDynamicDatabase::getInfos> The index 0 is not a valid index");
182 // map< uint32, CDynamicStringInfos *>::iterator itData = _Data.find( index );
183 // if( itData != _Data.end() )
185 // return (*itData).second;
189 // CDynamicStringInfos * infos = new CDynamicStringInfos();
192 // _Data.insert( make_pair(index,infos) );
197 // nlwarning("<CChatDynamicDatabase::getInfos> new infos allocation failed for string %d",index);
202 //} // getDynamicStringInfos //
207 ////-----------------------------------------------
208 //// ~CChatDynamicDatabase
210 ////-----------------------------------------------
211 //CChatDynamicDatabase::~CChatDynamicDatabase()
213 // map< uint32, CDynamicStringInfos *>::iterator itData;
214 // for( itData = _Data.begin(); itData != _Data.end(); ++itData )
216 // delete (*itData).second;
219 //} // ~CChatDynamicDatabase //
223 //-----------------------------------------------
226 //-----------------------------------------------
227 CClientChatManager::CClientChatManager()
229 _ChatMode
= (uint8
) CChatGroup::nbChatMode
;
230 _ChatDynamicChannelId
= 0;
232 _MaxNumTellPeople
= 5;
235 for(uint i
=0;i
<CChatGroup::MaxDynChanPerPlayer
;i
++)
237 _DynamicChannelNameLeaf
[i
]= NULL
;
238 _DynamicChannelIdLeaf
[i
]= NULL
;
239 _DynamicChannelIdCache
[i
]= DynamicChannelEmptyId
;
243 //-------------------------------------------------------
244 const string
*CClientChatManager::cycleLastTell()
246 if (_TellPeople
.empty()) return NULL
;
247 _TellPeople
.push_front(_TellPeople
.back());
248 _TellPeople
.pop_back();
249 return &(_TellPeople
.front());
252 //-------------------------------------------------------
253 void CClientChatManager::setTellListSize(uint numPeople
)
255 if (numPeople
<= _NumTellPeople
)
257 for(uint k
= 0; k
< (_NumTellPeople
- numPeople
); ++k
)
259 _TellPeople
.pop_front();
261 _NumTellPeople
= numPeople
;
263 _MaxNumTellPeople
= numPeople
;
266 //-----------------------------------------------
269 //-----------------------------------------------
270 void CClientChatManager::init( const string
& /* staticDBFileName */ )
272 //#ifdef OLD_STRING_SYSTEM //
274 // _StaticDB.load( staticDBFileName );
281 //-----------------------------------------------
284 //-----------------------------------------------
285 void CClientChatManager::chat( const string
& strIn
, bool isChatTeam
)
287 // Truncate to 255 chars max (because of server restriction)
288 ucstring str
= ucstring(strIn
).substr(0,255); // FIXME: UTF-8 (serial)
296 if (NLGUI::CDBManager::getInstance()->getDbProp("SERVER:GROUP:0:PRESENT")->getValueBool())
297 msgType
= "STRING:CHAT_TEAM";
299 return; // don't display team chat message if there is no team chat
302 msgType
= "STRING:CHAT";
304 if( GenericMsgHeaderMngr
.pushNameToStream(msgType
,bms
) )
306 bms
.serial( str
); // FIXME: UTF-8 (serial)
308 //nlinfo("impulseCallBack : %s %s sent", msgType.c_str(), str.toString().c_str());
312 nlwarning("<CClientChatManager::chat> unknown message name : %s", msgType
);
315 if (UserEntity
!= NULL
) UserEntity
->setAFK(false);
320 //-----------------------------------------------
323 //-----------------------------------------------
324 void CClientChatManager::tell( const string
& receiverIn
, const string
& strIn
)
326 // Truncate to 255 chars max (because of server restriction)
327 string receiver
= receiverIn
.substr(0,255); // FIXME: UTF-8 (serial)
328 ucstring str
= ucstring(strIn
).substr(0,255); // FIXME: UTF-8 (serial)
332 const char *msgType
= "STRING:TELL";
333 if( GenericMsgHeaderMngr
.pushNameToStream(msgType
,bms
) )
335 bms
.serial( receiver
);
336 bms
.serial( str
); // FIXME: UTF-8
338 //nlinfo("impulseCallBack : %s %s %s sent", msgType.c_str(), receiver.c_str(), str.toString().c_str());
342 nlwarning("<CClientChatManager::tell> unknown message name : STRING:TELL");
346 // *** manage list of last telled people
347 // remove the telled people from list (if present)
348 std::list
<string
>::iterator it
= _TellPeople
.begin();
349 while(it
!= _TellPeople
.end())
353 it
= _TellPeople
.erase(it
);
354 nlassert(_NumTellPeople
!= 0);
363 // readd to back of the list (most recent telled people)
364 _TellPeople
.push_back(receiver
);
367 // if too much people, remove the older one
368 if (_NumTellPeople
> _MaxNumTellPeople
)
371 _TellPeople
.pop_front();
374 // tell => the user is no more AFK.
375 if (UserEntity
!= NULL
) UserEntity
->setAFK(false);
381 //-----------------------------------------------
384 //-----------------------------------------------
385 void CClientChatManager::filter( uint8 filter
)
388 const char *msgType
= "STRING:FILTER";
389 if( GenericMsgHeaderMngr
.pushNameToStream(msgType
,bms
) )
391 bms
.serial( filter
);
393 //nlinfo("impulseCallBack : %s %d sent", msgType.c_str(), filter);
397 nlwarning("<CClientChatManager::filter> unknown message name : STRING:FILTER");
400 if (UserEntity
!= NULL
) UserEntity
->setAFK(false);
406 //-----------------------------------------------
409 //-----------------------------------------------
410 void CClientChatManager::setChatMode(CChatGroup::TGroupType group
, TChanID dynamicChannelId
)
413 // mode really changed ?
414 if (mode
== _ChatMode
&& dynamicChannelId
==_ChatDynamicChannelId
) return;
416 // Chat team don't need swap mode
417 if (group
!= CChatGroup::team
)
420 const char *msgType
= "STRING:CHAT_MODE";
421 if( GenericMsgHeaderMngr
.pushNameToStream(msgType
,bms
) )
424 bms
.serial( dynamicChannelId
);
426 //nlinfo("impulseCallBack : %s %d sent", msgType.c_str(), mode);
430 nlwarning("<CClientChatManager::setChatMode> unknown message name : STRING:CHAT_MODE");
436 _ChatDynamicChannelId
= dynamicChannelId
;
438 if (UserEntity
!= NULL
) UserEntity
->setAFK(false);
442 /// Reset the chat mode to force the client to resend it. Used during far TP.
443 void CClientChatManager::resetChatMode()
445 _ChatMode
= (uint8
)CChatGroup::nbChatMode
;
449 // ***************************************************************************
450 void CClientChatManager::processTellString(NLMISC::CBitMemStream
& bms
, IChatDisplayer
&chatDisplayer
)
454 // Serial. For tell message, there is no chat mode, coz we know we are in tell mode !
455 bms
.serial (chatMsg
.CompressedIndex
);
456 bms
.serial (chatMsg
.SenderNameId
);
457 bms
.serial (chatMsg
.Content
); // FIXME: UTF-8 (serial)
459 if (PermanentlyBanned
) return;
461 chatMsg
.ChatMode
= (uint8
) CChatGroup::tell
;
463 // If !complete, wait
465 bool complete
= true;
466 complete
&= STRING_MANAGER::CStringManagerClient::instance()->getString(chatMsg
.SenderNameId
, senderStr
);
469 _ChatBuffer
.push_back(CChatMsgNode(chatMsg
, true));
470 nldebug("<impulseTell> Received TELL, put in buffer : waiting association");
476 buildTellSentence(senderStr
, chatMsg
.Content
.toUtf8(), ucstr
);
477 chatDisplayer
.displayTell(/*chatMsg.CompressedIndex, */ucstr
, senderStr
);
480 // ***************************************************************************
481 void CClientChatManager::processFarTellString(NLMISC::CBitMemStream
& bms
, IChatDisplayer
&chatDisplayer
)
483 CFarTellMsg farTellMsg
;
485 // Serial. For far tell message, there is no chat mode nor sender index, and the sender is a string literal!
486 farTellMsg
.serial(bms
);
488 if (PermanentlyBanned
) return;
492 buildTellSentence(farTellMsg
.SenderName
.toUtf8(), farTellMsg
.Text
.toUtf8(), ucstr
);
493 chatDisplayer
.displayTell(/*chatMsg.CompressedIndex, */ucstr
, farTellMsg
.SenderName
.toUtf8());
497 // ***************************************************************************
498 void CClientChatManager::processChatString( NLMISC::CBitMemStream
& bms
, IChatDisplayer
&chatDisplayer
)
500 // before displaying anything, must ensure dynamic channels are up to date
501 // NB: only processChatString() have to do this. Other methods cannot be in dyn_chat mode
502 updateDynamicChatChannels(chatDisplayer
);
506 bms
.serial( chatMsg
);
507 CChatGroup::TGroupType type
= static_cast<CChatGroup::TGroupType
>(chatMsg
.ChatMode
);
510 bool complete
= true;
511 complete
&= STRING_MANAGER::CStringManagerClient::instance()->getString(chatMsg
.SenderNameId
, senderStr
);
513 if (type
== CChatGroup::dyn_chat
)
515 // retrieve the DBIndex from the dynamic chat id
516 sint32 dbIndex
= ChatMngr
.getDynamicChannelDbIndexFromId(chatMsg
.DynChatChanID
);
517 // if the client database is not yet up to date, put the chat message in buffer
522 // if !complete, wait
525 _ChatBuffer
.push_back(CChatMsgNode(chatMsg
, false));
526 //nldebug("<impulseChat> Received CHAT, put in buffer : waiting association");
532 buildChatSentence(chatMsg
.CompressedIndex
, senderStr
, chatMsg
.Content
.toUtf8(), type
, ucstr
);
533 chatDisplayer
.displayChat(chatMsg
.CompressedIndex
, ucstr
, chatMsg
.Content
.toUtf8(), type
, chatMsg
.DynChatChanID
, senderStr
);
537 // ***************************************************************************
538 void CClientChatManager::processTellString2(NLMISC::CBitMemStream
& bms
, IChatDisplayer
&chatDisplayer
)
542 chatMsg
.ChatMode
= CChatGroup::tell
;
543 bms
.serial(chatMsg
.CompressedIndex
);
544 bms
.serial(chatMsg
.SenderNameId
);
545 bms
.serial(chatMsg
.PhraseId
);
547 // if !complete, wait
550 bool complete
= true;
551 complete
&= STRING_MANAGER::CStringManagerClient::instance()->getString(chatMsg
.SenderNameId
, senderStr
);
552 complete
&= STRING_MANAGER::CStringManagerClient::instance()->getDynString(chatMsg
.PhraseId
, rawMessage
);
555 _ChatBuffer
.push_back(CChatMsgNode(chatMsg
, true));
556 nldebug("<impulseTell> Received TELL, put in buffer : waiting association");
562 buildTellSentence(senderStr
, rawMessage
, ucstr
);
563 chatDisplayer
.displayTell(/*chatMsg.CompressedIndex, */ucstr
, senderStr
);
567 // ***************************************************************************
568 void CClientChatManager::processChatString2(NLMISC::CBitMemStream
& bms
, IChatDisplayer
&chatDisplayer
)
571 bms
.serial( chatMsg
);
572 if (PermanentlyBanned
) return;
573 CChatGroup::TGroupType type
= static_cast<CChatGroup::TGroupType
>(chatMsg
.ChatMode
);
577 // here, the type cannot be dyn_chat (no DynChatId in the message) => discard
578 if(type
==CChatGroup::dyn_chat
)
580 nlwarning("Client don't support dyn_chat with CChatMsg2 messages => '%x' aborted", chatMsg
.PhraseId
);
584 // if !complete, wait
585 bool complete
= true;
586 complete
&= STRING_MANAGER::CStringManagerClient::instance()->getString(chatMsg
.SenderNameId
, senderStr
);
587 complete
&= STRING_MANAGER::CStringManagerClient::instance()->getDynString(chatMsg
.PhraseId
, rawMessage
);
590 _ChatBuffer
.push_back(CChatMsgNode(chatMsg
, false));
591 //nldebug("<impulseChat> Received CHAT, put in buffer : waiting association");
596 rawMessage
+= chatMsg
.CustomTxt
.toUtf8();
600 buildChatSentence(chatMsg
.CompressedIndex
, senderStr
, rawMessage
, type
, ucstr
);
601 chatDisplayer
.displayChat(chatMsg
.CompressedIndex
, ucstr
, rawMessage
, type
, CEntityId::Unknown
, senderStr
);
604 // ***************************************************************************
605 void CClientChatManager::processChatStringWithNoSender( NLMISC::CBitMemStream
& bms
, CChatGroup::TGroupType type
, IChatDisplayer
&chatDisplayer
)
607 nlassert(type
!=CChatGroup::dyn_chat
);
612 bms
.serial(phraseID
);
613 if (PermanentlyBanned
) return;
614 chatMsg
.CompressedIndex
= INVALID_DATASET_INDEX
;
615 chatMsg
.SenderNameId
= 0;
616 chatMsg
.ChatMode
= type
;
617 chatMsg
.PhraseId
= phraseID
;
620 // if !complete, wait
621 bool complete
= STRING_MANAGER::CStringManagerClient::instance()->getDynString(chatMsg
.PhraseId
, ucstr
);
624 _ChatBuffer
.push_back(CChatMsgNode(chatMsg
, false));
625 //nldebug("<impulseDynString> Received CHAT, put in buffer : waiting association");
631 chatDisplayer
.displayChat(INVALID_DATASET_INDEX
, ucstr
, ucstr
, type
, CEntityId::Unknown
, senderName
);
634 // ***************************************************************************
635 void CClientChatManager::flushBuffer(IChatDisplayer
&chatDisplayer
)
637 // before displaying anything, must ensure dynamic channels are up to date
638 updateDynamicChatChannels(chatDisplayer
);
640 // **** Process waiting messages
642 list
<CChatMsgNode
>::iterator itMsg
;
643 for( itMsg
= _ChatBuffer
.begin(); itMsg
!= _ChatBuffer
.end(); )
645 CChatGroup::TGroupType type
= static_cast<CChatGroup::TGroupType
>(itMsg
->ChatMode
);
646 string sender
, content
;
648 // all strings received?
649 bool complete
= true;
650 if (itMsg
->SenderNameId
!= 0)
651 complete
&= STRING_MANAGER::CStringManagerClient::instance()->getString(itMsg
->SenderNameId
, sender
);
652 if(itMsg
->UsePhraseId
)
653 complete
&= STRING_MANAGER::CStringManagerClient::instance()->getDynString(itMsg
->PhraseId
, content
);
655 content
= itMsg
->Content
.toUtf8();
657 if (type
== CChatGroup::dyn_chat
)
659 // retrieve the DBIndex from the dynamic chat id
660 sint32 dbIndex
= ChatMngr
.getDynamicChannelDbIndexFromId(itMsg
->DynChatChanID
);
661 // if the client database is not yet up to date, leave the chat message in buffer
666 // if complete, process
670 if (itMsg
->SenderNameId
== 0)
676 if(itMsg
->DisplayAsTell
)
677 buildTellSentence(sender
, content
, ucstr
);
679 buildChatSentence(itMsg
->CompressedIndex
, sender
, content
, type
, ucstr
);
683 if(itMsg
->DisplayAsTell
)
684 chatDisplayer
.displayTell(/*itMsg->CompressedIndex, */ucstr
, sender
);
686 chatDisplayer
.displayChat(itMsg
->CompressedIndex
, ucstr
, content
, type
, itMsg
->DynChatChanID
, sender
);
688 list
<CChatMsgNode
>::iterator itTmp
= itMsg
++;
689 _ChatBuffer
.erase( itTmp
);
702 //-----------------------------------------------
705 //-----------------------------------------------
706 string
CClientChatManager::getString( CBitMemStream
& bms
, string
& ucstr
)
709 // deal with parameters
710 uint32 dynParamIdx
= 0;
711 bool dynParamSearch
= true;
713 while( dynParamSearch
)
715 // search if a parameter exists in the string
716 sprintf(chTmp
,"$%d",dynParamIdx
);
717 string
ucstrTmp( chTmp
);
718 string::size_type idx
= ucstr
.find(ucstrTmp
);
720 // if there's a parameter in the string
721 if( idx
!= string::npos
)
723 char c
= (char)ucstr
[idx
+ucstrTmp
.size()];
726 // parameter is an entry in the dynamic database
731 const string
dynStr("???");
734 nldebug("<CClientChatManager::getString> receiving huffman dynamic parameter in static string");
735 // #ifdef OLD_STRING_SYSTEM
736 // _DynamicDB.decodeString( dynStr, bms );
741 //if( (sint32)bms.length()*8 - bms.getPosInBit() >= 32 )
744 bms
.serial(nameIndex
);
745 // #ifdef OLD_STRING_SYSTEM
746 // dynStr = _DynamicDB.getDynamicStringInfos(nameIndex)->Str;
750 ucstr
.replace( idx
, ucstrTmp
.size()+1, dynStr
);
754 // parameter is a string
758 bms
.serial( dynStr
);
759 ucstr
.replace( idx
, ucstrTmp
.size()+1, dynStr
);
763 // parameter is an unsigned integer
768 ucstr
.replace( idx
, ucstrTmp
.size()+1, toString(nb
) );
774 uint i = idx + strTmp.size() + 1;
776 while( isdigit(str[i]) )
778 bitCountStr += str[i];
781 nlassert( !bitCountStr.empty() );
783 fromString(bitCountStr, bitCount);
784 nlassert( bitCount <= 64 );
786 bms.serial( nb, bitCount );
787 str.replace( idx, strTmp.size() + 1 + bitCountStr.size(), toString(nb) );
791 // parameter is a signed integer
796 ucstr
.replace( idx
, ucstrTmp
.size()+1, toString(nb
) );
802 uint i = idx + strTmp.size() + 1;
804 while( isdigit(str[i]) )
806 bitCountStr += str[i];
809 nlassert( !bitCountStr.empty() );
811 fromString(bitCountStr, bitCount);
812 nlassert( bitCount <= 64 );
814 bms.serial( nb, bitCount );
815 str.replace( idx, strTmp.size() + 1 + bitCountStr.size(), toString(nb) );
820 // parameter is a float
825 ucstr
.replace( idx
, ucstrTmp
.size()+1, toString(nb
) );
829 // parameter type is unknown
832 nlwarning("<CClientChatManager::getString> The dynamic type %c is unknown",c
);
839 dynParamSearch
= false;
847 //-----------------------------------------------
850 //-----------------------------------------------
851 bool CClientChatManager::getString( string
&result
, std::vector
<uint64
>& args
, const string
&ucstrbase
)
855 bool finalString
= true;
857 // deal with parameters
858 uint32 dynParamIdx
= 0;
859 bool dynParamSearch
= true;
861 while( dynParamSearch
)
863 // search if a parameter exists in the string
864 sprintf(chTmp
,"$%d",dynParamIdx
);
865 string
ucstrTmp( chTmp
);
866 string::size_type idx
= result
.find(ucstrTmp
);
868 // if there's a parameter in the string
869 if( idx
!= string::npos
)
873 if (dynParamIdx
>= args
.size())
875 nlwarning ("Missing args for string '%s', only %d args, need arg %d", ucstrbase
.c_str(), args
.size(), dynParamIdx
);
879 char c
= (char)result
[idx
+ucstrTmp
.size()];
882 // parameter is an entry in the dynamic database
885 // #ifdef OLD_STRING_SYSTEM
886 // CDynamicStringInfos *res = _DynamicDB.getDynamicStringInfos ((uint32)args[dynParamIdx]);
887 // if (!res->Associated)
890 // #ifdef OLD_STRING_SYSTEM
896 // parameter is a string
899 nlwarning ("string param not implemented in the vector<uint64> decoding");
903 // parameter is an unsigned integer
906 uint32 nb
= (uint32
) args
[dynParamIdx
];
911 // parameter is a signed integer
914 sint32 nb
= (sint32
) args
[dynParamIdx
];
919 // parameter is a float
922 float nb
= *(float *) &(args
[dynParamIdx
]);
927 // parameter type is unknown
930 nlwarning("<CClientChatManager::getString> The dynamic type %c is unknown",c
);
935 result
.replace( idx
, ucstrTmp
.size()+1, rep
);
941 dynParamSearch
= false;
949 // ***************************************************************************
950 void CClientChatManager::buildTellSentence(const string
&sender
, const string
&msg
, string
&result
)
952 // If no sender name was provided, show only the msg
953 if ( sender
.empty() )
957 string name
= CEntityCL::removeTitleAndShardFromName(sender
);
960 // special case where there is only a title, very rare case for some NPC
963 // we need the gender to display the correct title
964 CCharacterCL
*entity
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.getEntityByName(sender
, true, true));
965 bool bWoman
= entity
&& entity
->getGender() == GSGENDER::female
;
967 name
= STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(CEntityCL::getTitleFromName(sender
), bWoman
);
969 // Sometimes translation contains another title
970 string::size_type pos
= name
.find('$');
971 if (pos
!= string::npos
)
973 name
= STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(CEntityCL::getTitleFromName(name
), bWoman
);
979 // Does the char have a CSR title?
980 csr
= CHARACTER_TITLE::isCsrTitle(CEntityCL::getTitleFromName(sender
)) ? "(CSR) " : "";
983 result
= csr
+ name
+ " " + CI18N::get("tellsYou") + ": " + msg
;
988 // ***************************************************************************
989 void CClientChatManager::buildChatSentence(TDataSetIndex
/* compressedSenderIndex */, const string
&sender
, const string
&msg
, CChatGroup::TGroupType type
, string
&result
)
991 // if its a tell, then use buildTellSentence
992 if(type
==CChatGroup::tell
)
994 buildTellSentence(sender
, msg
, result
);
998 // If no sender name was provided, show only the msg
999 if ( sender
.empty() )
1005 // get the category if any. Note, in some case (chat from other player), there is not categories
1006 // and we do not want getStringCategory to return 'SYS' category.
1008 string catStr
= getStringCategory(msg
, finalMsg
, false);
1010 if (!catStr
.empty())
1011 cat
= "&" + catStr
+ "&";
1019 // Format the sentence with the provided sender name
1020 string senderName
= CEntityCL::removeTitleAndShardFromName(sender
);
1023 // Does the char have a CSR title?
1024 csr
= CHARACTER_TITLE::isCsrTitle(CEntityCL::getTitleFromName(sender
)) ? "(CSR) " : "";
1026 if (UserEntity
&& senderName
== UserEntity
->getDisplayName())
1031 case CChatGroup::shout
:
1032 result
= cat
+ csr
+ CI18N::get("youShout") + ": " + finalMsg
;
1035 result
= cat
+ csr
+ CI18N::get("youSay") + ": " + finalMsg
;
1041 // Special case where there is only a title, very rare case for some NPC
1042 if (senderName
.empty())
1044 CCharacterCL
*entity
= dynamic_cast<CCharacterCL
*>(EntitiesMngr
.getEntityByName(sender
, true, true));
1045 // We need the gender to display the correct title
1046 bool bWoman
= entity
&& entity
->getGender() == GSGENDER::female
;
1048 senderName
= STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(CEntityCL::getTitleFromName(sender
), bWoman
);
1050 // Sometimes translation contains another title
1051 string::size_type pos
= senderName
.find('$');
1052 if (pos
!= string::npos
)
1054 senderName
= STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(CEntityCL::getTitleFromName(senderName
), bWoman
);
1059 senderName
= STRING_MANAGER::CStringManagerClient::getLocalizedName(senderName
);
1062 case CChatGroup::shout
:
1063 result
= cat
+ csr
+ senderName
+ " " + CI18N::get("heShout") + ": " + finalMsg
;
1066 result
= cat
+ csr
+ senderName
+ " " + CI18N::get("heSays") + ": " + finalMsg
;
1072 // ***************************************************************************
1073 void CClientChatManager::initInGame()
1075 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
1077 for(uint i
=0;i
<CChatGroup::MaxDynChanPerPlayer
;i
++)
1080 _DynamicChannelNameLeaf
[i
]= NULL
;
1081 _DynamicChannelIdLeaf
[i
]= NULL
;
1082 _DynamicChannelIdCache
[i
]= DynamicChannelEmptyId
;
1084 CCDBNodeLeaf
*name
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:DYN_CHAT:CHANNEL%d:NAME", i
), false);
1085 CCDBNodeLeaf
*id
= NLGUI::CDBManager::getInstance()->getDbProp(toString("SERVER:DYN_CHAT:CHANNEL%d:ID", i
), false);
1088 _DynamicChannelNameLeaf
[i
]= name
;
1089 _DynamicChannelIdLeaf
[i
]= id
;
1094 // ***************************************************************************
1095 void CClientChatManager::releaseInGame()
1097 for(uint i
=0;i
<CChatGroup::MaxDynChanPerPlayer
;i
++)
1099 _DynamicChannelNameLeaf
[i
]= NULL
;
1100 _DynamicChannelIdLeaf
[i
]= NULL
;
1101 _DynamicChannelIdCache
[i
]= DynamicChannelEmptyId
;
1105 // ***************************************************************************
1106 TChanID
CClientChatManager::getDynamicChannelIdFromDbIndex(uint32 dbIndex
)
1108 if(dbIndex
>=CChatGroup::MaxDynChanPerPlayer
|| _DynamicChannelIdLeaf
[dbIndex
]==NULL
)
1109 return CEntityId::Unknown
;
1111 return TChanID(uint64(_DynamicChannelIdLeaf
[dbIndex
]->getValue64()));
1114 // ***************************************************************************
1115 sint32
CClientChatManager::getDynamicChannelDbIndexFromId(TChanID channelId
)
1117 for(uint i
=0;i
<CChatGroup::MaxDynChanPerPlayer
;i
++)
1119 if(_DynamicChannelIdLeaf
[i
]!= NULL
)
1121 if(uint64(_DynamicChannelIdLeaf
[i
]->getValue64()) == channelId
.getRawId())
1129 // ***************************************************************************
1130 bool CClientChatManager::isDynamicChannelExist(TChanID channelId
)
1132 sint32 dbid
= getDynamicChannelDbIndexFromId(channelId
);
1133 return dbid
>=0 && dbid
<CChatGroup::MaxDynChanPerPlayer
;
1136 // ***************************************************************************
1137 uint32
CClientChatManager::getDynamicChannelNameFromDbIndex(uint32 dbIndex
)
1139 if(dbIndex
>=CChatGroup::MaxDynChanPerPlayer
|| _DynamicChannelNameLeaf
[dbIndex
]==NULL
)
1142 return _DynamicChannelNameLeaf
[dbIndex
]->getValue32();
1146 // ***************************************************************************
1147 void CClientChatManager::updateDynamicChatChannels(IChatDisplayer
&chatDisplayer
)
1150 for(uint i
=0;i
<CChatGroup::MaxDynChanPerPlayer
;i
++)
1152 // if the NAME is 0, force also an "empty id". because server may not release Id to 0 while NAME is.
1153 sint32 curActualChannelId
= DynamicChannelEmptyId
;
1154 if(_DynamicChannelIdLeaf
[i
] && _DynamicChannelNameLeaf
[i
])
1156 if(_DynamicChannelNameLeaf
[i
]->getValue32()!=0)
1157 curActualChannelId
= _DynamicChannelIdLeaf
[i
]->getValue32();
1160 // if different from precend, clear the channel
1161 if(curActualChannelId
!=(sint32
)_DynamicChannelIdCache
[i
])
1163 _DynamicChannelIdCache
[i
]= curActualChannelId
;
1165 chatDisplayer
.clearChannel(CChatGroup::dyn_chat
, i
);
1172 // ***************************************************************************
1174 class CHandlerTell
: public IActionHandler
1176 void execute (CCtrlBase
*pCaller
, const string
&sParams
)
1178 string receiver
= getParam (sParams
, "player");
1180 message
= getParam (sParams
, "text");
1182 if (receiver
.empty() || message
.empty())
1185 // Get the chat window (if any)
1186 CChatWindow
*cw
= NULL
;
1187 CGroupEditBox
*eb
= pCaller
?dynamic_cast<CGroupEditBox
*>(pCaller
):NULL
;
1189 cw
= getChatWndMgr().getChatWindowFromCaller(eb
);
1191 // Send the message.
1192 ChatMngr
.tell(receiver
, message
);
1194 // display in the good window
1195 CInterfaceProperty prop
;
1196 prop
.readRGBA("UI:SAVE:CHAT:COLORS:SPEAKER"," ");
1198 CChatWindow::encodeColorTag(prop
.getRGBA(), finalMsg
, false);
1200 string
csr(CHARACTER_TITLE::isCsrTitle(UserEntity
->getTitleRaw()) ? "(CSR) " : "");
1201 finalMsg
+= csr
+ CI18N::get("youTell") + ": ";
1202 prop
.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," ");
1203 CChatWindow::encodeColorTag(prop
.getRGBA(), finalMsg
, true);
1204 finalMsg
+= message
;
1205 // display msg with good color
1206 // TDataSetIndex dsi; // not used ....
1207 PeopleInterraction
.ChatInput
.Tell
.displayTellMessage(/*dsi, */finalMsg
, receiver
, prop
.getRGBA());
1209 string s
= CI18N::get("youTellPlayer");
1210 strFindReplace(s
, "%name", receiver
);
1211 strFindReplace(finalMsg
, CI18N::get("youTell"), s
);
1212 CInterfaceManager::getInstance()->log(finalMsg
, CChatGroup::groupTypeToString(CChatGroup::tell
));
1215 REGISTER_ACTION_HANDLER( CHandlerTell
, "tell");
1217 // ***************************************************************************
1219 class CHandlerEnterTell
: public IActionHandler
1221 void execute (CCtrlBase
* /* pCaller */, const string
&sParams
)
1223 CInterfaceManager
*im
= CInterfaceManager::getInstance();
1224 string receiver
= getParam (sParams
, "player");
1225 if (receiver
.empty())
1228 CChatGroupWindow
*pCGW
= PeopleInterraction
.getChatGroupWindow();
1231 CGroupContainer
*pGC
= pCGW
->createFreeTeller(receiver
);
1234 pGC
->setActive(true);
1235 CGroupEditBox
*eb
= dynamic_cast<CGroupEditBox
*>(pGC
->getGroup("eb"));
1238 CWidgetManager::getInstance()->setCaptureKeyboard(eb
);
1244 REGISTER_ACTION_HANDLER( CHandlerEnterTell
, "enter_tell");
1247 //-----------------------------------------------
1248 // updateChatModeAndButton
1250 //-----------------------------------------------
1251 void CClientChatManager::updateChatModeAndButton(uint mode
, uint32 dynamicChannelDbIndex
)
1253 // Check if USER chat is active
1254 bool userActive
= false;
1255 CChatGroupWindow
*pCGW
= PeopleInterraction
.getChatGroupWindow();
1258 CInterfaceGroup
* pIG
= pCGW
->getContainer()->getGroup("content:cb:user");
1260 userActive
= pIG
->getActive();
1263 CChatGroup::TGroupType m
= (CChatGroup::TGroupType
)mode
;
1267 // Change the button of the user chat to the corresponding chat target
1270 CCtrlTextButton
*pUserBut
= dynamic_cast<CCtrlTextButton
*>(pCGW
->getContainer()->getCtrl("content:but_user"));
1271 CCtrlTextButton
*pEmoteBut
= dynamic_cast<CCtrlTextButton
*>(pCGW
->getContainer()->getCtrl("content:but_emote"));
1272 CInterfaceGroup
*pEditBox
= dynamic_cast<CInterfaceGroup
*>(pCGW
->getContainer()->getGroup("content:ebw"));
1274 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
1275 const bool teamActive
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:GROUP:0:PRESENT")->getValueBool();
1276 const bool guildActive
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:GUILD:NAME")->getValueBool();
1278 if (m
== CChatGroup::team
&& ! teamActive
)
1279 m
= PeopleInterraction
.TheUserChat
.Filter
.getTargetGroup();
1281 if (m
== CChatGroup::guild
&& ! guildActive
)
1282 m
= PeopleInterraction
.TheUserChat
.Filter
.getTargetGroup();
1289 case CChatGroup::arround
:
1290 case CChatGroup::say
: pUserBut
->setHardText("uiFilterAround"); break;
1291 case CChatGroup::region
: pUserBut
->setHardText("uiFilterRegion"); break;
1292 case CChatGroup::universe
: pUserBut
->setHardText("uiFilterUniverse"); break;
1293 case CChatGroup::team
: if (teamActive
) pUserBut
->setHardText("uiFilterTeam"); break;
1294 case CChatGroup::guild
: if (guildActive
) pUserBut
->setHardText("uiFilterGuild"); break;
1295 case CChatGroup::dyn_chat
:
1296 uint32 textId
= ChatMngr
.getDynamicChannelNameFromDbIndex(dynamicChannelDbIndex
);
1298 STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId
, title
);
1301 // Dyn channel does not exist, don't change
1302 m
= PeopleInterraction
.TheUserChat
.Filter
.getTargetGroup();
1303 dynamicChannelDbIndex
= PeopleInterraction
.TheUserChat
.Filter
.getTargetDynamicChannelDbIndex();
1307 pUserBut
->setHardText(title
);
1312 pUserBut
->setActive(true);
1313 pUserBut
->getParent()->updateCoords();
1314 pUserBut
->updateCoords();
1319 pEmoteBut
->setActive(true);
1320 pEmoteBut
->updateCoords();
1323 if (pEditBox
&& pUserBut
&& pEmoteBut
)
1325 pEditBox
->setW(-pUserBut
->getWReal()-pEmoteBut
->getWReal()-8);
1326 pEditBox
->setX(pUserBut
->getWReal()+4);
1329 PeopleInterraction
.TheUserChat
.Filter
.setTargetGroup(m
, dynamicChannelDbIndex
);
1330 PeopleInterraction
.ChatGroup
.Filter
.setTargetGroup(m
, dynamicChannelDbIndex
);
1335 // ***************************************************************************
1337 class CHandlerTalk
: public IActionHandler
1339 void execute (CCtrlBase
* /* pCaller */, const string
&sParams
)
1343 fromString(getParam (sParams
, "mode"), mode
);
1344 string text
= getParam (sParams
, "text");
1346 // Parse any tokens in the text
1347 if ( ! CInterfaceManager::parseTokens(text
))
1352 // Find the base group
1353 if ((mode
<CChatGroup::nbChatMode
) && !text
.empty())
1358 string cmdWithArgs
= str
.substr(1);
1360 // Get the command name from the string, can contain spaces
1361 string cmd
= cmdWithArgs
.substr(0, cmdWithArgs
.find(' '));
1362 if (cmdWithArgs
.find('"') == 0)
1364 string::size_type pos
= cmdWithArgs
.find('"', 1);
1365 if (string::npos
!= pos
)
1367 cmd
= cmdWithArgs
.substr(1, pos
- 1);
1371 if ( NLMISC::ICommand::exists( cmd
) )
1373 NLMISC::ICommand::execute( cmdWithArgs
, g_log
);
1377 CInterfaceManager
*im
= CInterfaceManager::getInstance();
1378 im
->displaySystemInfo (cmd
+ ": " + CI18N::get ("uiCommandNotExists"));
1383 if (mode
== CChatGroup::dyn_chat
)
1386 fromString(getParam (sParams
, "channel"), channel
);
1387 if (channel
< CChatGroup::MaxDynChanPerPlayer
)
1389 PeopleInterraction
.talkInDynamicChannel(channel
, text
);
1393 nlwarning("/ah talk: invalid dyn_chat channel %d\n", channel
);
1398 ChatMngr
.setChatMode((CChatGroup::TGroupType
)mode
);
1399 ChatMngr
.chat(text
, mode
== CChatGroup::team
);
1405 REGISTER_ACTION_HANDLER( CHandlerTalk
, "talk");
1407 // ***************************************************************************
1409 class CHandlerEnterTalk
: public IActionHandler
1411 void execute (CCtrlBase
* /* pCaller */, const string
&sParams
)
1417 fromString(getParam (sParams
, "mode"), mode
);
1419 if (mode
== CChatGroup::dyn_chat
)
1421 fromString(getParam(sParams
, "channel"), channel
);
1423 if (channel
>= CChatGroup::MaxDynChanPerPlayer
)
1429 ChatMngr
.updateChatModeAndButton(mode
, channel
);
1432 REGISTER_ACTION_HANDLER( CHandlerEnterTalk
, "enter_talk");
1434 // ***************************************************************************
1436 class CHandlerTalkMessage
: public IActionHandler
1438 void execute (CCtrlBase
* /* pCaller */, const string
&sParams
)
1441 string text
= CI18N::get ("uiTalkMemMsg"+sParams
);
1443 // Find the base group
1446 ChatMngr
.setChatMode (CChatGroup::say
);
1447 ChatMngr
.chat(text
);
1451 REGISTER_ACTION_HANDLER( CHandlerTalkMessage
, "talk_message");
1453 // ***************************************************************************
1455 class CHandlerSwapChatMode
: public IActionHandler
1457 void execute (CCtrlBase
* /* pCaller */, const string
&sParams
)
1459 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
1460 bool updateCapture
= getParam(sParams
, "update_capture")=="1";
1462 CCDBNodeLeaf
*node
= NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:CHAT:ENTER_DONT_QUIT_CB", false);
1465 // if "chatmode" is active
1466 if(node
->getValue32())
1468 // leave it (enter quit CB)
1469 node
->setValue32(0);
1470 // also leave Chat Focus (important if comes from command)
1472 CWidgetManager::getInstance()->setCaptureKeyboard(NULL
);
1476 // enter chat mode (enter dont quit CB)
1477 node
->setValue32(1);
1478 // enter Chat focus if '/c' entered
1479 if (updateCapture
&& !CWidgetManager::getInstance()->getCaptureKeyboard())
1481 // reset to the old captured keyboard (should be the one that have launched the command)
1482 if(CWidgetManager::getInstance()->getOldCaptureKeyboard())
1483 CWidgetManager::getInstance()->setCaptureKeyboard(CWidgetManager::getInstance()->getOldCaptureKeyboard());
1489 REGISTER_ACTION_HANDLER( CHandlerSwapChatMode
, "swap_chat_mode");