Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / bot_chat_interface.cpp
blob36dc9aa428d15b352cdbdf805d7e175b7919bc8d
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
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 Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "stdpch.h"
20 #if 0
21 #error "Deprecated"
22 //#include "bot_chat_interface.h"
23 #include "game_share/synchronised_message.h"
24 #include "game_share/bot_chat_types.h"
27 // Nel Misc
28 #include "nel/net/unified_network.h"
30 // Game share
31 #include "game_share/news_types.h"
32 #include "game_share/bot_chat_types.h"
34 // Local includes
35 #include "bot_chat_interface.h"
38 using namespace NLMISC;
39 using namespace NLNET;
40 using namespace std;
42 //////////////////////////////////////////////////////////////////////////////
43 //////////////////////////////////////////////////////////////////////////////
45 //////////////////////////////////////////////////////////////////////////////
46 // the parent class for bot chat page type classes
48 class CBotChatPageType
50 public:
51 // virtual interface ----------------------------------------------------
52 virtual bool open(const CEntityId &player, const CEntityId &bot)=0;
53 virtual bool close(const CEntityId &player, const CEntityId &bot)=0;
57 //////////////////////////////////////////////////////////////////////////////
58 // the structure for bot chat pages
60 struct SBotChatPage
62 // ctor -----------------------------------------------------------------
63 SBotChatPage(
64 BOTCHATTYPE::TBotChatInterfaceId clientInterfaceId,
65 CBotChatPageType * chatPageType,
66 uint numOptions
69 ClientInterfaceId= clientInterfaceId;
70 PageType= chatPageType;
71 NumOptions= numOptions;
74 // data -----------------------------------------------------------------
75 BOTCHATTYPE::TBotChatInterfaceId ClientInterfaceId; // id of interface to display on client
76 CBotChatPageType * PageType; // type of chat page
77 uint NumOptions; // number of options for player to click on
81 //////////////////////////////////////////////////////////////////////////////
82 // the structure for a state for bot chat automatons
84 struct SBotChatAutomatonState
86 // public data ----------------------------------------------------------
87 SBotChatPage *Page;
88 uint On[5]; // value to return on player click of slot 0..4
90 // ctor -----------------------------------------------------------------
91 SBotChatAutomatonState(SBotChatPage *page,uint on0=std::numeric_limits<uint>::max(),uint on1=std::numeric_limits<uint>::max(),uint on2=std::numeric_limits<uint>::max(),uint on3=std::numeric_limits<uint>::max(),uint on4=std::numeric_limits<uint>::max())
93 Page=page;
94 On[0]=on0;
95 On[1]=on1;
96 On[2]=on2;
97 On[3]=on3;
98 On[4]=on4;
100 // make sure the number of arguments supplied corresponds to the
101 // number of options prresent on the user interfac page
102 nlassert(page->NumOptions>=0 && page->NumOptions<=4);
103 nlassert(page->NumOptions==0 || On[page->NumOptions-1]!=std::numeric_limits<uint>::max());
104 nlassert(page->NumOptions==4 || On[page->NumOptions]==std::numeric_limits<uint>::max());
109 //////////////////////////////////////////////////////////////////////////////
110 // the structure for a bot chat automatons & a singleton for indexing
111 // automatons by name
113 struct SBotChatAutomaton
115 // public data ----------------------------------------------------------
116 string Name;
117 SBotChatAutomatonState *States;
118 uint Size;
120 // ctor -----------------------------------------------------------------
121 SBotChatAutomaton(string name, SBotChatAutomatonState *states,uint size)
123 Name=name;
124 States=states;
125 Size=size;
127 if (NameMap.find(name)!=NameMap.end())
129 nlwarning("SBotChatAutomaton::SBotChatAutomaton(): More than one instance with name: %s",name.c_str());
130 return;
132 NameMap[name]=this;
135 // dtor -----------------------------------------------------------------
136 ~SBotChatAutomaton()
138 map <string,SBotChatAutomaton *>::iterator it=NameMap.find(Name);
139 if (it!=NameMap.end() && (*it).second==this)
140 NameMap.erase(it);
141 // don't try to display a warning in a dtor as the warning system is
142 // probably already down
146 // singleton methods ----------------------------------------------------
147 static SBotChatAutomaton *getAutomatonByName(string name)
149 map <string,SBotChatAutomaton *>::iterator it=NameMap.find(name);
150 if (it==NameMap.end())
151 return NULL;
152 return (*it).second;
155 // singleton data -------------------------------------------------------
156 static map <string,SBotChatAutomaton *> NameMap;
158 map <string,SBotChatAutomaton *> SBotChatAutomaton::NameMap;
161 //////////////////////////////////////////////////////////////////////////////
162 //////////////////////////////////////////////////////////////////////////////
163 // Implementation of different code modules for handling different bot
164 // chat page types
166 //////////////////////////////////////////////////////////////////////////////
167 // this is a dummy page used to terminate chats
169 class CBotChatPageTypeDone: public CBotChatPageType
171 public:
172 virtual bool open(const CEntityId &player, const CEntityId &bot)
174 return false; // stop the bot chat!
177 virtual bool close(const CEntityId &player, const CEntityId &bot)
179 return false;
182 BotChatPageTypeDone;
185 //////////////////////////////////////////////////////////////////////////////
186 // definition for a chat page that contains static text and buttons for
187 // player to click on/ select
189 class CBotChatPageTypeTextOnly: public CBotChatPageType
191 public:
192 virtual bool open(const CEntityId &player, const CEntityId &bot)
194 return true;
197 virtual bool close(const CEntityId &player, const CEntityId &bot)
199 return true;
202 BotChatPageTypeTextOnly;
205 //////////////////////////////////////////////////////////////////////////////
206 // definition for a chat page that displays NEWS as well as other text
208 class CBotChatPageTypeNews: public CBotChatPageType
210 public:
211 virtual bool open(const CEntityId &player, const CEntityId &bot)
213 return true;
216 virtual bool close(const CEntityId &player, const CEntityId &bot)
218 return true;
221 BotChatPageTypeNews;
224 //////////////////////////////////////////////////////////////////////////////
225 // definition for a chat page that displays a SHOP interface
227 class CBotChatPageTypeShop: public CBotChatPageType
229 public:
230 virtual bool open(const CEntityId &player, const CEntityId &bot)
232 nlinfo ("player %s entered trade page", player.toString().c_str());
233 CMessage msgout( "TRADE_BEGIN" );
234 msgout.serial( const_cast<CEntityId &>(player) );
235 msgout.serial( const_cast<CEntityId &>(bot) );
236 sendMessageViaMirror( "WOS", msgout );
237 return true;
240 virtual bool close(const CEntityId &player, const CEntityId &bot)
242 nlinfo ("end of trade with player %s", player.toString().c_str());
243 CMessage msgout( "TRADE_END" );
244 msgout.serial( const_cast<CEntityId &>(player) );
245 msgout.serial( const_cast<CEntityId &>(bot) );
246 sendMessageViaMirror( "WOS", msgout );
247 sendMessageViaMirror( "EGS", msgout );
248 return true;
251 BotChatPageTypeShop;
254 //////////////////////////////////////////////////////////////////////////////
255 // definition for a chat page that displays a MISSION SHOP interface
257 class CBotChatPageTypeMissionShop: public CBotChatPageType
259 public:
260 virtual bool open(const CEntityId &player, const CEntityId &bot)
262 nlinfo ("player %s entered mission page", player.toString().c_str());
263 CMessage msgout( "MISSION_LIST_BEGIN" );
264 msgout.serial( const_cast<CEntityId &>(player) );
265 msgout.serial( const_cast<CEntityId &>(bot) );
266 sendMessageViaMirror( "WOS", msgout );
267 return true;
270 virtual bool close(const CEntityId &player, const CEntityId &bot)
272 nlinfo ("end of mission page with player %s", player.toString().c_str());
273 CMessage msgout( "MISSION_LIST_END" );
274 msgout.serial( const_cast<CEntityId &>(player) );
275 msgout.serial( const_cast<CEntityId &>(bot) );
276 sendMessageViaMirror( "EGS", msgout );
277 return true;
280 BotChatPageTypeMissionShop;
284 //////////////////////////////////////////////////////////////////////////////
285 //////////////////////////////////////////////////////////////////////////////
286 // Definitions of bot chat pages and automatons
288 // define the usable bot chat pages ------------------------------------------
289 SBotChatPage BotChatPageIntro (BOTCHATTYPE::Intro, &BotChatPageTypeTextOnly, 4);
290 SBotChatPage BotChatPageFriendly (BOTCHATTYPE::FriendlyMainPage, &BotChatPageTypeNews, 4);
291 SBotChatPage BotChatPageNeutral (BOTCHATTYPE::NeutralMainPage, &BotChatPageTypeNews, 3);
292 SBotChatPage BotChatPageHostile (BOTCHATTYPE::NastyMainPage, &BotChatPageTypeTextOnly, 1);
293 SBotChatPage BotChatPageMoreNews (BOTCHATTYPE::MoreNewsPage, &BotChatPageTypeNews, 2);
294 SBotChatPage BotChatPageShop (BOTCHATTYPE::BuySellPage, &BotChatPageTypeShop, 2);
295 SBotChatPage BotChatPageMissionShop (BOTCHATTYPE::MissionsPage, &BotChatPageTypeMissionShop, 2);
296 SBotChatPage BotChatPageDone (BOTCHATTYPE::Done, &BotChatPageTypeDone, 0);
298 // the default automaton -----------------------------------------------------
299 SBotChatAutomatonState BotChatStatesDefault[]=
301 SBotChatAutomatonState(&BotChatPageIntro,2,3,4,1), // 0 - friendly/ neutral/ hostile/ done
302 SBotChatAutomatonState(&BotChatPageDone), // 1
303 SBotChatAutomatonState(&BotChatPageFriendly,5,6,7,1), // 2 - more news/ buy sell/ mission/ done
304 SBotChatAutomatonState(&BotChatPageNeutral,6,7,1), // 3 - buy sell/ mission/ done
305 SBotChatAutomatonState(&BotChatPageHostile,1), // 4 - done
306 SBotChatAutomatonState(&BotChatPageMoreNews,2,1), // 5 - friendly/ done
307 SBotChatAutomatonState(&BotChatPageShop,3,1), // 6 - neutral/ done
308 SBotChatAutomatonState(&BotChatPageMissionShop,3,1), // 7 - neutral/ done
310 SBotChatAutomaton BotChatDefault("default",BotChatStatesDefault,sizeof(BotChatStatesDefault)/sizeof(BotChatStatesDefault[0]));
312 // the automaton for merchants -----------------------------------------------
313 SBotChatAutomatonState BotChatStatesMerchant[]=
315 SBotChatAutomatonState(&BotChatPageMoreNews,2,1), // 0 - shop/ done
316 SBotChatAutomatonState(&BotChatPageDone), // 1
317 SBotChatAutomatonState(&BotChatPageShop,0,1), // 2 - news/ done
319 SBotChatAutomaton BotChatMerchant("merchant",BotChatStatesMerchant,sizeof(BotChatStatesMerchant)/sizeof(BotChatStatesMerchant[0]));
321 // the automaton for walkers and talkers -------------------------------------
322 SBotChatAutomatonState BotChatStatesWalkerTalker[]=
324 SBotChatAutomatonState(&BotChatPageHostile,1), // 0
325 SBotChatAutomatonState(&BotChatPageDone) // 1
327 SBotChatAutomaton BotChatWalkerTalker("walker talker",BotChatStatesWalkerTalker,sizeof(BotChatStatesMerchant)/sizeof(BotChatStatesMerchant[0]));
330 //////////////////////////////////////////////////////////////////////////////
331 //////////////////////////////////////////////////////////////////////////////
332 // Represetnation of a conversation between a player and a bot
333 // includes conversation automaton state data
335 struct CBotChat
337 CBotChat () : Player(CEntityId::Unknown), Bot(CEntityId::Unknown) { }
338 CBotChat (CEntityId player, CEntityId bot, SBotChatAutomaton *automaton)
340 Player=player;
341 Bot=bot;
342 setAutomaton(automaton);
345 void setState(uint32 state)
347 if (state>=Automaton->Size && state!=std::numeric_limits<uint32>::max())
349 nlwarning("CBotChatEntry()::setState: Invalid state: %d",state);
350 return;
353 // if there is already a page open close it
354 if (CurrentState<Automaton->Size)
355 Automaton->States[CurrentState].Page->PageType->close(Player,Bot);
357 // open the new page
358 CurrentState=state;
359 if (state==std::numeric_limits<uint32>::max())
360 Done=true;
361 else
362 Done=!Automaton->States[CurrentState].Page->PageType->open(Player,Bot);
364 // transmit the new page id to the client
365 uint32 happyness=10;
366 BOTCHATTYPE::TBotChatInterfaceId interfaceId=Done?BOTCHATTYPE::Done:Automaton->States[CurrentState].Page->ClientInterfaceId;
367 NEWSTYPE::TNewsType newsType=NEWSTYPE::Unknown;
369 CMessage msgout("BOT_CHAT_SELECT_INTERFACE");
370 msgout.serial (Player);
371 msgout.serial (happyness);
372 msgout.serialEnum (interfaceId);
373 msgout.serialEnum (newsType);
374 sendMessageViaMirror("IOS", msgout);
377 void setAutomaton(SBotChatAutomaton *automaton)
379 Automaton=automaton;
380 CurrentState=std::numeric_limits<uint32>::max(); // set this to a ~0 so that setState doesn't try to clse existing page
381 setState(0);
384 void selectEntry (sint8 userInput)
386 // select the new page
387 if ((unsigned)userInput >= Automaton->States[CurrentState].Page->NumOptions)
389 nlwarning ("CBotChatEntry::selectEntry: For player %s: input out of bounds: %d", Player.toString().c_str(), userInput);
390 return;
393 // advance through the state table
394 setState(Automaton->States[CurrentState].On[userInput]);
397 void endChat ()
399 setState(std::numeric_limits<uint32>::max());
402 CEntityId Player;
403 CEntityId Bot;
404 SBotChatAutomaton *Automaton;
405 uint32 CurrentState;
406 bool Done;
410 //////////////////////////////////////////////////////////////////////////////
411 //////////////////////////////////////////////////////////////////////////////
412 // Singleton manager class central to the bot chat system
414 class CBotChatManager
416 public:
418 static void newChat(CEntityId player, CEntityId bot)
420 // make sure the player isn't already chatting
421 map<CEntityId, CBotChat>::iterator it = BotChatMap.find (player);
422 if (it != BotChatMap.end())
423 return;
425 // a bit of logging
426 nlinfo ("new chat between player %s and bot %s", player.toString().c_str(), bot.toString().c_str());
428 // call the CbBegin() callback to get the name of the automaton to use
429 string automatonName;
430 if (CbBegin!=NULL)
431 automatonName=CbBegin(player,bot);
432 else
433 automatonName="default";
434 SBotChatAutomaton *automaton=SBotChatAutomaton::getAutomatonByName(automatonName);
435 if (automaton==NULL)
437 nlwarning("- ignoring bot chat request as automaton '%s' not found",automatonName.c_str());
438 return;
441 // setup the new chat
442 BotChatMap[player] = CBotChat(player, bot, automaton);
445 static void endChat(CEntityId player)
447 CEntityId bot; // for use in callback ... at end of routine
449 map<CEntityId, CBotChat>::iterator it = BotChatMap.find (player);
450 if (it != BotChatMap.end())
452 bot=(*it).second.Bot;
454 nlinfo ("end of bot chat between player %s and bot %s", player.toString().c_str(),bot.toString().c_str());
456 // if the chat is still active then stop it
457 if ((*it).second.Done)
458 (*it).second.endChat();
460 // remove the map entry
461 BotChatMap.erase (it);
463 // **** this code may be dodgy 'cos its in a dtor
464 // **** if it is dodgy then we need to migrate from an STL map
465 // **** to some kind of custom structure that we can guarantee OK
468 if (CbEnd!=NULL)
469 CbEnd(player,bot);
472 static void treatInput(CEntityId player, sint8 userInput)
474 // locate the bot chat for the given player
475 map<CEntityId, CBotChat>::iterator it = BotChatMap.find (player);
476 if (it == BotChatMap.end())
478 nlwarning ("No bot chat with the player %s", player.toString().c_str());
479 return;
482 // pass the player input to the bot chat handler
483 (*it).second.selectEntry(userInput);
485 // check whether the bot chat is finished
486 if ((*it).second.Done)
487 endChat(player);
491 // static data for the singleton -----------------------------------------
492 static CBotChatInterface::TCallbackBegin CbBegin;
493 static CBotChatInterface::TCallbackEnd CbEnd;
494 static map<CEntityId, CBotChat> BotChatMap;
496 CBotChatInterface::TCallbackBegin CBotChatManager::CbBegin;
497 CBotChatInterface::TCallbackEnd CBotChatManager::CbEnd;
498 map<CEntityId, CBotChat> CBotChatManager::BotChatMap;
501 //////////////////////////////////////////////////////////////////////////////
502 //////////////////////////////////////////////////////////////////////////////
503 // message callbacks and callback table
505 static void cbBotChatStart (CMessage& msgin, const string &serviceName, uint16 serviceId )
507 CEntityId player, bot;
509 msgin.serial( player );
510 msgin.serial( bot );
512 CBotChatManager::newChat (player, bot);
515 static void cbBotChatSelectAnswer (CMessage& msgin, const string &serviceName, uint16 serviceId )
517 CEntityId player, bot;
518 sint8 answer;
520 msgin.serial( player );
521 msgin.serial( answer );
523 CBotChatManager::treatInput (player, answer);
527 static TUnifiedCallbackItem CbArray[]=
529 { "BOT_CHAT_START", cbBotChatStart },
530 { "BOT_CHAT_SELECT_ANSWER", cbBotChatSelectAnswer },
534 //////////////////////////////////////////////////////////////////////////////
535 //////////////////////////////////////////////////////////////////////////////
536 // Interface class providing API for bot chat system
538 void CBotChatInterface::init(CBotChatInterface::TCallbackBegin cbBegin,CBotChatInterface::TCallbackEnd cbEnd)
540 CBotChatManager::CbBegin=cbBegin;
541 CBotChatManager::CbEnd=cbEnd;
542 CUnifiedNetwork::getInstance()->addCallbackArray(CbArray, sizeof (CbArray) / sizeof (CbArray[0]));
545 void CBotChatInterface::release()
549 void CBotChatInterface::getBotChatPartners(CEntityId bot,vector<CEntityId> &result)
551 map<CEntityId, CBotChat>::iterator it;
552 for (it=CBotChatManager::BotChatMap.begin();it!=CBotChatManager::BotChatMap.end();++it)
553 if ((*it).second.Bot==bot)
554 result.push_back((*it).first);
557 void CBotChatInterface::endChatForPlayer(CEntityId player)
559 CBotChatManager::endChat(player);
562 void CBotChatInterface::endAllChatForBot(CEntityId bot)
564 map<CEntityId, CBotChat>::iterator it=CBotChatManager::BotChatMap.begin();
565 while (it!=CBotChatManager::BotChatMap.end())
567 map<CEntityId, CBotChat>::iterator next=it;
568 if ((*it).second.Bot==bot)
569 CBotChatManager::endChat((*it).first);
570 it=next;
616 //////////////////////
617 /// The following is probably out of date but I'm copying it here just in case...
621 #if 0
626 //////////////////////////////////////////////////////////////////////////////
627 //////////////////////////////////////////////////////////////////////////////
629 //////////////////////////////////////////////////////////////////////////////
630 // the parent class for bot chat page type classes
632 class CBotChatPageType
634 public:
635 // virtual interface ----------------------------------------------------
636 virtual bool open(const CEntityId &player, const CEntityId &bot)=0;
637 virtual bool close(const CEntityId &player, const CEntityId &bot)=0;
641 //////////////////////////////////////////////////////////////////////////////
642 // the structure for bot chat pages
644 struct SBotChatPage
646 // ctor -----------------------------------------------------------------
647 SBotChatPage(
648 BOTCHATTYPE::TBotChatInterfaceId clientInterfaceId,
649 CBotChatPageType * chatPageType,
650 uint numOptions
653 ClientInterfaceId= clientInterfaceId;
654 PageType= chatPageType;
655 NumOptions= numOptions;
658 // data -----------------------------------------------------------------
659 BOTCHATTYPE::TBotChatInterfaceId ClientInterfaceId; // id of interface to display on client
660 CBotChatPageType * PageType; // type of chat page
661 uint NumOptions; // number of options for player to click on
665 //////////////////////////////////////////////////////////////////////////////
666 // the structure for a state for bot chat automatons
668 struct SBotChatAutomatonState
670 // public data ----------------------------------------------------------
671 SBotChatPage *Page;
672 uint On[5]; // value to return on player click of slot 0..4
674 // ctor -----------------------------------------------------------------
675 SBotChatAutomatonState(SBotChatPage *page,uint on0=std::numeric_limits<uint>::max(),uint on1=std::numeric_limits<uint>::max(),uint on2=std::numeric_limits<uint>::max(),uint on3=std::numeric_limits<uint>::max(),uint on4=std::numeric_limits<uint>::max())
677 Page=page;
678 On[0]=on0;
679 On[1]=on1;
680 On[2]=on2;
681 On[3]=on3;
682 On[4]=on4;
684 // make sure the number of arguments supplied corresponds to the
685 // number of options prresent on the user interfac page
686 nlassert(page->NumOptions>=0 && page->NumOptions<=4);
687 nlassert(page->NumOptions==0 || On[page->NumOptions-1]!=std::numeric_limits<uint>::max());
688 nlassert(page->NumOptions==4 || On[page->NumOptions]==std::numeric_limits<uint>::max());
693 //////////////////////////////////////////////////////////////////////////////
694 // the structure for a bot chat automatons & a singleton for indexing
695 // automatons by name
697 struct SBotChatAutomaton
699 // public data ----------------------------------------------------------
700 string Name;
701 SBotChatAutomatonState *States;
702 uint Size;
704 // ctor -----------------------------------------------------------------
705 SBotChatAutomaton(string name, SBotChatAutomatonState *states,uint size)
707 Name=name;
708 States=states;
709 Size=size;
711 if (NameMap.find(name)!=NameMap.end())
713 nlwarning("SBotChatAutomaton::SBotChatAutomaton(): More than one instance with name: %s",name.c_str());
714 return;
716 NameMap[name]=this;
719 // dtor -----------------------------------------------------------------
720 ~SBotChatAutomaton()
722 map <string,SBotChatAutomaton *>::iterator it=NameMap.find(Name);
723 if (it!=NameMap.end() && (*it).second==this)
724 NameMap.erase(it);
725 // don't try to display a warning in a dtor as the warning system is
726 // probably already down
730 // singleton methods ----------------------------------------------------
731 static SBotChatAutomaton *getAutomatonByName(string name)
733 map <string,SBotChatAutomaton *>::iterator it=NameMap.find(name);
734 if (it==NameMap.end())
735 return NULL;
736 return (*it).second;
739 // singleton data -------------------------------------------------------
740 static map <string,SBotChatAutomaton *> NameMap;
742 map <string,SBotChatAutomaton *> SBotChatAutomaton::NameMap;
745 //////////////////////////////////////////////////////////////////////////////
746 //////////////////////////////////////////////////////////////////////////////
747 // Implementation of different code modules for handling different bot
748 // chat page types
750 //////////////////////////////////////////////////////////////////////////////
751 // this is a dummy page used to terminate chats
753 class CBotChatPageTypeDone: public CBotChatPageType
755 public:
756 virtual bool open(const CEntityId &player, const CEntityId &bot)
758 return false; // stop the bot chat!
761 virtual bool close(const CEntityId &player, const CEntityId &bot)
763 return false;
766 BotChatPageTypeDone;
769 //////////////////////////////////////////////////////////////////////////////
770 // definition for a chat page that contains static text and buttons for
771 // player to click on/ select
773 class CBotChatPageTypeTextOnly: public CBotChatPageType
775 public:
776 virtual bool open(const CEntityId &player, const CEntityId &bot)
778 return true;
781 virtual bool close(const CEntityId &player, const CEntityId &bot)
783 return true;
786 BotChatPageTypeTextOnly;
789 //////////////////////////////////////////////////////////////////////////////
790 // definition for a chat page that displays NEWS as well as other text
792 class CBotChatPageTypeNews: public CBotChatPageType
794 public:
795 virtual bool open(const CEntityId &player, const CEntityId &bot)
797 return true;
800 virtual bool close(const CEntityId &player, const CEntityId &bot)
802 return true;
805 BotChatPageTypeNews;
808 //////////////////////////////////////////////////////////////////////////////
809 // definition for a chat page that displays a SHOP interface
811 class CBotChatPageTypeShop: public CBotChatPageType
813 public:
814 virtual bool open(const CEntityId &player, const CEntityId &bot)
816 nlinfo ("player %s entered trade page", player.toString().c_str());
817 CMessage msgout( "TRADE_BEGIN" );
818 msgout.serial( const_cast<CEntityId &>(player) );
819 msgout.serial( const_cast<CEntityId &>(bot) );
820 sendMessageViaMirror( "WOS", msgout );
821 return true;
824 virtual bool close(const CEntityId &player, const CEntityId &bot)
826 nlinfo ("end of trade with player %s", player.toString().c_str());
827 CMessage msgout( "TRADE_END" );
828 msgout.serial( const_cast<CEntityId &>(player) );
829 msgout.serial( const_cast<CEntityId &>(bot) );
830 sendMessageViaMirror( "WOS", msgout );
831 sendMessageViaMirror( "EGS", msgout );
832 return true;
835 BotChatPageTypeShop;
838 //////////////////////////////////////////////////////////////////////////////
839 // definition for a chat page that displays a MISSION SHOP interface
841 class CBotChatPageTypeMissionShop: public CBotChatPageType
843 public:
844 virtual bool open(const CEntityId &player, const CEntityId &bot)
846 nlinfo ("player %s entered mission page", player.toString().c_str());
847 CMessage msgout( "MISSION_LIST_BEGIN" );
848 msgout.serial( const_cast<CEntityId &>(player) );
849 msgout.serial( const_cast<CEntityId &>(bot) );
850 sendMessageViaMirror( "WOS", msgout );
851 return true;
854 virtual bool close(const CEntityId &player, const CEntityId &bot)
856 nlinfo ("end of mission page with player %s", player.toString().c_str());
857 CMessage msgout( "MISSION_LIST_END" );
858 msgout.serial( const_cast<CEntityId &>(player) );
859 msgout.serial( const_cast<CEntityId &>(bot) );
860 sendMessageViaMirror( "EGS", msgout );
861 return true;
864 BotChatPageTypeMissionShop;
868 //////////////////////////////////////////////////////////////////////////////
869 //////////////////////////////////////////////////////////////////////////////
870 // Definitions of bot chat pages and automatons
872 // define the usable bot chat pages ------------------------------------------
873 SBotChatPage BotChatPageIntro (BOTCHATTYPE::Intro, &BotChatPageTypeTextOnly, 4);
874 SBotChatPage BotChatPageFriendly (BOTCHATTYPE::FriendlyMainPage, &BotChatPageTypeNews, 4);
875 SBotChatPage BotChatPageNeutral (BOTCHATTYPE::NeutralMainPage, &BotChatPageTypeNews, 3);
876 SBotChatPage BotChatPageHostile (BOTCHATTYPE::NastyMainPage, &BotChatPageTypeTextOnly, 1);
877 SBotChatPage BotChatPageMoreNews (BOTCHATTYPE::MoreNewsPage, &BotChatPageTypeNews, 2);
878 SBotChatPage BotChatPageShop (BOTCHATTYPE::BuySellPage, &BotChatPageTypeShop, 2);
879 SBotChatPage BotChatPageMissionShop (BOTCHATTYPE::MissionsPage, &BotChatPageTypeMissionShop, 2);
880 SBotChatPage BotChatPageDone (BOTCHATTYPE::Done, &BotChatPageTypeDone, 0);
882 // the default automaton -----------------------------------------------------
883 SBotChatAutomatonState BotChatStatesDefault[]=
885 SBotChatAutomatonState(&BotChatPageIntro,2,3,4,1), // 0 - friendly/ neutral/ hostile/ done
886 SBotChatAutomatonState(&BotChatPageDone), // 1
887 SBotChatAutomatonState(&BotChatPageFriendly,5,6,7,1), // 2 - more news/ buy sell/ mission/ done
888 SBotChatAutomatonState(&BotChatPageNeutral,6,7,1), // 3 - buy sell/ mission/ done
889 SBotChatAutomatonState(&BotChatPageHostile,1), // 4 - done
890 SBotChatAutomatonState(&BotChatPageMoreNews,2,1), // 5 - friendly/ done
891 SBotChatAutomatonState(&BotChatPageShop,3,1), // 6 - neutral/ done
892 SBotChatAutomatonState(&BotChatPageMissionShop,3,1), // 7 - neutral/ done
894 SBotChatAutomaton BotChatDefault("default",BotChatStatesDefault,sizeof(BotChatStatesDefault)/sizeof(BotChatStatesDefault[0]));
896 // the automaton for merchants -----------------------------------------------
897 SBotChatAutomatonState BotChatStatesMerchant[]=
899 SBotChatAutomatonState(&BotChatPageMoreNews,2,1), // 0 - shop/ done
900 SBotChatAutomatonState(&BotChatPageDone), // 1
901 SBotChatAutomatonState(&BotChatPageShop,0,1), // 2 - news/ done
903 SBotChatAutomaton BotChatMerchant("merchant",BotChatStatesMerchant,sizeof(BotChatStatesMerchant)/sizeof(BotChatStatesMerchant[0]));
905 // the automaton for walkers and talkers -------------------------------------
906 SBotChatAutomatonState BotChatStatesWalkerTalker[]=
908 SBotChatAutomatonState(&BotChatPageHostile,1), // 0
909 SBotChatAutomatonState(&BotChatPageDone) // 1
911 SBotChatAutomaton BotChatWalkerTalker("walker talker",BotChatStatesWalkerTalker,sizeof(BotChatStatesMerchant)/sizeof(BotChatStatesMerchant[0]));
914 //////////////////////////////////////////////////////////////////////////////
915 //////////////////////////////////////////////////////////////////////////////
916 // Represetnation of a conversation between a player and a bot
917 // includes conversation automaton state data
919 struct CBotChat
921 CBotChat () : Player(CEntityId::Unknown), Bot(CEntityId::Unknown) { }
922 CBotChat (CEntityId player, CEntityId bot, SBotChatAutomaton *automaton)
924 Player=player;
925 Bot=bot;
926 setAutomaton(automaton);
929 void setState(uint32 state)
931 if (state>=Automaton->Size && state!=std::numeric_limits<uint32>::max())
933 nlwarning("CBotChatEntry()::setState: Invalid state: %d",state);
934 return;
937 // if there is already a page open close it
938 if (CurrentState<Automaton->Size)
939 Automaton->States[CurrentState].Page->PageType->close(Player,Bot);
941 // open the new page
942 CurrentState=state;
943 if (state==std::numeric_limits<uint32>::max())
944 Done=true;
945 else
946 Done=!Automaton->States[CurrentState].Page->PageType->open(Player,Bot);
948 // transmit the new page id to the client
949 uint32 happyness=10;
950 BOTCHATTYPE::TBotChatInterfaceId interfaceId=Done?BOTCHATTYPE::Done:Automaton->States[CurrentState].Page->ClientInterfaceId;
951 NEWSTYPE::TNewsType newsType=NEWSTYPE::Unknown;
953 CMessage msgout("BOT_CHAT_SELECT_INTERFACE");
954 msgout.serial (Player);
955 msgout.serial (happyness);
956 msgout.serialEnum (interfaceId);
957 msgout.serialEnum (newsType);
958 sendMessageViaMirror("IOS", msgout);
961 void setAutomaton(SBotChatAutomaton *automaton)
963 Automaton=automaton;
964 CurrentState=std::numeric_limits<uint32>::max(); // set this to a std::numeric_limits<uint32>::max() so that setState doesn't try to clse existing page
965 setState(0);
968 void selectEntry (sint8 userInput)
970 // select the new page
971 if ((unsigned)userInput >= Automaton->States[CurrentState].Page->NumOptions)
973 nlwarning ("CBotChatEntry::selectEntry: For player %s: input out of bounds: %d", Player.toString().c_str(), userInput);
974 return;
977 // advance through the state table
978 setState(Automaton->States[CurrentState].On[userInput]);
981 void endChat ()
983 setState(std::numeric_limits<uint32>::max());
986 CEntityId Player;
987 CEntityId Bot;
988 SBotChatAutomaton *Automaton;
989 uint32 CurrentState;
990 bool Done;
994 //////////////////////////////////////////////////////////////////////////////
995 //////////////////////////////////////////////////////////////////////////////
996 // Singleton manager class central to the bot chat system
998 class CBotChatManager
1000 public:
1002 static void newChat(CEntityId player, CEntityId bot)
1004 // make sure the player isn't already chatting
1005 map<CEntityId, CBotChat>::iterator it = BotChatMap.find (player);
1006 if (it != BotChatMap.end())
1007 return;
1009 // a bit of logging
1010 nlinfo ("new chat between player %s and bot %s", player.toString().c_str(), bot.toString().c_str());
1012 // call the CbBegin() callback to get the name of the automaton to use
1013 string automatonName;
1014 if (CbBegin!=NULL)
1015 automatonName=CbBegin(player,bot);
1016 else
1017 automatonName="default";
1018 SBotChatAutomaton *automaton=SBotChatAutomaton::getAutomatonByName(automatonName);
1019 if (automaton==NULL)
1021 nlwarning("- ignoring bot chat request as automaton '%s' not found",automatonName.c_str());
1022 return;
1025 // setup the new chat
1026 BotChatMap[player] = CBotChat(player, bot, automaton);
1029 static void endChat(CEntityId player)
1031 CEntityId bot; // for use in callback ... at end of routine
1033 map<CEntityId, CBotChat>::iterator it = BotChatMap.find (player);
1034 if (it != BotChatMap.end())
1036 bot=(*it).second.Bot;
1038 nlinfo ("end of bot chat between player %s and bot %s", player.toString().c_str(),bot.toString().c_str());
1040 // if the chat is still active then stop it
1041 if ((*it).second.Done)
1042 (*it).second.endChat();
1044 // remove the map entry
1045 BotChatMap.erase (it);
1047 // **** this code may be dodgy 'cos its in a dtor
1048 // **** if it is dodgy then we need to migrate from an STL map
1049 // **** to some kind of custom structure that we can guarantee OK
1052 if (CbEnd!=NULL)
1053 CbEnd(player,bot);
1056 static void treatInput(CEntityId player, sint8 userInput)
1058 // locate the bot chat for the given player
1059 map<CEntityId, CBotChat>::iterator it = BotChatMap.find (player);
1060 if (it == BotChatMap.end())
1062 nlwarning ("No bot chat with the player %s", player.toString().c_str());
1063 return;
1066 // pass the player input to the bot chat handler
1067 (*it).second.selectEntry(userInput);
1069 // check whether the bot chat is finished
1070 if ((*it).second.Done)
1071 endChat(player);
1075 // static data for the singleton -----------------------------------------
1076 static CBotChatInterface::TCallbackBegin CbBegin;
1077 static CBotChatInterface::TCallbackEnd CbEnd;
1078 static map<CEntityId, CBotChat> BotChatMap;
1080 CBotChatInterface::TCallbackBegin CBotChatManager::CbBegin;
1081 CBotChatInterface::TCallbackEnd CBotChatManager::CbEnd;
1082 map<CEntityId, CBotChat> CBotChatManager::BotChatMap;
1085 //////////////////////////////////////////////////////////////////////////////
1086 //////////////////////////////////////////////////////////////////////////////
1087 // message callbacks and callback table
1089 static void cbBotChatStart (CMessage& msgin, const string &serviceName, uint16 serviceId )
1091 CEntityId player, bot;
1093 msgin.serial( player );
1094 msgin.serial( bot );
1096 CBotChatManager::newChat (player, bot);
1099 static void cbBotChatSelectAnswer (CMessage& msgin, const string &serviceName, uint16 serviceId )
1101 CEntityId player, bot;
1102 sint8 answer;
1104 msgin.serial( player );
1105 msgin.serial( answer );
1107 CBotChatManager::treatInput (player, answer);
1111 static TUnifiedCallbackItem CbArray[]=
1113 { "BOT_CHAT_START", cbBotChatStart },
1114 { "BOT_CHAT_SELECT_ANSWER", cbBotChatSelectAnswer },
1118 //////////////////////////////////////////////////////////////////////////////
1119 //////////////////////////////////////////////////////////////////////////////
1120 // Interface class providing API for bot chat system
1122 void CBotChatInterface::init(CBotChatInterface::TCallbackBegin cbBegin,CBotChatInterface::TCallbackEnd cbEnd)
1124 CBotChatManager::CbBegin=cbBegin;
1125 CBotChatManager::CbEnd=cbEnd;
1126 CUnifiedNetwork::getInstance()->addCallbackArray(CbArray, sizeof (CbArray) / sizeof (CbArray[0]));
1129 void CBotChatInterface::release()
1133 void CBotChatInterface::getBotChatPartners(CEntityId bot,vector<CEntityId> &result)
1135 map<CEntityId, CBotChat>::iterator it;
1136 for (it=CBotChatManager::BotChatMap.begin();it!=CBotChatManager::BotChatMap.end();++it)
1137 if ((*it).second.Bot==bot)
1138 result.push_back((*it).first);
1141 void CBotChatInterface::endChatForPlayer(CEntityId player)
1143 CBotChatManager::endChat(player);
1146 void CBotChatInterface::endAllChatForBot(CEntityId bot)
1148 map<CEntityId, CBotChat>::iterator it=CBotChatManager::BotChatMap.begin();
1149 while (it!=CBotChatManager::BotChatMap.end())
1151 map<CEntityId, CBotChat>::iterator next=it;
1152 if ((*it).second.Bot==bot)
1153 CBotChatManager::endChat((*it).first);
1154 it=next;
1158 #endif
1160 #endif