1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
22 //#include "bot_chat_interface.h"
23 #include "game_share/synchronised_message.h"
24 #include "game_share/bot_chat_types.h"
28 #include "nel/net/unified_network.h"
31 #include "game_share/news_types.h"
32 #include "game_share/bot_chat_types.h"
35 #include "bot_chat_interface.h"
38 using namespace NLMISC
;
39 using namespace NLNET
;
42 //////////////////////////////////////////////////////////////////////////////
43 //////////////////////////////////////////////////////////////////////////////
45 //////////////////////////////////////////////////////////////////////////////
46 // the parent class for bot chat page type classes
48 class CBotChatPageType
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
62 // ctor -----------------------------------------------------------------
64 BOTCHATTYPE::TBotChatInterfaceId clientInterfaceId
,
65 CBotChatPageType
* chatPageType
,
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 ----------------------------------------------------------
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())
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 ----------------------------------------------------------
117 SBotChatAutomatonState
*States
;
120 // ctor -----------------------------------------------------------------
121 SBotChatAutomaton(string name
, SBotChatAutomatonState
*states
,uint size
)
127 if (NameMap
.find(name
)!=NameMap
.end())
129 nlwarning("SBotChatAutomaton::SBotChatAutomaton(): More than one instance with name: %s",name
.c_str());
135 // dtor -----------------------------------------------------------------
138 map
<string
,SBotChatAutomaton
*>::iterator it
=NameMap
.find(Name
);
139 if (it
!=NameMap
.end() && (*it
).second
==this)
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())
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
166 //////////////////////////////////////////////////////////////////////////////
167 // this is a dummy page used to terminate chats
169 class CBotChatPageTypeDone
: public CBotChatPageType
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
)
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
192 virtual bool open(const CEntityId
&player
, const CEntityId
&bot
)
197 virtual bool close(const CEntityId
&player
, const CEntityId
&bot
)
202 BotChatPageTypeTextOnly
;
205 //////////////////////////////////////////////////////////////////////////////
206 // definition for a chat page that displays NEWS as well as other text
208 class CBotChatPageTypeNews
: public CBotChatPageType
211 virtual bool open(const CEntityId
&player
, const CEntityId
&bot
)
216 virtual bool close(const CEntityId
&player
, const CEntityId
&bot
)
224 //////////////////////////////////////////////////////////////////////////////
225 // definition for a chat page that displays a SHOP interface
227 class CBotChatPageTypeShop
: public CBotChatPageType
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
);
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
);
254 //////////////////////////////////////////////////////////////////////////////
255 // definition for a chat page that displays a MISSION SHOP interface
257 class CBotChatPageTypeMissionShop
: public CBotChatPageType
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
);
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
);
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
337 CBotChat () : Player(CEntityId::Unknown
), Bot(CEntityId::Unknown
) { }
338 CBotChat (CEntityId player
, CEntityId bot
, SBotChatAutomaton
*automaton
)
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
);
353 // if there is already a page open close it
354 if (CurrentState
<Automaton
->Size
)
355 Automaton
->States
[CurrentState
].Page
->PageType
->close(Player
,Bot
);
359 if (state
==std::numeric_limits
<uint32
>::max())
362 Done
=!Automaton
->States
[CurrentState
].Page
->PageType
->open(Player
,Bot
);
364 // transmit the new page id to the client
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
)
380 CurrentState
=std::numeric_limits
<uint32
>::max(); // set this to a ~0 so that setState doesn't try to clse existing page
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
);
393 // advance through the state table
394 setState(Automaton
->States
[CurrentState
].On
[userInput
]);
399 setState(std::numeric_limits
<uint32
>::max());
404 SBotChatAutomaton
*Automaton
;
410 //////////////////////////////////////////////////////////////////////////////
411 //////////////////////////////////////////////////////////////////////////////
412 // Singleton manager class central to the bot chat system
414 class CBotChatManager
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())
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
;
431 automatonName
=CbBegin(player
,bot
);
433 automatonName
="default";
434 SBotChatAutomaton
*automaton
=SBotChatAutomaton::getAutomatonByName(automatonName
);
437 nlwarning("- ignoring bot chat request as automaton '%s' not found",automatonName
.c_str());
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
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());
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
)
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
);
512 CBotChatManager::newChat (player
, bot
);
515 static void cbBotChatSelectAnswer (CMessage
& msgin
, const string
&serviceName
, uint16 serviceId
)
517 CEntityId player
, bot
;
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
);
616 //////////////////////
617 /// The following is probably out of date but I'm copying it here just in case...
626 //////////////////////////////////////////////////////////////////////////////
627 //////////////////////////////////////////////////////////////////////////////
629 //////////////////////////////////////////////////////////////////////////////
630 // the parent class for bot chat page type classes
632 class CBotChatPageType
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
646 // ctor -----------------------------------------------------------------
648 BOTCHATTYPE::TBotChatInterfaceId clientInterfaceId
,
649 CBotChatPageType
* chatPageType
,
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 ----------------------------------------------------------
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())
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 ----------------------------------------------------------
701 SBotChatAutomatonState
*States
;
704 // ctor -----------------------------------------------------------------
705 SBotChatAutomaton(string name
, SBotChatAutomatonState
*states
,uint size
)
711 if (NameMap
.find(name
)!=NameMap
.end())
713 nlwarning("SBotChatAutomaton::SBotChatAutomaton(): More than one instance with name: %s",name
.c_str());
719 // dtor -----------------------------------------------------------------
722 map
<string
,SBotChatAutomaton
*>::iterator it
=NameMap
.find(Name
);
723 if (it
!=NameMap
.end() && (*it
).second
==this)
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())
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
750 //////////////////////////////////////////////////////////////////////////////
751 // this is a dummy page used to terminate chats
753 class CBotChatPageTypeDone
: public CBotChatPageType
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
)
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
776 virtual bool open(const CEntityId
&player
, const CEntityId
&bot
)
781 virtual bool close(const CEntityId
&player
, const CEntityId
&bot
)
786 BotChatPageTypeTextOnly
;
789 //////////////////////////////////////////////////////////////////////////////
790 // definition for a chat page that displays NEWS as well as other text
792 class CBotChatPageTypeNews
: public CBotChatPageType
795 virtual bool open(const CEntityId
&player
, const CEntityId
&bot
)
800 virtual bool close(const CEntityId
&player
, const CEntityId
&bot
)
808 //////////////////////////////////////////////////////////////////////////////
809 // definition for a chat page that displays a SHOP interface
811 class CBotChatPageTypeShop
: public CBotChatPageType
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
);
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
);
838 //////////////////////////////////////////////////////////////////////////////
839 // definition for a chat page that displays a MISSION SHOP interface
841 class CBotChatPageTypeMissionShop
: public CBotChatPageType
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
);
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
);
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
921 CBotChat () : Player(CEntityId::Unknown
), Bot(CEntityId::Unknown
) { }
922 CBotChat (CEntityId player
, CEntityId bot
, SBotChatAutomaton
*automaton
)
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
);
937 // if there is already a page open close it
938 if (CurrentState
<Automaton
->Size
)
939 Automaton
->States
[CurrentState
].Page
->PageType
->close(Player
,Bot
);
943 if (state
==std::numeric_limits
<uint32
>::max())
946 Done
=!Automaton
->States
[CurrentState
].Page
->PageType
->open(Player
,Bot
);
948 // transmit the new page id to the client
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
)
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
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
);
977 // advance through the state table
978 setState(Automaton
->States
[CurrentState
].On
[userInput
]);
983 setState(std::numeric_limits
<uint32
>::max());
988 SBotChatAutomaton
*Automaton
;
994 //////////////////////////////////////////////////////////////////////////////
995 //////////////////////////////////////////////////////////////////////////////
996 // Singleton manager class central to the bot chat system
998 class CBotChatManager
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())
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
;
1015 automatonName
=CbBegin(player
,bot
);
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());
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
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());
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
)
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
;
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
);