conversationmgr.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Combat *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/conversationmgr.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 1/15/02 9:37p $*
  29. * *
  30. * $Revision:: 24 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "conversationmgr.h"
  36. #include "combatchunkid.h"
  37. #include "chunkio.h"
  38. #include "vector3.h"
  39. #include "pscene.h"
  40. #include "physicalgameobj.h"
  41. #include "aabox.h"
  42. #include "conversation.h"
  43. #include "wwmath.h"
  44. #include "soldierobserver.h"
  45. #include "playertype.h"
  46. #include "activeconversation.h"
  47. #include "phys.h"
  48. #include "string_ids.h"
  49. #include "oratortypes.h"
  50. ////////////////////////////////////////////////////////////////
  51. // Constants
  52. ////////////////////////////////////////////////////////////////
  53. enum
  54. {
  55. CHUNKID_VARIABLES = 0x08090315,
  56. CHUNKID_OLD_CONVERSATION,
  57. CHUNKID_ACTIVE_CONVERSATION,
  58. CHUNKID_CONVERSATION_CATEGORY,
  59. CHUNKID_CONVERSATION
  60. };
  61. enum
  62. {
  63. VARID_NEXT_ACTIVE_CONVERSATION_ID = 0,
  64. XXX_VARID_NEXT_GLOBAL_CONVERSATION_ID,
  65. VARID_NEXT_LEVEL_CONVERSATION_ID,
  66. VARID_NEXT_GLOBAL_CONVERSATION_ID,
  67. };
  68. static const float BUDDY_LOCATE_CX = 7;
  69. static const float BUDDY_LOCATE_CY = 7;
  70. static const float BUDDY_LOCATE_CZ = 3;
  71. static const int ACTIVE_CONVERSATION_START_ID = 1000;
  72. static const int LEVEL_CONVERSATION_START_ID = 1;
  73. static const int GLOBAL_CONVERSATION_START_ID = 100000;
  74. ////////////////////////////////////////////////////////////////
  75. // Static member initialization
  76. ////////////////////////////////////////////////////////////////
  77. CONVERSATION_LIST ConversationMgrClass::ConversationList[CATEGORY_MAX];
  78. ACTIVE_CONVERSATION_LIST ConversationMgrClass::ActiveConversationList;
  79. int ConversationMgrClass::NextActiveConversationID = ACTIVE_CONVERSATION_START_ID;
  80. int ConversationMgrClass::NextGlobalConversationID = GLOBAL_CONVERSATION_START_ID;
  81. int ConversationMgrClass::NextLevelConversationID = LEVEL_CONVERSATION_START_ID;
  82. ConversationMgrClass::CATEGORY ConversationMgrClass::SaveCategoryID = CATEGORY_GLOBAL;
  83. bool ConversationMgrClass::DisplayEmotIcons = false;
  84. ////////////////////////////////////////////////////////////////
  85. // Singleton instance
  86. ////////////////////////////////////////////////////////////////
  87. ConversationMgrClass _ConversationMgrSaveLoad;
  88. ////////////////////////////////////////////////////////////////
  89. //
  90. // ConversationMgrClass
  91. //
  92. ////////////////////////////////////////////////////////////////
  93. ConversationMgrClass::ConversationMgrClass (void)
  94. {
  95. return ;
  96. }
  97. ////////////////////////////////////////////////////////////////
  98. //
  99. // ~ConversationMgrClass
  100. //
  101. ////////////////////////////////////////////////////////////////
  102. ConversationMgrClass::~ConversationMgrClass (void)
  103. {
  104. Reset ();
  105. return ;
  106. }
  107. ////////////////////////////////////////////////////////////////
  108. //
  109. // Reset_Conversations
  110. //
  111. ////////////////////////////////////////////////////////////////
  112. void
  113. ConversationMgrClass::Reset_Conversations (int category_index, bool reset_start_id)
  114. {
  115. if (reset_start_id && category_index == CATEGORY_LEVEL) {
  116. NextLevelConversationID = LEVEL_CONVERSATION_START_ID;
  117. }
  118. int count = ConversationList[category_index].Count ();
  119. //
  120. // Release our hold on all the conversations in this category
  121. //
  122. for (int index = 0; index < count; index ++) {
  123. ConversationClass *conversation = ConversationList[category_index][index];
  124. REF_PTR_RELEASE (conversation);
  125. }
  126. ConversationList[category_index].Delete_All ();
  127. return ;
  128. }
  129. ////////////////////////////////////////////////////////////////
  130. //
  131. // Reset_All_Other_Conversations
  132. //
  133. ////////////////////////////////////////////////////////////////
  134. void
  135. ConversationMgrClass::Reset_All_Other_Conversations (ActiveConversationClass *active_conversation)
  136. {
  137. NextActiveConversationID = active_conversation->Get_ID () + 1;
  138. //
  139. // Release our hold on all the other conversations
  140. //
  141. int count = ActiveConversationList.Count ();
  142. for (int index = 0; index < count; index ++) {
  143. ActiveConversationClass *conversation = ActiveConversationList[index];
  144. if (conversation != NULL && conversation != active_conversation) {
  145. conversation->Stop_Conversation (ACTION_COMPLETE_CONVERSATION_INTERRUPTED);
  146. REF_PTR_RELEASE (conversation);
  147. }
  148. }
  149. ActiveConversationList.Delete_All ();
  150. //
  151. // Add this conversation back into the list
  152. //
  153. ActiveConversationList.Add (active_conversation);
  154. return ;
  155. }
  156. ////////////////////////////////////////////////////////////////
  157. //
  158. // Reset_Active_Conversations
  159. //
  160. ////////////////////////////////////////////////////////////////
  161. void
  162. ConversationMgrClass::Reset_Active_Conversations (void)
  163. {
  164. NextActiveConversationID = ACTIVE_CONVERSATION_START_ID;
  165. //
  166. // Release our hold on all the active conversations
  167. //
  168. while (ActiveConversationList.Count () > 0) {
  169. ActiveConversationClass *active_conversation = ActiveConversationList[0];
  170. ActiveConversationList.Delete (0);
  171. if (active_conversation != NULL) {
  172. active_conversation->Stop_Conversation (ACTION_COMPLETE_CONVERSATION_INTERRUPTED);
  173. }
  174. REF_PTR_RELEASE (active_conversation);
  175. }
  176. return ;
  177. }
  178. ////////////////////////////////////////////////////////////////
  179. //
  180. // Reset
  181. //
  182. ////////////////////////////////////////////////////////////////
  183. void
  184. ConversationMgrClass::Reset (void)
  185. {
  186. //
  187. // Release our hold on all the active conversations
  188. //
  189. Reset_Active_Conversations ();
  190. //
  191. // Release our hold on all the conversations
  192. //
  193. for (int cat_index = 0; cat_index < CATEGORY_MAX; cat_index ++) {
  194. Reset_Conversations (cat_index);
  195. }
  196. return ;
  197. }
  198. ////////////////////////////////////////////////////////////////
  199. //
  200. // Chunk_ID
  201. //
  202. ////////////////////////////////////////////////////////////////
  203. uint32
  204. ConversationMgrClass::Chunk_ID (void) const
  205. {
  206. return CHUNKID_CONVERSATION_MGR;
  207. }
  208. ////////////////////////////////////////////////////////////////
  209. //
  210. // Save
  211. //
  212. ////////////////////////////////////////////////////////////////
  213. bool
  214. ConversationMgrClass::Save (ChunkSaveClass &csave)
  215. {
  216. csave.Begin_Chunk (CHUNKID_VARIABLES);
  217. WRITE_MICRO_CHUNK (csave, VARID_NEXT_ACTIVE_CONVERSATION_ID, NextActiveConversationID);
  218. if (SaveCategoryID == CATEGORY_GLOBAL) {
  219. WRITE_MICRO_CHUNK (csave, VARID_NEXT_GLOBAL_CONVERSATION_ID, NextGlobalConversationID);
  220. } else {
  221. WRITE_MICRO_CHUNK (csave, VARID_NEXT_LEVEL_CONVERSATION_ID, NextLevelConversationID);
  222. }
  223. csave.End_Chunk ();
  224. //
  225. // Save the conversations in the current category
  226. //
  227. csave.Begin_Chunk (CHUNKID_CONVERSATION_CATEGORY);
  228. //
  229. // Save the category ID
  230. //
  231. csave.Write (&SaveCategoryID, sizeof (SaveCategoryID));
  232. //
  233. // Save each conversation in this category
  234. //
  235. int count = ConversationList[SaveCategoryID].Count ();
  236. for (int index = 0; index < count; index ++) {
  237. ConversationClass *conversation = ConversationList[SaveCategoryID][index];
  238. if (conversation != NULL) {
  239. csave.Begin_Chunk (CHUNKID_CONVERSATION);
  240. conversation->Save (csave);
  241. csave.End_Chunk ();
  242. }
  243. }
  244. csave.End_Chunk ();
  245. //
  246. // Save each active conversation to its own chunk
  247. //
  248. count = ActiveConversationList.Count ();
  249. for (index = 0; index < count; index ++) {
  250. ActiveConversationClass *active_conversation = ActiveConversationList[index];
  251. if (active_conversation != NULL) {
  252. csave.Begin_Chunk (CHUNKID_ACTIVE_CONVERSATION);
  253. active_conversation->Save (csave);
  254. csave.End_Chunk ();
  255. }
  256. }
  257. return true;
  258. }
  259. ////////////////////////////////////////////////////////////////
  260. //
  261. // Load_Conversations
  262. //
  263. ////////////////////////////////////////////////////////////////
  264. bool
  265. ConversationMgrClass::Load_Conversations (ChunkLoadClass &cload, int category_id)
  266. {
  267. while (cload.Open_Chunk ()) {
  268. switch (cload.Cur_Chunk_ID ()) {
  269. case CHUNKID_CONVERSATION:
  270. {
  271. //
  272. // Create a new object and its state from the chunk
  273. //
  274. ConversationClass *conversation = new ConversationClass;
  275. SET_REF_OWNER( conversation );
  276. conversation->Load (cload);
  277. //
  278. // Debug check to ensure we've got the correct cateogry
  279. //
  280. WWASSERT (conversation->Get_Category_ID () == category_id);
  281. //
  282. // Add this conversation to our list
  283. //
  284. Add_Conversation (conversation);
  285. REF_PTR_RELEASE (conversation);
  286. }
  287. break;
  288. }
  289. cload.Close_Chunk ();
  290. }
  291. return true;
  292. }
  293. ////////////////////////////////////////////////////////////////
  294. //
  295. // Load
  296. //
  297. ////////////////////////////////////////////////////////////////
  298. bool
  299. ConversationMgrClass::Load (ChunkLoadClass &cload)
  300. {
  301. int old_style_conv_count = 0;
  302. //
  303. // Remove all currently active conversations
  304. //
  305. Reset_Active_Conversations ();
  306. while (cload.Open_Chunk ()) {
  307. switch (cload.Cur_Chunk_ID ()) {
  308. case CHUNKID_ACTIVE_CONVERSATION:
  309. {
  310. //
  311. // Create a new object and its state from the chunk
  312. //
  313. ActiveConversationClass *active_conversation = new ActiveConversationClass;
  314. active_conversation->Load (cload);
  315. //
  316. // Add this new active conversation to our list
  317. //
  318. ActiveConversationList.Add (active_conversation);
  319. }
  320. break;
  321. case CHUNKID_CONVERSATION_CATEGORY:
  322. {
  323. int category_id = 0;
  324. cload.Read (&category_id, sizeof (category_id));
  325. //
  326. // Remove all conversations in this category
  327. //
  328. Reset_Conversations (category_id);
  329. //
  330. // Load all the conversations in this category
  331. //
  332. Load_Conversations (cload, category_id);
  333. }
  334. break;
  335. case CHUNKID_OLD_CONVERSATION:
  336. {
  337. //
  338. // Reset the list of level conversations if this
  339. // the first one loaded
  340. //
  341. if (old_style_conv_count == 0) {
  342. Reset_Conversations (CATEGORY_LEVEL);
  343. }
  344. old_style_conv_count ++;
  345. //
  346. // Create a new object and its state from the chunk
  347. //
  348. ConversationClass *conversation = new ConversationClass;
  349. conversation->Load (cload);
  350. conversation->Set_Category_ID (CATEGORY_LEVEL);
  351. //
  352. // Add this conversation to our list
  353. //
  354. Add_Conversation (conversation);
  355. REF_PTR_RELEASE (conversation);
  356. }
  357. break;
  358. case CHUNKID_VARIABLES:
  359. Load_Variables (cload);
  360. break;
  361. }
  362. cload.Close_Chunk ();
  363. }
  364. return true;
  365. }
  366. ///////////////////////////////////////////////////////////////////////
  367. //
  368. // Load_Variables
  369. //
  370. ///////////////////////////////////////////////////////////////////////
  371. void
  372. ConversationMgrClass::Load_Variables (ChunkLoadClass &cload)
  373. {
  374. //
  375. // Loop through all the microchunks that define the variables
  376. //
  377. while (cload.Open_Micro_Chunk ()) {
  378. switch (cload.Cur_Micro_Chunk_ID ()) {
  379. READ_MICRO_CHUNK (cload, VARID_NEXT_ACTIVE_CONVERSATION_ID, NextActiveConversationID);
  380. READ_MICRO_CHUNK (cload, VARID_NEXT_GLOBAL_CONVERSATION_ID, NextGlobalConversationID);
  381. READ_MICRO_CHUNK (cload, VARID_NEXT_LEVEL_CONVERSATION_ID, NextLevelConversationID);
  382. }
  383. cload.Close_Micro_Chunk ();
  384. }
  385. return ;
  386. }
  387. ////////////////////////////////////////////////////////////////
  388. //
  389. // Build_Buddy_List
  390. //
  391. ////////////////////////////////////////////////////////////////
  392. void
  393. ConversationMgrClass::Build_Buddy_List
  394. (
  395. PhysicalGameObj * orator,
  396. DynamicVectorClass<PhysicalGameObj *> & buddy_list,
  397. bool include_orator
  398. )
  399. {
  400. //
  401. // Build a box, centered around the orator, that we can use to
  402. // collect other 'friendly' orators in the area
  403. //
  404. Vector3 orator_pos (0, 0, 0);
  405. orator->Get_Position (&orator_pos);
  406. orator_pos.Z += BUDDY_LOCATE_CZ / 3.0F;
  407. AABoxClass box (orator_pos, Vector3 (BUDDY_LOCATE_CX, BUDDY_LOCATE_CY, BUDDY_LOCATE_CZ));
  408. //
  409. // Collect all the dynamic objects in this box
  410. //
  411. NonRefPhysListClass obj_list;
  412. PhysicsSceneClass::Get_Instance ()->Collect_Objects (box, false, true, &obj_list);
  413. //
  414. // Loop over all the collected objects
  415. //
  416. NonRefPhysListIterator it (&obj_list);
  417. for (it.First(); !it.Is_Done(); it.Next()) {
  418. PhysClass *phys_obj = it.Peek_Obj ();
  419. //
  420. // Check to ensure this game object is a orator
  421. //
  422. CombatPhysObserverClass *phys_observer = reinterpret_cast<CombatPhysObserverClass *>(phys_obj->Get_Observer ());
  423. if (phys_observer != NULL) {
  424. PhysicalGameObj *game_obj = phys_observer->As_PhysicalGameObj();
  425. if (game_obj != NULL) {
  426. //
  427. // Check to make sure we found a orator who can participate
  428. // in this conversation
  429. //
  430. PhysicalGameObj *other_orator = game_obj->As_PhysicalGameObj ();
  431. if (other_orator != NULL && (include_orator || other_orator != orator)) {
  432. //
  433. // Can this orator hold a conversation?
  434. //
  435. if ( other_orator->Is_In_Conversation () == false &&
  436. other_orator->Are_Innate_Conversations_Enabled ())
  437. {
  438. buddy_list.Add (other_orator);
  439. }
  440. }
  441. }
  442. }
  443. }
  444. return ;
  445. }
  446. ////////////////////////////////////////////////////////////////
  447. //
  448. // Find_Active_Conversation
  449. //
  450. ////////////////////////////////////////////////////////////////
  451. ActiveConversationClass *
  452. ConversationMgrClass::Find_Active_Conversation (int id)
  453. {
  454. ActiveConversationClass *conversation = NULL;
  455. //
  456. // Loop over each entry in the active conversation list, looking for one with
  457. // the supplied identifier.
  458. //
  459. for (int index = 0; index < ActiveConversationList.Count (); index ++) {
  460. ActiveConversationClass *curr_conversation = ActiveConversationList[index];
  461. //
  462. // Is this the conversation we were looking for?
  463. //
  464. if (curr_conversation != NULL && curr_conversation->Get_ID () == id) {
  465. conversation = curr_conversation;
  466. conversation->Add_Ref ();
  467. break;
  468. }
  469. }
  470. return conversation;
  471. }
  472. ////////////////////////////////////////////////////////////////
  473. //
  474. // Find_Conversation
  475. //
  476. ////////////////////////////////////////////////////////////////
  477. ConversationClass *
  478. ConversationMgrClass::Find_Conversation (const char *conversation_name)
  479. {
  480. ConversationClass *conversation = NULL;
  481. //
  482. // Loop over all the conversation categories
  483. //
  484. for (int cat_index = 0; cat_index < CATEGORY_MAX; cat_index ++) {
  485. //
  486. // Loop over each conversation in this category, looking for one with
  487. // the requested name.
  488. //
  489. int count = ConversationList[cat_index].Count ();
  490. for (int index = 0; index < count; index ++) {
  491. ConversationClass *curr_conversation = ConversationList[cat_index][index];
  492. //
  493. // Is this the conversation we were looking for?
  494. //
  495. if ( curr_conversation != NULL &&
  496. ::strcmpi (curr_conversation->Get_Name (), conversation_name) == 0)
  497. {
  498. conversation = curr_conversation;
  499. conversation->Add_Ref ();
  500. break;
  501. }
  502. }
  503. }
  504. return conversation;
  505. }
  506. ////////////////////////////////////////////////////////////////
  507. //
  508. // Find_Conversation
  509. //
  510. ////////////////////////////////////////////////////////////////
  511. ConversationClass *
  512. ConversationMgrClass::Find_Conversation (int conversation_id)
  513. {
  514. ConversationClass *conversation = NULL;
  515. //
  516. // Loop over all the conversation categories
  517. //
  518. for (int cat_index = 0; cat_index < CATEGORY_MAX; cat_index ++) {
  519. //
  520. // Loop over each conversation in this category, looking for one with
  521. // the requested name.
  522. //
  523. int count = ConversationList[cat_index].Count ();
  524. for (int index = 0; index < count; index ++) {
  525. ConversationClass *curr_conversation = ConversationList[cat_index][index];
  526. //
  527. // Is this the conversation we were looking for?
  528. //
  529. if ( (curr_conversation != NULL) &&
  530. (curr_conversation->Get_ID () == conversation_id))
  531. {
  532. conversation = curr_conversation;
  533. conversation->Add_Ref ();
  534. break;
  535. }
  536. }
  537. }
  538. return conversation;
  539. }
  540. ////////////////////////////////////////////////////////////////
  541. //
  542. // Start_Conversation
  543. //
  544. ////////////////////////////////////////////////////////////////
  545. ActiveConversationClass *
  546. ConversationMgrClass::Start_Conversation (PhysicalGameObj *orator, int conversation_id, bool force)
  547. {
  548. ActiveConversationClass *active_conversation = NULL;
  549. //
  550. // Try to find the requested conversation
  551. //
  552. ConversationClass *conversation = Find_Conversation (conversation_id);
  553. if (conversation != NULL) {
  554. //
  555. // Start the conversation
  556. //
  557. active_conversation = Start_Conversation (orator, conversation, force);
  558. REF_PTR_RELEASE (conversation);
  559. }
  560. //
  561. // Return a pointer to the conversation. Note: This object has
  562. // an extra ref count on it, its the caller's responsibility to release
  563. // this ref count when they are finished with it.
  564. //
  565. return active_conversation;
  566. }
  567. ////////////////////////////////////////////////////////////////
  568. //
  569. // Start_Conversation
  570. //
  571. ////////////////////////////////////////////////////////////////
  572. ActiveConversationClass *
  573. ConversationMgrClass::Start_Conversation (PhysicalGameObj *orator, const char *conversation_name, bool force)
  574. {
  575. ActiveConversationClass *active_conversation = NULL;
  576. //
  577. // Try to find the requested conversation
  578. //
  579. ConversationClass *conversation = Find_Conversation (conversation_name);
  580. if (conversation != NULL) {
  581. //
  582. // Start the conversation
  583. //
  584. active_conversation = Start_Conversation (orator, conversation, force);
  585. REF_PTR_RELEASE (conversation);
  586. }
  587. //
  588. // Return a pointer to the conversation. Note: This object has
  589. // an extra ref count on it, its the caller's responsibility to release
  590. // this ref count when they are finished with it.
  591. //
  592. return active_conversation;
  593. }
  594. ////////////////////////////////////////////////////////////////
  595. //
  596. // Start_Conversation
  597. //
  598. ////////////////////////////////////////////////////////////////
  599. ActiveConversationClass *
  600. ConversationMgrClass::Start_Conversation (PhysicalGameObj *orator, ConversationClass *conversation, bool force)
  601. {
  602. //
  603. // Build a list of potential participants in the conversation
  604. //
  605. DynamicVectorClass<PhysicalGameObj *> buddy_list;
  606. Build_Buddy_List (orator, buddy_list, false);
  607. //
  608. // If we have enough people to hold this conversation, then activate
  609. // the new conversation.
  610. //
  611. ActiveConversationClass *active_conversation = NULL;
  612. if (Test_Conversation (orator, conversation, buddy_list, force)) {
  613. active_conversation = Create_New_Conversation (conversation, buddy_list);
  614. }
  615. //
  616. // Return a pointer to the conversation. Note: This object has
  617. // an extra ref count on it, its the caller's responsibility to release
  618. // this ref count when they are finished with it.
  619. //
  620. return active_conversation;
  621. }
  622. ////////////////////////////////////////////////////////////////
  623. //
  624. // Create_New_Conversation
  625. //
  626. ////////////////////////////////////////////////////////////////
  627. ActiveConversationClass *
  628. ConversationMgrClass::Create_New_Conversation
  629. (
  630. ConversationClass * conversation,
  631. DynamicVectorClass<PhysicalGameObj *> & buddy_list
  632. )
  633. {
  634. ActiveConversationClass *active_conversation = new ActiveConversationClass;
  635. active_conversation->Set_Conversation (conversation);
  636. //
  637. // Add the participants to the active conversation.
  638. // Note: Its assumed that the buddy_list is in the correct
  639. // order for this conversation.
  640. //
  641. int count = conversation->Get_Orator_Count ();
  642. for (int index = 0; index < count; index ++) {
  643. active_conversation->Add_Orator (buddy_list[index]);
  644. }
  645. //
  646. // Add it to our active conversation list
  647. //
  648. active_conversation->Set_ID (NextActiveConversationID ++);
  649. ActiveConversationList.Add (active_conversation);
  650. //
  651. // Initialize the conversation
  652. //
  653. active_conversation->Start_Conversation ();
  654. //
  655. // Increment the reference count on the conversation and
  656. // return it to the caller.
  657. //
  658. active_conversation->Add_Ref ();
  659. return active_conversation;
  660. }
  661. ////////////////////////////////////////////////////////////////
  662. //
  663. // Start_Conversation
  664. //
  665. ////////////////////////////////////////////////////////////////
  666. void
  667. ConversationMgrClass::Start_Conversation (PhysicalGameObj *orator)
  668. {
  669. //
  670. // Build a list of potential participants in the conversation
  671. //
  672. DynamicVectorClass<PhysicalGameObj *> available_buddy_list;
  673. Build_Buddy_List (orator, available_buddy_list, false);
  674. //
  675. // Try to find a conversation that this list of orators can have
  676. //
  677. DynamicVectorClass<PhysicalGameObj *> orator_list;
  678. ConversationClass *conversation = Pick_Conversation (orator, available_buddy_list, orator_list);
  679. if (conversation != NULL) {
  680. ActiveConversationClass *active_conversation = Create_New_Conversation (conversation, orator_list);
  681. REF_PTR_RELEASE (active_conversation);
  682. }
  683. return ;
  684. }
  685. ////////////////////////////////////////////////////////////////
  686. //
  687. // Test_Conversation
  688. //
  689. ////////////////////////////////////////////////////////////////
  690. bool
  691. ConversationMgrClass::Test_Conversation
  692. (
  693. PhysicalGameObj * initiator,
  694. ConversationClass * conversation,
  695. DynamicVectorClass<PhysicalGameObj *> & buddy_list,
  696. bool force
  697. )
  698. {
  699. int orator_count = conversation->Get_Orator_Count ();
  700. bool retval = (orator_count > 0);
  701. bool initiator_included = false;
  702. DynamicVectorClass<PhysicalGameObj *> available_buddy_list = buddy_list;
  703. buddy_list.Reset_Active ();
  704. //
  705. // Loop over all the orator requirements for this conversation and
  706. // see if they are in the buddy list
  707. //
  708. for (int orator_index = 0; orator_index < orator_count; orator_index ++) {
  709. OratorClass *orator = conversation->Get_Orator (orator_index);
  710. //
  711. // If the orator is invisible, it automatically passes the test (we
  712. // don't need a physical object to take the part).
  713. //
  714. bool found = false;
  715. if (orator->Is_Invisible ()) {
  716. buddy_list.Add (NULL);
  717. found = true;
  718. } else {
  719. //
  720. // Test the initiator
  721. //
  722. if (initiator_included == false) {
  723. if (force || Test_Orator (conversation, orator, initiator)) {
  724. buddy_list.Add (initiator);
  725. initiator_included = true;
  726. found = true;
  727. }
  728. }
  729. //
  730. // Try to find an entry in our buddy list that matches the orator
  731. // requirements
  732. //
  733. for (int index = 0; index < available_buddy_list.Count (); index ++) {
  734. PhysicalGameObj *game_obj = available_buddy_list[index];
  735. //
  736. // Does this game object fit the orator description?
  737. //
  738. if (Test_Orator (conversation, orator, game_obj)) {
  739. buddy_list.Add (game_obj);
  740. //
  741. // Remove this orator from the buddy list
  742. //
  743. available_buddy_list.Delete (index);
  744. found = true;
  745. break;
  746. }
  747. }
  748. }
  749. //
  750. // If we can't match up even ONE of the orators, then the
  751. // conversation can't be executed
  752. //
  753. if (found == false) {
  754. retval = false;
  755. break;
  756. }
  757. }
  758. //
  759. // We can't pass the conversation if the initiator isn't
  760. // included in the conversation.
  761. //
  762. if (initiator_included == false) {
  763. retval = false;
  764. }
  765. return retval;
  766. }
  767. ////////////////////////////////////////////////////////////////
  768. //
  769. // Test_Orator
  770. //
  771. ////////////////////////////////////////////////////////////////
  772. bool
  773. ConversationMgrClass::Test_Orator
  774. (
  775. ConversationClass * conversation,
  776. OratorClass * orator,
  777. PhysicalGameObj * game_obj
  778. )
  779. {
  780. bool retval = false;
  781. //
  782. // Check to ensure the orator types match
  783. //
  784. if (orator->Get_Orator_Type () == game_obj->Get_Definition ().Get_Orator_Type ()) {
  785. //
  786. // Check to see (if its a soldier) if this orator is in the correct state
  787. //
  788. SoldierGameObj *soldier = game_obj->As_SoldierGameObj ();
  789. if ( soldier == NULL ||
  790. soldier->Is_Human_Controlled () ||
  791. soldier->Get_AI_State () == conversation->Get_AI_State ())
  792. {
  793. retval = true;
  794. }
  795. }
  796. return retval;
  797. }
  798. ////////////////////////////////////////////////////////////////
  799. //
  800. // Pick_Conversation
  801. //
  802. ////////////////////////////////////////////////////////////////
  803. ConversationClass *
  804. ConversationMgrClass::Pick_Conversation
  805. (
  806. PhysicalGameObj * initiator,
  807. const DynamicVectorClass<PhysicalGameObj *> & available_orator_list,
  808. DynamicVectorClass<PhysicalGameObj *> & orator_list
  809. )
  810. {
  811. ConversationClass *conversation = NULL;
  812. //
  813. // Try to find a conversation that matches the criteria
  814. //
  815. float best_match = 0;
  816. //
  817. // Loop over all the conversation categories
  818. //
  819. for (int cat_index = 0; cat_index < CATEGORY_MAX; cat_index ++) {
  820. //
  821. // Loop over all the conversations in this category
  822. //
  823. int count = ConversationList[cat_index].Count ();
  824. for (int index = 0; index < count; index ++) {
  825. ConversationClass *curr_conversation = ConversationList[cat_index][index];
  826. //
  827. // Can this conversation be used innately?
  828. //
  829. if ( curr_conversation != NULL &&
  830. curr_conversation->Is_Innate ())
  831. {
  832. //
  833. // Test this conversation to ensure all requirements are met
  834. //
  835. DynamicVectorClass<PhysicalGameObj *> curr_orator_list = available_orator_list;
  836. if (Test_Conversation (initiator, curr_conversation, curr_orator_list)) {
  837. //
  838. // Randomize the selection process
  839. //
  840. float match_percent = WWMath::Random_Float (0.9F, 1.1F);
  841. match_percent *= curr_conversation->Get_Probability ();
  842. if (match_percent > best_match) {
  843. //
  844. // This is our best bet so far, so save this conversation
  845. //
  846. best_match = match_percent;
  847. conversation = curr_conversation;
  848. orator_list = curr_orator_list;
  849. }
  850. }
  851. }
  852. }
  853. }
  854. //
  855. // If we've found a conversation, decrease its probability so
  856. // it will be less likely to be picked next time.
  857. //
  858. if (conversation != NULL) {
  859. float probability = conversation->Get_Probability ();
  860. probability = probability * 0.75F;
  861. conversation->Set_Probability (probability);
  862. }
  863. return conversation;
  864. }
  865. ////////////////////////////////////////////////////////////////
  866. //
  867. // Is_Key_Conversation_Playing
  868. //
  869. ////////////////////////////////////////////////////////////////
  870. bool
  871. ConversationMgrClass::Is_Key_Conversation_Playing (void)
  872. {
  873. bool retval = false;
  874. //
  875. // Check for any "key" conversations
  876. //
  877. int index = ActiveConversationList.Count ();
  878. while (index --) {
  879. ActiveConversationClass *active_conversation = ActiveConversationList[index];
  880. //
  881. // Is this a "key" conversation?
  882. //
  883. if (active_conversation->Peek_Conversation ()->Is_Key ()) {
  884. retval = true;
  885. break;
  886. }
  887. }
  888. return retval;
  889. }
  890. ////////////////////////////////////////////////////////////////
  891. //
  892. // Think
  893. //
  894. ////////////////////////////////////////////////////////////////
  895. void
  896. ConversationMgrClass::Think (void)
  897. {
  898. bool is_key = Is_Key_Conversation_Playing ();
  899. //
  900. // Stop any non-key conversations that are playing (if there is a key conversation playing)
  901. //
  902. if (is_key) {
  903. bool release_key_conv = false;
  904. int index = ActiveConversationList.Count ();
  905. while (index --) {
  906. ActiveConversationClass *active_conversation = ActiveConversationList[index];
  907. if (active_conversation->Peek_Conversation ()->Is_Key () == false || release_key_conv) {
  908. //
  909. // Stop any non-key conversation
  910. //
  911. active_conversation->Stop_Conversation ();
  912. //
  913. // Remove the conversation from the list
  914. //
  915. ActiveConversationList.Delete (index);
  916. REF_PTR_RELEASE (active_conversation);
  917. } else {
  918. release_key_conv = true;
  919. }
  920. }
  921. }
  922. //
  923. // Loop over all the remaining active conversations
  924. //
  925. int count = ActiveConversationList.Count ();
  926. for (int index = 0; index < count; index ++) {
  927. ActiveConversationClass *active_conversation = ActiveConversationList[index];
  928. //
  929. // Let this conversation process
  930. //
  931. bool remove_from_list = true;
  932. if (active_conversation != NULL) {
  933. active_conversation->Think ();
  934. remove_from_list = active_conversation->Is_Finished ();
  935. }
  936. //
  937. // Remove this conversation from our control (if necessary)
  938. //
  939. if (remove_from_list) {
  940. ActiveConversationList.Delete (index);
  941. REF_PTR_RELEASE (active_conversation);
  942. index --;
  943. count --;
  944. }
  945. }
  946. return ;
  947. }
  948. ////////////////////////////////////////////////////////////////
  949. //
  950. // Add_Conversation
  951. //
  952. ////////////////////////////////////////////////////////////////
  953. void
  954. ConversationMgrClass::Add_Conversation (ConversationClass *conversation)
  955. {
  956. WWASSERT (conversation != NULL);
  957. if (conversation == NULL) {
  958. return ;
  959. }
  960. //
  961. // Assign this conversation an ID (if necessary)
  962. //
  963. if (conversation->Get_ID () == 0) {
  964. if (conversation->Get_Category_ID () == CATEGORY_LEVEL) {
  965. conversation->Set_ID (NextLevelConversationID ++);
  966. } else {
  967. conversation->Set_ID (NextGlobalConversationID ++);
  968. }
  969. }
  970. //
  971. // Add a reference to the converation, then add it to our list
  972. //
  973. conversation->Add_Ref ();
  974. ConversationList[conversation->Get_Category_ID ()].Add (conversation);
  975. return ;
  976. }
  977. ////////////////////////////////////////////////////////////////
  978. //
  979. // Create_New_Conversation
  980. //
  981. ////////////////////////////////////////////////////////////////
  982. ActiveConversationClass *
  983. ConversationMgrClass::Create_New_Conversation (ConversationClass *conversation)
  984. {
  985. ActiveConversationClass *active_conversation = NULL;
  986. if (conversation != NULL) {
  987. //
  988. // Allocate the new conversation
  989. //
  990. active_conversation = new ActiveConversationClass;
  991. active_conversation->Set_Conversation (conversation);
  992. //
  993. // Add it to our active conversation list
  994. //
  995. active_conversation->Set_ID (NextActiveConversationID ++);
  996. ActiveConversationList.Add (active_conversation);
  997. active_conversation->Add_Ref ();
  998. }
  999. //
  1000. // Return a pointer to the conversation. Note: This object has
  1001. // an extra ref count on it, its the caller's responsibility to release
  1002. // this ref count when they are finished with it.
  1003. //
  1004. return active_conversation;
  1005. }
  1006. ////////////////////////////////////////////////////////////////
  1007. //
  1008. // Remove_Conversation
  1009. //
  1010. ////////////////////////////////////////////////////////////////
  1011. void
  1012. ConversationMgrClass::Remove_Conversation (ConversationClass *conversation)
  1013. {
  1014. int cat_index = conversation->Get_Category_ID ();
  1015. int count = ConversationList[cat_index].Count ();
  1016. //
  1017. // Loop over all the conversations in this category
  1018. //
  1019. for (int index = 0; index < count; index ++) {
  1020. ConversationClass *curr_conversation = ConversationList[cat_index][index];
  1021. if (conversation == curr_conversation) {
  1022. //
  1023. // Remove this conversation from the list
  1024. //
  1025. ConversationList[cat_index].Delete (index);
  1026. conversation->Set_ID (0);
  1027. REF_PTR_RELEASE (conversation);
  1028. break;
  1029. }
  1030. }
  1031. return ;
  1032. }