activeconversation.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318
  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/activeconversation.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 1/16/02 6:01p $*
  29. * *
  30. * $Revision:: 31 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "activeconversation.h"
  36. #include "soldier.h"
  37. #include "chunkio.h"
  38. #include "actionparams.h"
  39. #include "timemgr.h"
  40. #include "orator.h"
  41. #include "pathfind.h"
  42. #include "globalsettings.h"
  43. #include "soldierobserver.h"
  44. #include "gameobjmanager.h"
  45. #include "conversationmgr.h"
  46. #include "debug.h"
  47. #include "wwaudio.h"
  48. #include "audiblesound.h"
  49. #include "translatedb.h"
  50. #include "translateobj.h"
  51. #include "combat.h"
  52. ////////////////////////////////////////////////////////////////
  53. // Constants
  54. ////////////////////////////////////////////////////////////////
  55. enum
  56. {
  57. CHUNKID_VARIABLES = 0x08090727,
  58. CHUNKID_ORATOR,
  59. CHUNKID_MONITOR
  60. };
  61. enum
  62. {
  63. XXXXX_CONVERSATION_PTR = 0,
  64. VARID_CURRENT_REMARK,
  65. XXXXX_NEXT_REMARK_TIME,
  66. VARID_STATE,
  67. VARID_OLD_PTR,
  68. VARID_ID,
  69. VARID_INITIALIZING_TIMER,
  70. VARID_ACTION_ID,
  71. VARID_SPOKEN_BITMASK,
  72. VARID_CENTRAL_POS,
  73. VARID_PRIORITY,
  74. VARID_MAXDIST,
  75. VARID_IS_INTERRUPTABLE,
  76. VARID_CONVERSATION_ID,
  77. VARID_NEXT_REMARK_TIMER,
  78. };
  79. static const float RAND_STAND_DIST = 2.0F;
  80. static const float MIN_AUDIENCE_DIST = 4.0F;
  81. ////////////////////////////////////////////////////////////////
  82. //
  83. // ActiveConversationClass
  84. //
  85. ////////////////////////////////////////////////////////////////
  86. ActiveConversationClass::ActiveConversationClass (void) :
  87. ID (0),
  88. Conversation (NULL),
  89. CurrentRemark (-1),
  90. NextRemarkTimer (0),
  91. State (STATE_INITIALIZING),
  92. InitializingTimeLeft (60000),
  93. ActionID (0),
  94. OratorSpokenBitmask (0),
  95. CentralPos (0, 0, 0),
  96. Priority (30),
  97. MaxDist (0),
  98. IsInterruptable (true),
  99. CurrentSound (NULL)
  100. {
  101. return ;
  102. }
  103. ////////////////////////////////////////////////////////////////
  104. //
  105. // ~ActiveConversationClass
  106. //
  107. ////////////////////////////////////////////////////////////////
  108. ActiveConversationClass::~ActiveConversationClass (void)
  109. {
  110. Stop_Current_Sound ();
  111. Free_Orator_List ();
  112. REF_PTR_RELEASE (Conversation);
  113. return ;
  114. }
  115. ////////////////////////////////////////////////////////////////
  116. //
  117. // Free_Orator_List
  118. //
  119. ////////////////////////////////////////////////////////////////
  120. void
  121. ActiveConversationClass::Free_Orator_List (void)
  122. {
  123. //
  124. // Free each orator in our list
  125. //
  126. for (int index = 0; index < OratorList.Count (); index ++) {
  127. OratorClass *orator = OratorList[index];
  128. if (orator != NULL) {
  129. PhysicalGameObj *game_obj = orator->Get_Game_Obj ();
  130. if (game_obj != NULL) {
  131. //
  132. // Was this a soldier we were talking to?
  133. //
  134. SoldierGameObj *soldier = game_obj->As_SoldierGameObj ();
  135. if (soldier != NULL) {
  136. //
  137. // Turn idle animations back on for soldiers
  138. //
  139. soldier->As_SoldierGameObj ()->Set_Loiters_Allowed (true);
  140. //
  141. // Make sure the soldier doesn't ramble on after the
  142. // conversation has ended.
  143. //
  144. soldier->Stop_Current_Speech ();
  145. //
  146. // Reset the possibility that this soldier will have a conversation
  147. //
  148. SoldierObserverClass *innate_ai = soldier->Get_Innate_Controller ();
  149. if (innate_ai != NULL) {
  150. innate_ai->Reset_Conversation_Timer ();
  151. }
  152. //
  153. // If we were controlling the soldier's head, return
  154. // it to its default position
  155. //
  156. if (orator->Get_Flag (OratorClass::FLAG_DONT_TURN_HEAD) == false) {
  157. soldier->Cancel_Look_At ();
  158. }
  159. }
  160. //
  161. // Let the game obj know its no longer in a conversation
  162. //
  163. game_obj->Set_Conversation (NULL);
  164. }
  165. delete orator;
  166. orator = NULL;
  167. }
  168. }
  169. OratorList.Delete_All ();
  170. return ;
  171. }
  172. ////////////////////////////////////////////////////////////////
  173. //
  174. // Set_Conversation
  175. //
  176. ////////////////////////////////////////////////////////////////
  177. void
  178. ActiveConversationClass::Set_Conversation (ConversationClass *conversation)
  179. {
  180. REF_PTR_SET (Conversation, conversation);
  181. CurrentRemark = -1;
  182. NextRemarkTimer = 0;
  183. OratorSpokenBitmask = 0;
  184. Priority = conversation->Get_Priority ();
  185. MaxDist = conversation->Get_Max_Dist ();
  186. IsInterruptable = conversation->Is_Interruptable ();
  187. Free_Orator_List ();
  188. return ;
  189. }
  190. ////////////////////////////////////////////////////////////////
  191. //
  192. // Add_Orator
  193. //
  194. ////////////////////////////////////////////////////////////////
  195. OratorClass *
  196. ActiveConversationClass::Add_Orator (PhysicalGameObj *game_obj)
  197. {
  198. //
  199. // Let the game obj know he's part of a conversation
  200. //
  201. if (game_obj != NULL) {
  202. game_obj->Set_Conversation (this);
  203. }
  204. //
  205. // Allocate and initialize a new orator
  206. //
  207. OratorClass *orator = new OratorClass;
  208. orator->Initialize (game_obj);
  209. orator->Set_ID (OratorList.Count ());
  210. //
  211. // Add the new orator to our list
  212. //
  213. OratorList.Add (orator);
  214. //
  215. // If orators are still being added to the conversation, the
  216. // the object is considered initializing.
  217. //
  218. State = STATE_INITIALIZING;
  219. return orator;
  220. }
  221. ////////////////////////////////////////////////////////////////
  222. //
  223. // Start_Conversation
  224. //
  225. ////////////////////////////////////////////////////////////////
  226. void
  227. ActiveConversationClass::Start_Conversation (void)
  228. {
  229. WWASSERT (OratorList.Count () > 0);
  230. OratorSpokenBitmask = 0;
  231. //
  232. // Find the first "visible" orator in our list
  233. //
  234. PhysicalGameObj *main_speaker = NULL;
  235. for ( int orator_index = 0;
  236. main_speaker == NULL && orator_index < OratorList.Count ();
  237. orator_index ++)
  238. {
  239. main_speaker = OratorList[orator_index]->Get_Game_Obj ();
  240. }
  241. //
  242. // Get the location of the first orator
  243. //
  244. if (main_speaker != NULL) {
  245. //
  246. // Pick a center position somewhere around the first speaker
  247. //
  248. main_speaker->Get_Position (&CentralPos);
  249. //
  250. // Find a safe central position for the conversation
  251. //
  252. PathfindClass *pathfind = PathfindClass::Get_Instance();
  253. if (pathfind->Find_Random_Spot( CentralPos, 2, &CentralPos)) {
  254. for (int index = 0; index < OratorList.Count (); index ++) {
  255. //
  256. // Don't allow this unit to face the speaker by default. (This
  257. // flag will be cleared by the ActionCodeClass if its priority is high enough)
  258. //
  259. OratorList[index]->Set_Flag (OratorClass::FLAG_TEMP_DONT_FACE, true);
  260. //
  261. // If this soldier is allowed to move, then calculate a new position for them
  262. //
  263. if (OratorList[index]->Get_Flag (OratorClass::FLAG_DONT_MOVE) == false) {
  264. //
  265. // Lookup a safe random position to stand
  266. //
  267. Vector3 new_location;
  268. if (pathfind->Find_Random_Spot (CentralPos, 2, &new_location)) {
  269. //
  270. // Store this position in our list so the orator can query
  271. // for his position later.
  272. //
  273. OratorList[index]->Set_Position (new_location);
  274. }
  275. }
  276. }
  277. }
  278. //
  279. // Now, let each orator know its part of a conversation
  280. //
  281. for (int index = 0; index < OratorList.Count (); index ++) {
  282. PhysicalGameObj *game_obj = OratorList[index]->Get_Game_Obj ();
  283. if (game_obj != NULL) {
  284. //
  285. // Turn off idle animations for soldiers when they are
  286. // having a conversation
  287. //
  288. if (game_obj->As_SoldierGameObj () != NULL) {
  289. game_obj->As_SoldierGameObj ()->Set_Loiters_Allowed (false);
  290. }
  291. //
  292. // Make sure we don't take control of a human player
  293. //
  294. if ( game_obj->As_SmartGameObj () == NULL ||
  295. game_obj->As_SmartGameObj ()->Is_Human_Controlled () ||
  296. OratorList.Count () <= 2)
  297. {
  298. OratorList[index]->Set_Flag (OratorClass::FLAG_DONT_MOVE, true);
  299. OratorList[index]->Set_Flag (OratorClass::FLAG_TEMP_DONT_FACE, false);
  300. //
  301. // Don't force facing if its a human player or inanimate object
  302. //
  303. if ( game_obj->As_SmartGameObj () == NULL ||
  304. game_obj->As_SmartGameObj ()->Is_Human_Controlled ())
  305. {
  306. OratorList[index]->Set_Flag (OratorClass::FLAG_DONT_FACE, true);
  307. }
  308. //
  309. // Since this object doesn't have to move -- reset its position
  310. //
  311. Vector3 position;
  312. game_obj->Get_Position (&position);
  313. OratorList[index]->Set_Position (position);
  314. } else {
  315. ActionParamsStruct parameters;
  316. parameters.Priority = Priority;
  317. parameters.ActionID = ActionID;
  318. parameters.ObserverID = 0;
  319. parameters.Join_Conversation (ID);
  320. game_obj->As_SmartGameObj ()->Get_Action ()->Have_Conversation (parameters);
  321. }
  322. }
  323. }
  324. }
  325. //
  326. // Begin waiting for the participants to move to their location
  327. //
  328. State = STATE_WAITING_FOR_AUDIENCE;
  329. //
  330. // Don't allow a key conversation to be interrupted
  331. //
  332. if (Conversation != NULL && Conversation->Is_Key ()) {
  333. Set_Is_Interruptable (false);
  334. //
  335. // Clear all other conversations from the manager
  336. //
  337. //ConversationMgrClass::Reset_All_Other_Conversations (this);
  338. } else {
  339. //
  340. // Stop this conversation before it can start if there is
  341. // a key conversation playing
  342. //
  343. if (ConversationMgrClass::Is_Key_Conversation_Playing ()) {
  344. Stop_Conversation (ACTION_COMPLETE_CONVERSATION_INTERRUPTED);
  345. }
  346. }
  347. return ;
  348. }
  349. ////////////////////////////////////////////////////////////////
  350. //
  351. // Think
  352. //
  353. ////////////////////////////////////////////////////////////////
  354. void
  355. ActiveConversationClass::Think (void)
  356. {
  357. //
  358. // Make each orator do something meaningful
  359. //
  360. for (int index = 0; index < OratorList.Count (); index ++) {
  361. PhysicalGameObj *game_obj = OratorList[index]->Get_Game_Obj ();
  362. if (game_obj != NULL && game_obj->As_SoldierGameObj () != NULL) {
  363. SoldierGameObj *soldier = game_obj->As_SoldierGameObj ();
  364. //
  365. // Stop the conversation if the orator is dead, otherwise
  366. // take control of some of his movements
  367. //
  368. if (soldier->Is_Dead () || soldier->Is_Destroyed ()) {
  369. Stop_Conversation (ACTION_COMPLETE_CONVERSATION_INTERRUPTED);
  370. } else {
  371. Control_Orator (soldier);
  372. }
  373. }
  374. }
  375. if (State == STATE_WAITING_FOR_AUDIENCE) {
  376. Check_For_Audience ();
  377. } else if (State == STATE_TALKING) {
  378. //
  379. // Should we advance to the next remark?
  380. //
  381. NextRemarkTimer -= TimeManager::Get_Frame_Seconds ();
  382. if (NextRemarkTimer <= 0) {
  383. //
  384. // If everyone is listening, then move on to the next remark,
  385. // otherwise kick out of the conversation
  386. //
  387. if (Is_Audience_In_Place () == false) {
  388. Stop_Conversation (ACTION_COMPLETE_CONVERSATION_INTERRUPTED);
  389. } else {
  390. Say_Next_Remark ();
  391. }
  392. }
  393. } else if (State == STATE_INITIALIZING) {
  394. //
  395. // Scrap the conversation if too much time was spent initializing (probably an error).
  396. //
  397. InitializingTimeLeft -= TimeManager::Get_Frame_Seconds ();
  398. if (InitializingTimeLeft <= 0) {
  399. Stop_Conversation (ACTION_COMPLETE_CONVERSATION_UNABLE_TO_INIT);
  400. }
  401. }
  402. return ;
  403. }
  404. ////////////////////////////////////////////////////////////////
  405. //
  406. // Say_Next_Remark
  407. //
  408. ////////////////////////////////////////////////////////////////
  409. void
  410. ActiveConversationClass::Say_Next_Remark (void)
  411. {
  412. //
  413. // Move onto the next remark if possible
  414. //
  415. CurrentRemark ++;
  416. if (CurrentRemark < Conversation->Get_Remark_Count ()) {
  417. //
  418. // Notify the monitors of the event
  419. //
  420. if (CurrentRemark > 0) {
  421. Notify_Monitors (CUSTOM_EVENT_CONVERSATION_REMARK_ENDED, CurrentRemark - 1);
  422. }
  423. Notify_Monitors (CUSTOM_EVENT_CONVERSATION_REMARK_STARTED, CurrentRemark);
  424. //
  425. // Lookup who says the next line
  426. //
  427. ConversationRemarkClass remark;
  428. Conversation->Get_Remark_Info (CurrentRemark, remark);
  429. int orator_id = remark.Get_Orator_ID ();
  430. int text_id = remark.Get_Text_ID ();
  431. //
  432. // Make sure the data is valid
  433. //
  434. WWASSERT_PRINT (orator_id >= 0 && orator_id < OratorList.Count (), Conversation->Get_Name ());
  435. if (orator_id >= 0 && orator_id < OratorList.Count ()) {
  436. PhysicalGameObj *orator = OratorList[orator_id]->Get_Game_Obj ();
  437. //
  438. // Set a bit which lets us know this orator has spoken at least once...
  439. //
  440. OratorSpokenBitmask |= (1 << orator_id);
  441. //
  442. // Have the orator start his remark
  443. //
  444. SoldierGameObj *soldier = NULL;
  445. if (orator != NULL) {
  446. soldier = orator->As_SoldierGameObj ();
  447. }
  448. float duration = 0;
  449. if (soldier != NULL) {
  450. duration = SoldierGameObj::Say_Dynamic_Dialogue (text_id, soldier);
  451. } else {
  452. REF_PTR_RELEASE (CurrentSound);
  453. duration = SoldierGameObj::Say_Dynamic_Dialogue (text_id, NULL, &CurrentSound);
  454. }
  455. //
  456. // Play an animation on the orator
  457. //
  458. if (orator != NULL && remark.Get_Animation_Name ().Get_Length () > 0) {
  459. orator->Set_Animation (remark.Get_Animation_Name (), false);
  460. }
  461. //
  462. // Determine when we should switch to the next remark
  463. //
  464. NextRemarkTimer = duration;
  465. }
  466. } else {
  467. Stop_Conversation (ACTION_COMPLETE_CONVERSATION_ENDED);
  468. }
  469. return ;
  470. }
  471. ////////////////////////////////////////////////////////////////
  472. //
  473. // Is_Audience_In_Place
  474. //
  475. ////////////////////////////////////////////////////////////////
  476. bool
  477. ActiveConversationClass::Is_Audience_In_Place (void)
  478. {
  479. bool retval = true;
  480. //
  481. // Determine how far away each participant can be before
  482. // we terminate the conversation
  483. //
  484. float max_dist = MaxDist;
  485. if (max_dist <= 0) {
  486. if (Conversation->Get_AI_State () == AI_STATE_COMBAT) {
  487. max_dist = GlobalSettingsDef::Get_Global_Settings ()->Get_Combat_Conversation_Dist ();
  488. } else {
  489. max_dist = GlobalSettingsDef::Get_Global_Settings ()->Get_Conversation_Dist ();
  490. }
  491. }
  492. //
  493. // Square the distance for faster comparisons
  494. //
  495. float max_dist2 = max_dist * max_dist;
  496. //
  497. // Check to see if everyone is close enough to have this conversation
  498. //
  499. for (int index = 0; index < OratorList.Count (); index ++) {
  500. PhysicalGameObj *game_obj = OratorList[index]->Get_Game_Obj ();
  501. if (game_obj != NULL) {
  502. //
  503. // Get this soldier's position
  504. //
  505. Vector3 position;
  506. game_obj->Get_Position (&position);
  507. //
  508. // Calculate how far from the conversation's center this
  509. // soldier is.
  510. //
  511. Vector3 delta = position - CentralPos;
  512. delta.Z = delta.Z * 0.5F;
  513. //
  514. // If this distance is greater then the maximum allowed, then
  515. // the audience is NOT is place
  516. //
  517. float distance2 = delta.Length2 ();
  518. if (distance2 > max_dist2) {
  519. retval = false;
  520. break;
  521. }
  522. }
  523. }
  524. return retval;
  525. }
  526. ////////////////////////////////////////////////////////////////
  527. //
  528. // Check_For_Audience
  529. //
  530. ////////////////////////////////////////////////////////////////
  531. void
  532. ActiveConversationClass::Check_For_Audience (void)
  533. {
  534. if (State == STATE_WAITING_FOR_AUDIENCE) {
  535. //
  536. // If everyone is close enough then start the conversation
  537. //
  538. if (Is_Audience_In_Place ()) {
  539. State = STATE_TALKING;
  540. Notify_Monitors (CUSTOM_EVENT_CONVERSATION_BEGAN, Conversation->Get_ID ());
  541. }
  542. }
  543. return ;
  544. }
  545. ////////////////////////////////////////////////////////////////
  546. //
  547. // Save
  548. //
  549. ////////////////////////////////////////////////////////////////
  550. bool
  551. ActiveConversationClass::Save (ChunkSaveClass &csave)
  552. {
  553. csave.Begin_Chunk (CHUNKID_VARIABLES);
  554. //
  555. // Save the ID of the conversation (if necessary)
  556. //
  557. if (Conversation != NULL) {
  558. int conversation_id = Conversation->Get_ID ();
  559. WRITE_MICRO_CHUNK (csave, VARID_CONVERSATION_ID, conversation_id);
  560. }
  561. WRITE_MICRO_CHUNK (csave, VARID_CURRENT_REMARK, CurrentRemark);
  562. WRITE_MICRO_CHUNK (csave, VARID_NEXT_REMARK_TIMER, NextRemarkTimer);
  563. WRITE_MICRO_CHUNK (csave, VARID_STATE, State);
  564. WRITE_MICRO_CHUNK (csave, VARID_ID, ID);
  565. WRITE_MICRO_CHUNK (csave, VARID_INITIALIZING_TIMER, InitializingTimeLeft);
  566. WRITE_MICRO_CHUNK (csave, VARID_ACTION_ID, ActionID);
  567. WRITE_MICRO_CHUNK (csave, VARID_SPOKEN_BITMASK, OratorSpokenBitmask);
  568. WRITE_MICRO_CHUNK (csave, VARID_CENTRAL_POS, CentralPos);
  569. WRITE_MICRO_CHUNK (csave, VARID_PRIORITY, Priority);
  570. WRITE_MICRO_CHUNK (csave, VARID_MAXDIST, MaxDist);
  571. WRITE_MICRO_CHUNK (csave, VARID_IS_INTERRUPTABLE, IsInterruptable);
  572. //
  573. // Save our current pointer so we can remap it on load
  574. //
  575. ActiveConversationClass *old_ptr = this;
  576. WRITE_MICRO_CHUNK (csave, VARID_OLD_PTR, old_ptr);
  577. csave.End_Chunk ();
  578. //
  579. // Save each of the monitors
  580. //
  581. for (int index = 0; index < MAX_MONITORS; index ++) {
  582. if (MonitorArray[index].Get_Ptr () != NULL) {
  583. csave.Begin_Chunk (CHUNKID_MONITOR);
  584. MonitorArray[index].Save (csave);
  585. csave.End_Chunk ();
  586. }
  587. }
  588. //
  589. // Save each of the orators
  590. //
  591. for (index = 0; index < OratorList.Count (); index ++) {
  592. csave.Begin_Chunk (CHUNKID_ORATOR);
  593. OratorList[index]->Save (csave);
  594. csave.End_Chunk ();
  595. }
  596. return true;
  597. }
  598. ////////////////////////////////////////////////////////////////
  599. //
  600. // Load
  601. //
  602. ////////////////////////////////////////////////////////////////
  603. bool
  604. ActiveConversationClass::Load (ChunkLoadClass &cload)
  605. {
  606. Free_Orator_List ();
  607. int monitor_index = 0;
  608. while (cload.Open_Chunk ()) {
  609. switch (cload.Cur_Chunk_ID ()) {
  610. case CHUNKID_VARIABLES:
  611. Load_Variables (cload);
  612. break;
  613. case CHUNKID_ORATOR:
  614. {
  615. //
  616. // Allocate and load a new orator object
  617. //
  618. OratorClass *orator = new OratorClass;
  619. orator->Load (cload);
  620. //
  621. // Add this new object to our list
  622. //
  623. OratorList.Add (orator);
  624. }
  625. break;
  626. case CHUNKID_MONITOR:
  627. {
  628. //
  629. // Load the monitor's reference from the chunk
  630. //
  631. MonitorArray[monitor_index++].Load (cload);
  632. }
  633. break;
  634. }
  635. cload.Close_Chunk ();
  636. }
  637. return true;
  638. }
  639. ///////////////////////////////////////////////////////////////////////
  640. //
  641. // Load_Variables
  642. //
  643. ///////////////////////////////////////////////////////////////////////
  644. void
  645. ActiveConversationClass::Load_Variables (ChunkLoadClass &cload)
  646. {
  647. ActiveConversationClass *old_ptr = NULL;
  648. int conversation_id = 0;
  649. //
  650. // Loop through all the microchunks that define the variables
  651. //
  652. while (cload.Open_Micro_Chunk ()) {
  653. switch (cload.Cur_Micro_Chunk_ID ()) {
  654. READ_MICRO_CHUNK (cload, VARID_CONVERSATION_ID, conversation_id);
  655. READ_MICRO_CHUNK (cload, VARID_CURRENT_REMARK, CurrentRemark);
  656. READ_MICRO_CHUNK (cload, VARID_NEXT_REMARK_TIMER, NextRemarkTimer);
  657. READ_MICRO_CHUNK (cload, VARID_STATE, State);
  658. READ_MICRO_CHUNK (cload, VARID_ID, ID);
  659. READ_MICRO_CHUNK (cload, VARID_OLD_PTR, old_ptr);
  660. READ_MICRO_CHUNK (cload, VARID_INITIALIZING_TIMER, InitializingTimeLeft);
  661. READ_MICRO_CHUNK (cload, VARID_ACTION_ID, ActionID);
  662. READ_MICRO_CHUNK (cload, VARID_SPOKEN_BITMASK, OratorSpokenBitmask);
  663. READ_MICRO_CHUNK (cload, VARID_CENTRAL_POS, CentralPos);
  664. READ_MICRO_CHUNK (cload, VARID_PRIORITY, Priority);
  665. READ_MICRO_CHUNK (cload, VARID_MAXDIST, MaxDist);
  666. READ_MICRO_CHUNK (cload, VARID_IS_INTERRUPTABLE, IsInterruptable);
  667. }
  668. cload.Close_Micro_Chunk ();
  669. }
  670. //
  671. // Lookup the conversation pointer
  672. //
  673. if (conversation_id != 0) {
  674. Conversation = ConversationMgrClass::Find_Conversation (conversation_id);
  675. }
  676. //
  677. // Register our old pointer so other objects can safely remap to it
  678. //
  679. WWASSERT (old_ptr != NULL);
  680. SaveLoadSystemClass::Register_Pointer (old_ptr, this);
  681. return ;
  682. }
  683. ///////////////////////////////////////////////////////////////////////
  684. //
  685. // Set_Orator_Arrived
  686. //
  687. ///////////////////////////////////////////////////////////////////////
  688. void
  689. ActiveConversationClass::Set_Orator_Arrived (PhysicalGameObj *orator, bool has_arrived)
  690. {
  691. WWASSERT (orator != NULL);
  692. for (int index = 0; index < OratorList.Count (); index ++) {
  693. PhysicalGameObj *curr_orator = OratorList[index]->Get_Game_Obj ();
  694. //
  695. // If this is the orator we were looking for, then set
  696. // its arrived state
  697. //
  698. if (curr_orator != NULL && curr_orator == orator) {
  699. OratorList[index]->Set_Has_Arrived (has_arrived);
  700. break;
  701. }
  702. }
  703. return ;
  704. }
  705. ///////////////////////////////////////////////////////////////////////
  706. //
  707. // Get_Orator_Location
  708. //
  709. ///////////////////////////////////////////////////////////////////////
  710. bool
  711. ActiveConversationClass::Get_Orator_Location (PhysicalGameObj *orator, Vector3 *position)
  712. {
  713. WWASSERT (orator != NULL);
  714. WWASSERT (position != NULL);
  715. bool retval = false;
  716. for (int index = 0; index < OratorList.Count (); index ++) {
  717. PhysicalGameObj *curr_orator = OratorList[index]->Get_Game_Obj ();
  718. //
  719. // If this is the orator we were looking for, then pass the
  720. // expected position back to the caller
  721. //
  722. if (curr_orator != NULL && curr_orator == orator) {
  723. (*position) = OratorList[index]->Get_Position ();
  724. break;
  725. }
  726. }
  727. return retval;
  728. }
  729. ///////////////////////////////////////////////////////////////////////
  730. //
  731. // Get_Current_Orator_Location
  732. //
  733. ///////////////////////////////////////////////////////////////////////
  734. bool
  735. ActiveConversationClass::Get_Current_Orator_Location (Vector3 *position)
  736. {
  737. WWASSERT (position != NULL);
  738. WWASSERT (Conversation != NULL);
  739. bool retval = false;
  740. //
  741. // Lookup who is saying the current line
  742. //
  743. int orator_id = 0;
  744. if (CurrentRemark >= 0 && CurrentRemark < Conversation->Get_Remark_Count ()) {
  745. ConversationRemarkClass remark;
  746. Conversation->Get_Remark_Info (CurrentRemark, remark);
  747. orator_id = remark.Get_Orator_ID ();
  748. }
  749. //
  750. // Make sure the data is valid
  751. //
  752. WWASSERT (orator_id >= 0 && orator_id < OratorList.Count ());
  753. if (orator_id >= 0 && orator_id < OratorList.Count ()) {
  754. //
  755. // Now, return this orator's current position
  756. //
  757. PhysicalGameObj *orator = OratorList[orator_id]->Get_Game_Obj ();
  758. if (orator != NULL) {
  759. orator->Get_Position (position);
  760. }
  761. retval = true;
  762. }
  763. return retval;
  764. }
  765. ///////////////////////////////////////////////////////////////////////
  766. //
  767. // Get_Current_Orator
  768. //
  769. ///////////////////////////////////////////////////////////////////////
  770. PhysicalGameObj *
  771. ActiveConversationClass::Get_Current_Orator (void)
  772. {
  773. PhysicalGameObj *current_orator = NULL;
  774. //
  775. // Lookup who is saying the current line
  776. //
  777. if (CurrentRemark >= 0 && CurrentRemark < Conversation->Get_Remark_Count ()) {
  778. ConversationRemarkClass remark;
  779. Conversation->Get_Remark_Info (CurrentRemark, remark);
  780. int orator_id = remark.Get_Orator_ID ();
  781. if (orator_id >= 0 && orator_id < OratorList.Count ()) {
  782. current_orator = OratorList[orator_id]->Get_Game_Obj ();
  783. }
  784. }
  785. return current_orator;
  786. }
  787. ///////////////////////////////////////////////////////////////////////
  788. //
  789. // Get_Conversation_Center
  790. //
  791. ///////////////////////////////////////////////////////////////////////
  792. void
  793. ActiveConversationClass::Get_Conversation_Center (Vector3 *position)
  794. {
  795. WWASSERT (position != NULL);
  796. (*position) = CentralPos;
  797. return ;
  798. }
  799. ///////////////////////////////////////////////////////////////////////
  800. //
  801. // Get_Orator_Information
  802. //
  803. ///////////////////////////////////////////////////////////////////////
  804. OratorClass *
  805. ActiveConversationClass::Get_Orator_Information (PhysicalGameObj *soldier)
  806. {
  807. WWASSERT (soldier != NULL);
  808. OratorClass *orator = NULL;
  809. for (int index = 0; index < OratorList.Count (); index ++) {
  810. PhysicalGameObj *curr_soldier = OratorList[index]->Get_Game_Obj ();
  811. //
  812. // If this is the orator we were looking for, then pass the
  813. // expected information back to the caller
  814. //
  815. if (curr_soldier != NULL && curr_soldier == soldier) {
  816. orator = OratorList[index];
  817. break;
  818. }
  819. }
  820. return orator;
  821. }
  822. ///////////////////////////////////////////////////////////////////////
  823. //
  824. // Register_Monitor
  825. //
  826. ///////////////////////////////////////////////////////////////////////
  827. void
  828. ActiveConversationClass::Register_Monitor (ScriptableGameObj *game_obj)
  829. {
  830. bool found = false;
  831. int empty_index = -1;
  832. //
  833. // Check to ensure this game object isn't already registered as a monitor
  834. //
  835. for (int index = 0; index < MAX_MONITORS; index ++) {
  836. ScriptableGameObj *curr_game_obj = MonitorArray[index];
  837. if (curr_game_obj == game_obj) {
  838. found = true;
  839. break;
  840. } else if (curr_game_obj == NULL) {
  841. empty_index = index;
  842. }
  843. }
  844. if (found == false) {
  845. //
  846. // Insert the reference inside our array
  847. //
  848. if (empty_index != -1) {
  849. MonitorArray[empty_index] = game_obj;
  850. } else {
  851. Debug_Say (("Exceeded max monitors for an active conversation.\n"));
  852. }
  853. }
  854. return ;
  855. }
  856. ///////////////////////////////////////////////////////////////////////
  857. //
  858. // Unregister_Monitor
  859. //
  860. ///////////////////////////////////////////////////////////////////////
  861. void
  862. ActiveConversationClass::Unregister_Monitor (ScriptableGameObj *game_obj)
  863. {
  864. //
  865. // Remove the monitor from our list
  866. //
  867. for (int index = 0; index < MAX_MONITORS; index ++) {
  868. ScriptableGameObj *curr_game_obj = MonitorArray[index];
  869. if (curr_game_obj == game_obj) {
  870. MonitorArray[index] = NULL;
  871. break;
  872. }
  873. }
  874. return ;
  875. }
  876. ///////////////////////////////////////////////////////////////////////
  877. //
  878. // Notify_Monitors_On_End
  879. //
  880. ///////////////////////////////////////////////////////////////////////
  881. void
  882. ActiveConversationClass::Notify_Monitors_On_End (ActionCompleteReason reason)
  883. {
  884. //
  885. // Notify all the game objects
  886. //
  887. for (int index = 0; index < MAX_MONITORS; index ++) {
  888. ScriptableGameObj *game_obj = MonitorArray[index];
  889. //
  890. // Notify all the observers of this game object
  891. //
  892. if (game_obj != NULL) {
  893. const GameObjObserverList &observer_list = game_obj->Get_Observers ();
  894. for (int observer_index = 0; observer_index < observer_list.Count (); observer_index ++) {
  895. observer_list[observer_index]->Action_Complete (game_obj, ActionID, reason);
  896. }
  897. }
  898. }
  899. return ;
  900. }
  901. ///////////////////////////////////////////////////////////////////////
  902. //
  903. // Notify_Monitors
  904. //
  905. ///////////////////////////////////////////////////////////////////////
  906. void
  907. ActiveConversationClass::Notify_Monitors (int custom_event_id, int param)
  908. {
  909. //
  910. // Notify all the game objects
  911. //
  912. for (int index = 0; index < MAX_MONITORS; index ++) {
  913. ScriptableGameObj *game_obj = MonitorArray[index];
  914. //
  915. // Notify all the observers of this game object
  916. //
  917. if (game_obj != NULL) {
  918. const GameObjObserverList &observer_list = game_obj->Get_Observers ();
  919. for (int observer_index = 0; observer_index < observer_list.Count (); observer_index ++) {
  920. observer_list[observer_index]->Custom (game_obj, custom_event_id, param, NULL);
  921. }
  922. }
  923. }
  924. return ;
  925. }
  926. ///////////////////////////////////////////////////////////////////////
  927. //
  928. // Control_Orator
  929. //
  930. ///////////////////////////////////////////////////////////////////////
  931. void
  932. ActiveConversationClass::Control_Orator (SoldierGameObj *soldier)
  933. {
  934. OratorClass *orator_info = Get_Orator_Information (soldier);
  935. if (orator_info == NULL || CurrentRemark < 0) {
  936. return ;
  937. }
  938. const float HEAD_HEIGHT = 1.7F;
  939. //
  940. // Lookup information about the current remark
  941. //
  942. ConversationRemarkClass remark;
  943. Conversation->Get_Remark_Info (CurrentRemark, remark);
  944. if (orator_info->Get_Flag (OratorClass::FLAG_DONT_TURN_HEAD) == false) {
  945. //
  946. // Now determine where this participant should be looking
  947. //
  948. PhysicalGameObj *orator = Get_Current_Orator ();
  949. if (orator != NULL) {
  950. Vector3 look_pos (0, 0, 0);
  951. bool is_something_to_look_at = false;
  952. bool force_facing = false;
  953. //
  954. // Lookup the object we are trying to look at...
  955. //
  956. int look_at_id = orator_info->Get_Look_At_Obj ();
  957. PhysicalGameObj *look_at_obj = NULL;
  958. if (look_at_id > 0) {
  959. look_at_obj = GameObjManager::Find_PhysicalGameObj (look_at_id);
  960. } else if (look_at_id == -1) {
  961. look_at_obj = COMBAT_STAR;
  962. }
  963. //
  964. // Now, either look at a specific object, look at the speaker, or
  965. // find a random person to look at...
  966. //
  967. if (look_at_obj != NULL) {
  968. look_at_obj->Get_Position (&look_pos);
  969. look_pos.Z += HEAD_HEIGHT;
  970. soldier->Look_At (look_pos, 100.0F);
  971. is_something_to_look_at = true;
  972. force_facing = true;
  973. } else if (orator != soldier) {
  974. Get_Current_Orator_Location (&look_pos);
  975. look_pos.Z += HEAD_HEIGHT;
  976. soldier->Look_At (look_pos, 100.0F);
  977. is_something_to_look_at = true;
  978. } else if (OratorList.Count () > 1) {
  979. //
  980. // Choose a different participant to look at
  981. //
  982. int index = remark.Get_Orator_ID () + 1;
  983. if (index >= OratorList.Count ()) {
  984. index = 0;
  985. }
  986. //
  987. // Look at someone else
  988. //
  989. PhysicalGameObj *someone_to_lookat = OratorList[index]->Get_Game_Obj ();
  990. if (someone_to_lookat != NULL) {
  991. someone_to_lookat->Get_Position (&look_pos);
  992. look_pos.Z += HEAD_HEIGHT;
  993. soldier->Look_At (look_pos, 100.0F);
  994. is_something_to_look_at = true;
  995. }
  996. }
  997. //
  998. // If the head look would turn the soldier to far, then turn the body as well
  999. //
  1000. if ( is_something_to_look_at &&
  1001. (force_facing ||
  1002. ((orator_info->Get_Flag (OratorClass::FLAG_DONT_FACE) == false) &&
  1003. (orator_info->Get_Flag (OratorClass::FLAG_TEMP_DONT_FACE) == false))))
  1004. {
  1005. //
  1006. // Get the target relative to the head
  1007. //
  1008. Vector3 relative_look_pos;
  1009. Matrix3D::Inverse_Transform_Vector( soldier->Get_Transform (), look_pos, &relative_look_pos );
  1010. //Vector3 delta = look_pos - soldier->Get_Transform ().Get_Translation ();
  1011. //float curr_facing = soldier->Get_Facing ();
  1012. //float head_turn_angle = WWMath::Atan2 (delta.Y, delta.X);
  1013. //float angle = head_turn_angle - curr_facing;
  1014. float angle = WWMath::Atan2 (relative_look_pos.Y, relative_look_pos.X);
  1015. if (force_facing || WWMath::Fabs (angle) > DEG_TO_RADF (15)) {
  1016. soldier->Set_Targeting (look_pos, false);
  1017. }
  1018. }
  1019. }
  1020. }
  1021. return ;
  1022. }
  1023. ///////////////////////////////////////////////////////////////////////
  1024. //
  1025. // Stop_Conversation
  1026. //
  1027. ///////////////////////////////////////////////////////////////////////
  1028. void
  1029. ActiveConversationClass::Stop_Conversation (ActionCompleteReason reason)
  1030. {
  1031. if (Is_Finished ()) {
  1032. return ;
  1033. }
  1034. State = STATE_FINISHED;
  1035. Stop_Current_Sound ();
  1036. if (CurrentRemark >= 0 && CurrentRemark < Conversation->Get_Remark_Count ()) {
  1037. //
  1038. // Get the current speaker
  1039. //
  1040. ConversationRemarkClass remark;
  1041. Conversation->Get_Remark_Info (CurrentRemark, remark);
  1042. int orator_id = remark.Get_Orator_ID ();
  1043. //
  1044. // Sanity check
  1045. //
  1046. if (orator_id >= 0 && orator_id < OratorList.Count ()) {
  1047. PhysicalGameObj *orator = OratorList[orator_id]->Get_Game_Obj ();
  1048. //
  1049. // Have the current speaker stop speaking
  1050. //
  1051. if (orator != NULL && orator->As_SoldierGameObj () != NULL) {
  1052. orator->As_SoldierGameObj ()->Stop_Current_Speech ();
  1053. }
  1054. }
  1055. }
  1056. Free_Orator_List ();
  1057. Notify_Monitors_On_End (reason);
  1058. return ;
  1059. }
  1060. ///////////////////////////////////////////////////////////////////////
  1061. //
  1062. // Get_Conversation_Time
  1063. //
  1064. ///////////////////////////////////////////////////////////////////////
  1065. float
  1066. ActiveConversationClass::Get_Conversation_Time (void)
  1067. {
  1068. float retval = 0.0F;
  1069. for (int index = 0; index < Conversation->Get_Remark_Count (); index ++) {
  1070. //
  1071. // Lookup up the line of text that will be spoken
  1072. //
  1073. ConversationRemarkClass remark;
  1074. Conversation->Get_Remark_Info (index, remark);
  1075. int text_id = remark.Get_Text_ID ();
  1076. //
  1077. // Lookup the translation object from the strings database
  1078. //
  1079. TDBObjClass *translate_obj = TranslateDBClass::Find_Object (text_id);
  1080. if (translate_obj != NULL) {
  1081. //
  1082. // Create the sound object
  1083. //
  1084. uint32 sound_def_id = translate_obj->Get_Sound_ID ();
  1085. if (sound_def_id != 0) {
  1086. //
  1087. // Add the duration of the sound object to the total
  1088. //
  1089. AudibleSoundClass *speech = WWAudioClass::Get_Instance ()->Create_Sound (sound_def_id);
  1090. if (speech != NULL) {
  1091. retval += (speech->Get_Duration() / 1000.0F);
  1092. REF_PTR_RELEASE (speech);
  1093. }
  1094. }
  1095. }
  1096. }
  1097. return (retval > 0.0F) ? retval : 2.0F;
  1098. }
  1099. ///////////////////////////////////////////////////////////////////////
  1100. //
  1101. // Stop_Current_Sound
  1102. //
  1103. ///////////////////////////////////////////////////////////////////////
  1104. void
  1105. ActiveConversationClass::Stop_Current_Sound (void)
  1106. {
  1107. //
  1108. // Kill the current sound we are making (speech, scream, grunt, etc)
  1109. //
  1110. if (CurrentSound != NULL) {
  1111. CurrentSound->Stop ();
  1112. CurrentSound->Release_Ref ();
  1113. CurrentSound = NULL;
  1114. }
  1115. return ;
  1116. }