| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /***********************************************************************************************
- *** 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 ***
- ***********************************************************************************************
- * *
- * Project Name : Combat *
- * *
- * $Archive:: /Commando/Code/Combat/activeconversation.cpp $*
- * *
- * Author:: Patrick Smith *
- * *
- * $Modtime:: 1/16/02 6:01p $*
- * *
- * $Revision:: 31 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "activeconversation.h"
- #include "soldier.h"
- #include "chunkio.h"
- #include "actionparams.h"
- #include "timemgr.h"
- #include "orator.h"
- #include "pathfind.h"
- #include "globalsettings.h"
- #include "soldierobserver.h"
- #include "gameobjmanager.h"
- #include "conversationmgr.h"
- #include "debug.h"
- #include "wwaudio.h"
- #include "audiblesound.h"
- #include "translatedb.h"
- #include "translateobj.h"
- #include "combat.h"
- ////////////////////////////////////////////////////////////////
- // Constants
- ////////////////////////////////////////////////////////////////
- enum
- {
- CHUNKID_VARIABLES = 0x08090727,
- CHUNKID_ORATOR,
- CHUNKID_MONITOR
- };
- enum
- {
- XXXXX_CONVERSATION_PTR = 0,
- VARID_CURRENT_REMARK,
- XXXXX_NEXT_REMARK_TIME,
- VARID_STATE,
- VARID_OLD_PTR,
- VARID_ID,
- VARID_INITIALIZING_TIMER,
- VARID_ACTION_ID,
- VARID_SPOKEN_BITMASK,
- VARID_CENTRAL_POS,
- VARID_PRIORITY,
- VARID_MAXDIST,
- VARID_IS_INTERRUPTABLE,
- VARID_CONVERSATION_ID,
- VARID_NEXT_REMARK_TIMER,
- };
- static const float RAND_STAND_DIST = 2.0F;
- static const float MIN_AUDIENCE_DIST = 4.0F;
- ////////////////////////////////////////////////////////////////
- //
- // ActiveConversationClass
- //
- ////////////////////////////////////////////////////////////////
- ActiveConversationClass::ActiveConversationClass (void) :
- ID (0),
- Conversation (NULL),
- CurrentRemark (-1),
- NextRemarkTimer (0),
- State (STATE_INITIALIZING),
- InitializingTimeLeft (60000),
- ActionID (0),
- OratorSpokenBitmask (0),
- CentralPos (0, 0, 0),
- Priority (30),
- MaxDist (0),
- IsInterruptable (true),
- CurrentSound (NULL)
- {
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // ~ActiveConversationClass
- //
- ////////////////////////////////////////////////////////////////
- ActiveConversationClass::~ActiveConversationClass (void)
- {
- Stop_Current_Sound ();
- Free_Orator_List ();
- REF_PTR_RELEASE (Conversation);
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Free_Orator_List
- //
- ////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Free_Orator_List (void)
- {
- //
- // Free each orator in our list
- //
- for (int index = 0; index < OratorList.Count (); index ++) {
- OratorClass *orator = OratorList[index];
- if (orator != NULL) {
- PhysicalGameObj *game_obj = orator->Get_Game_Obj ();
- if (game_obj != NULL) {
- //
- // Was this a soldier we were talking to?
- //
- SoldierGameObj *soldier = game_obj->As_SoldierGameObj ();
- if (soldier != NULL) {
- //
- // Turn idle animations back on for soldiers
- //
- soldier->As_SoldierGameObj ()->Set_Loiters_Allowed (true);
-
- //
- // Make sure the soldier doesn't ramble on after the
- // conversation has ended.
- //
- soldier->Stop_Current_Speech ();
- //
- // Reset the possibility that this soldier will have a conversation
- //
- SoldierObserverClass *innate_ai = soldier->Get_Innate_Controller ();
- if (innate_ai != NULL) {
- innate_ai->Reset_Conversation_Timer ();
- }
-
- //
- // If we were controlling the soldier's head, return
- // it to its default position
- //
- if (orator->Get_Flag (OratorClass::FLAG_DONT_TURN_HEAD) == false) {
- soldier->Cancel_Look_At ();
- }
- }
- //
- // Let the game obj know its no longer in a conversation
- //
- game_obj->Set_Conversation (NULL);
- }
- delete orator;
- orator = NULL;
- }
- }
- OratorList.Delete_All ();
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Set_Conversation
- //
- ////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Set_Conversation (ConversationClass *conversation)
- {
- REF_PTR_SET (Conversation, conversation);
- CurrentRemark = -1;
- NextRemarkTimer = 0;
- OratorSpokenBitmask = 0;
- Priority = conversation->Get_Priority ();
- MaxDist = conversation->Get_Max_Dist ();
- IsInterruptable = conversation->Is_Interruptable ();
- Free_Orator_List ();
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Add_Orator
- //
- ////////////////////////////////////////////////////////////////
- OratorClass *
- ActiveConversationClass::Add_Orator (PhysicalGameObj *game_obj)
- {
- //
- // Let the game obj know he's part of a conversation
- //
- if (game_obj != NULL) {
- game_obj->Set_Conversation (this);
- }
- //
- // Allocate and initialize a new orator
- //
- OratorClass *orator = new OratorClass;
- orator->Initialize (game_obj);
- orator->Set_ID (OratorList.Count ());
- //
- // Add the new orator to our list
- //
- OratorList.Add (orator);
- //
- // If orators are still being added to the conversation, the
- // the object is considered initializing.
- //
- State = STATE_INITIALIZING;
- return orator;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Start_Conversation
- //
- ////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Start_Conversation (void)
- {
- WWASSERT (OratorList.Count () > 0);
- OratorSpokenBitmask = 0;
- //
- // Find the first "visible" orator in our list
- //
- PhysicalGameObj *main_speaker = NULL;
- for ( int orator_index = 0;
- main_speaker == NULL && orator_index < OratorList.Count ();
- orator_index ++)
- {
- main_speaker = OratorList[orator_index]->Get_Game_Obj ();
- }
- //
- // Get the location of the first orator
- //
- if (main_speaker != NULL) {
-
- //
- // Pick a center position somewhere around the first speaker
- //
- main_speaker->Get_Position (&CentralPos);
- //
- // Find a safe central position for the conversation
- //
- PathfindClass *pathfind = PathfindClass::Get_Instance();
- if (pathfind->Find_Random_Spot( CentralPos, 2, &CentralPos)) {
- for (int index = 0; index < OratorList.Count (); index ++) {
- //
- // Don't allow this unit to face the speaker by default. (This
- // flag will be cleared by the ActionCodeClass if its priority is high enough)
- //
- OratorList[index]->Set_Flag (OratorClass::FLAG_TEMP_DONT_FACE, true);
- //
- // If this soldier is allowed to move, then calculate a new position for them
- //
- if (OratorList[index]->Get_Flag (OratorClass::FLAG_DONT_MOVE) == false) {
- //
- // Lookup a safe random position to stand
- //
- Vector3 new_location;
- if (pathfind->Find_Random_Spot (CentralPos, 2, &new_location)) {
- //
- // Store this position in our list so the orator can query
- // for his position later.
- //
- OratorList[index]->Set_Position (new_location);
- }
- }
- }
- }
- //
- // Now, let each orator know its part of a conversation
- //
- for (int index = 0; index < OratorList.Count (); index ++) {
- PhysicalGameObj *game_obj = OratorList[index]->Get_Game_Obj ();
- if (game_obj != NULL) {
- //
- // Turn off idle animations for soldiers when they are
- // having a conversation
- //
- if (game_obj->As_SoldierGameObj () != NULL) {
- game_obj->As_SoldierGameObj ()->Set_Loiters_Allowed (false);
- }
- //
- // Make sure we don't take control of a human player
- //
- if ( game_obj->As_SmartGameObj () == NULL ||
- game_obj->As_SmartGameObj ()->Is_Human_Controlled () ||
- OratorList.Count () <= 2)
- {
- OratorList[index]->Set_Flag (OratorClass::FLAG_DONT_MOVE, true);
- OratorList[index]->Set_Flag (OratorClass::FLAG_TEMP_DONT_FACE, false);
-
- //
- // Don't force facing if its a human player or inanimate object
- //
- if ( game_obj->As_SmartGameObj () == NULL ||
- game_obj->As_SmartGameObj ()->Is_Human_Controlled ())
- {
- OratorList[index]->Set_Flag (OratorClass::FLAG_DONT_FACE, true);
- }
-
- //
- // Since this object doesn't have to move -- reset its position
- //
- Vector3 position;
- game_obj->Get_Position (&position);
- OratorList[index]->Set_Position (position);
- } else {
- ActionParamsStruct parameters;
- parameters.Priority = Priority;
- parameters.ActionID = ActionID;
- parameters.ObserverID = 0;
- parameters.Join_Conversation (ID);
- game_obj->As_SmartGameObj ()->Get_Action ()->Have_Conversation (parameters);
- }
- }
- }
- }
- //
- // Begin waiting for the participants to move to their location
- //
- State = STATE_WAITING_FOR_AUDIENCE;
- //
- // Don't allow a key conversation to be interrupted
- //
- if (Conversation != NULL && Conversation->Is_Key ()) {
- Set_Is_Interruptable (false);
- //
- // Clear all other conversations from the manager
- //
- //ConversationMgrClass::Reset_All_Other_Conversations (this);
- } else {
- //
- // Stop this conversation before it can start if there is
- // a key conversation playing
- //
- if (ConversationMgrClass::Is_Key_Conversation_Playing ()) {
- Stop_Conversation (ACTION_COMPLETE_CONVERSATION_INTERRUPTED);
- }
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Think
- //
- ////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Think (void)
- {
- //
- // Make each orator do something meaningful
- //
- for (int index = 0; index < OratorList.Count (); index ++) {
- PhysicalGameObj *game_obj = OratorList[index]->Get_Game_Obj ();
- if (game_obj != NULL && game_obj->As_SoldierGameObj () != NULL) {
- SoldierGameObj *soldier = game_obj->As_SoldierGameObj ();
-
- //
- // Stop the conversation if the orator is dead, otherwise
- // take control of some of his movements
- //
- if (soldier->Is_Dead () || soldier->Is_Destroyed ()) {
- Stop_Conversation (ACTION_COMPLETE_CONVERSATION_INTERRUPTED);
- } else {
- Control_Orator (soldier);
- }
- }
- }
- if (State == STATE_WAITING_FOR_AUDIENCE) {
- Check_For_Audience ();
- } else if (State == STATE_TALKING) {
-
- //
- // Should we advance to the next remark?
- //
- NextRemarkTimer -= TimeManager::Get_Frame_Seconds ();
- if (NextRemarkTimer <= 0) {
-
- //
- // If everyone is listening, then move on to the next remark,
- // otherwise kick out of the conversation
- //
- if (Is_Audience_In_Place () == false) {
- Stop_Conversation (ACTION_COMPLETE_CONVERSATION_INTERRUPTED);
- } else {
- Say_Next_Remark ();
- }
- }
- } else if (State == STATE_INITIALIZING) {
-
- //
- // Scrap the conversation if too much time was spent initializing (probably an error).
- //
- InitializingTimeLeft -= TimeManager::Get_Frame_Seconds ();
- if (InitializingTimeLeft <= 0) {
- Stop_Conversation (ACTION_COMPLETE_CONVERSATION_UNABLE_TO_INIT);
- }
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Say_Next_Remark
- //
- ////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Say_Next_Remark (void)
- {
- //
- // Move onto the next remark if possible
- //
- CurrentRemark ++;
- if (CurrentRemark < Conversation->Get_Remark_Count ()) {
- //
- // Notify the monitors of the event
- //
- if (CurrentRemark > 0) {
- Notify_Monitors (CUSTOM_EVENT_CONVERSATION_REMARK_ENDED, CurrentRemark - 1);
- }
- Notify_Monitors (CUSTOM_EVENT_CONVERSATION_REMARK_STARTED, CurrentRemark);
- //
- // Lookup who says the next line
- //
- ConversationRemarkClass remark;
- Conversation->Get_Remark_Info (CurrentRemark, remark);
- int orator_id = remark.Get_Orator_ID ();
- int text_id = remark.Get_Text_ID ();
- //
- // Make sure the data is valid
- //
- WWASSERT_PRINT (orator_id >= 0 && orator_id < OratorList.Count (), Conversation->Get_Name ());
- if (orator_id >= 0 && orator_id < OratorList.Count ()) {
- PhysicalGameObj *orator = OratorList[orator_id]->Get_Game_Obj ();
- //
- // Set a bit which lets us know this orator has spoken at least once...
- //
- OratorSpokenBitmask |= (1 << orator_id);
- //
- // Have the orator start his remark
- //
- SoldierGameObj *soldier = NULL;
- if (orator != NULL) {
- soldier = orator->As_SoldierGameObj ();
- }
-
- float duration = 0;
-
- if (soldier != NULL) {
- duration = SoldierGameObj::Say_Dynamic_Dialogue (text_id, soldier);
- } else {
- REF_PTR_RELEASE (CurrentSound);
- duration = SoldierGameObj::Say_Dynamic_Dialogue (text_id, NULL, &CurrentSound);
- }
- //
- // Play an animation on the orator
- //
- if (orator != NULL && remark.Get_Animation_Name ().Get_Length () > 0) {
- orator->Set_Animation (remark.Get_Animation_Name (), false);
- }
- //
- // Determine when we should switch to the next remark
- //
- NextRemarkTimer = duration;
- }
- } else {
- Stop_Conversation (ACTION_COMPLETE_CONVERSATION_ENDED);
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Is_Audience_In_Place
- //
- ////////////////////////////////////////////////////////////////
- bool
- ActiveConversationClass::Is_Audience_In_Place (void)
- {
- bool retval = true;
- //
- // Determine how far away each participant can be before
- // we terminate the conversation
- //
- float max_dist = MaxDist;
- if (max_dist <= 0) {
- if (Conversation->Get_AI_State () == AI_STATE_COMBAT) {
- max_dist = GlobalSettingsDef::Get_Global_Settings ()->Get_Combat_Conversation_Dist ();
- } else {
- max_dist = GlobalSettingsDef::Get_Global_Settings ()->Get_Conversation_Dist ();
- }
- }
-
- //
- // Square the distance for faster comparisons
- //
- float max_dist2 = max_dist * max_dist;
- //
- // Check to see if everyone is close enough to have this conversation
- //
- for (int index = 0; index < OratorList.Count (); index ++) {
- PhysicalGameObj *game_obj = OratorList[index]->Get_Game_Obj ();
- if (game_obj != NULL) {
-
- //
- // Get this soldier's position
- //
- Vector3 position;
- game_obj->Get_Position (&position);
- //
- // Calculate how far from the conversation's center this
- // soldier is.
- //
- Vector3 delta = position - CentralPos;
- delta.Z = delta.Z * 0.5F;
- //
- // If this distance is greater then the maximum allowed, then
- // the audience is NOT is place
- //
- float distance2 = delta.Length2 ();
- if (distance2 > max_dist2) {
- retval = false;
- break;
- }
- }
- }
- return retval;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Check_For_Audience
- //
- ////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Check_For_Audience (void)
- {
- if (State == STATE_WAITING_FOR_AUDIENCE) {
- //
- // If everyone is close enough then start the conversation
- //
- if (Is_Audience_In_Place ()) {
- State = STATE_TALKING;
- Notify_Monitors (CUSTOM_EVENT_CONVERSATION_BEGAN, Conversation->Get_ID ());
- }
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Save
- //
- ////////////////////////////////////////////////////////////////
- bool
- ActiveConversationClass::Save (ChunkSaveClass &csave)
- {
- csave.Begin_Chunk (CHUNKID_VARIABLES);
- //
- // Save the ID of the conversation (if necessary)
- //
- if (Conversation != NULL) {
- int conversation_id = Conversation->Get_ID ();
- WRITE_MICRO_CHUNK (csave, VARID_CONVERSATION_ID, conversation_id);
- }
- WRITE_MICRO_CHUNK (csave, VARID_CURRENT_REMARK, CurrentRemark);
- WRITE_MICRO_CHUNK (csave, VARID_NEXT_REMARK_TIMER, NextRemarkTimer);
- WRITE_MICRO_CHUNK (csave, VARID_STATE, State);
- WRITE_MICRO_CHUNK (csave, VARID_ID, ID);
- WRITE_MICRO_CHUNK (csave, VARID_INITIALIZING_TIMER, InitializingTimeLeft);
- WRITE_MICRO_CHUNK (csave, VARID_ACTION_ID, ActionID);
- WRITE_MICRO_CHUNK (csave, VARID_SPOKEN_BITMASK, OratorSpokenBitmask);
- WRITE_MICRO_CHUNK (csave, VARID_CENTRAL_POS, CentralPos);
- WRITE_MICRO_CHUNK (csave, VARID_PRIORITY, Priority);
- WRITE_MICRO_CHUNK (csave, VARID_MAXDIST, MaxDist);
- WRITE_MICRO_CHUNK (csave, VARID_IS_INTERRUPTABLE, IsInterruptable);
-
- //
- // Save our current pointer so we can remap it on load
- //
- ActiveConversationClass *old_ptr = this;
- WRITE_MICRO_CHUNK (csave, VARID_OLD_PTR, old_ptr);
-
- csave.End_Chunk ();
- //
- // Save each of the monitors
- //
- for (int index = 0; index < MAX_MONITORS; index ++) {
- if (MonitorArray[index].Get_Ptr () != NULL) {
- csave.Begin_Chunk (CHUNKID_MONITOR);
- MonitorArray[index].Save (csave);
- csave.End_Chunk ();
- }
- }
- //
- // Save each of the orators
- //
- for (index = 0; index < OratorList.Count (); index ++) {
- csave.Begin_Chunk (CHUNKID_ORATOR);
- OratorList[index]->Save (csave);
- csave.End_Chunk ();
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////
- //
- // Load
- //
- ////////////////////////////////////////////////////////////////
- bool
- ActiveConversationClass::Load (ChunkLoadClass &cload)
- {
- Free_Orator_List ();
- int monitor_index = 0;
- while (cload.Open_Chunk ()) {
- switch (cload.Cur_Chunk_ID ()) {
- case CHUNKID_VARIABLES:
- Load_Variables (cload);
- break;
- case CHUNKID_ORATOR:
- {
- //
- // Allocate and load a new orator object
- //
- OratorClass *orator = new OratorClass;
- orator->Load (cload);
- //
- // Add this new object to our list
- //
- OratorList.Add (orator);
- }
- break;
- case CHUNKID_MONITOR:
- {
- //
- // Load the monitor's reference from the chunk
- //
- MonitorArray[monitor_index++].Load (cload);
- }
- break;
- }
- cload.Close_Chunk ();
- }
- return true;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Load_Variables
- //
- ///////////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Load_Variables (ChunkLoadClass &cload)
- {
- ActiveConversationClass *old_ptr = NULL;
- int conversation_id = 0;
- //
- // Loop through all the microchunks that define the variables
- //
- while (cload.Open_Micro_Chunk ()) {
- switch (cload.Cur_Micro_Chunk_ID ()) {
- READ_MICRO_CHUNK (cload, VARID_CONVERSATION_ID, conversation_id);
- READ_MICRO_CHUNK (cload, VARID_CURRENT_REMARK, CurrentRemark);
- READ_MICRO_CHUNK (cload, VARID_NEXT_REMARK_TIMER, NextRemarkTimer);
- READ_MICRO_CHUNK (cload, VARID_STATE, State);
- READ_MICRO_CHUNK (cload, VARID_ID, ID);
- READ_MICRO_CHUNK (cload, VARID_OLD_PTR, old_ptr);
- READ_MICRO_CHUNK (cload, VARID_INITIALIZING_TIMER, InitializingTimeLeft);
- READ_MICRO_CHUNK (cload, VARID_ACTION_ID, ActionID);
- READ_MICRO_CHUNK (cload, VARID_SPOKEN_BITMASK, OratorSpokenBitmask);
- READ_MICRO_CHUNK (cload, VARID_CENTRAL_POS, CentralPos);
- READ_MICRO_CHUNK (cload, VARID_PRIORITY, Priority);
- READ_MICRO_CHUNK (cload, VARID_MAXDIST, MaxDist);
- READ_MICRO_CHUNK (cload, VARID_IS_INTERRUPTABLE, IsInterruptable);
- }
- cload.Close_Micro_Chunk ();
- }
- //
- // Lookup the conversation pointer
- //
- if (conversation_id != 0) {
- Conversation = ConversationMgrClass::Find_Conversation (conversation_id);
- }
- //
- // Register our old pointer so other objects can safely remap to it
- //
- WWASSERT (old_ptr != NULL);
- SaveLoadSystemClass::Register_Pointer (old_ptr, this);
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Set_Orator_Arrived
- //
- ///////////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Set_Orator_Arrived (PhysicalGameObj *orator, bool has_arrived)
- {
- WWASSERT (orator != NULL);
-
- for (int index = 0; index < OratorList.Count (); index ++) {
- PhysicalGameObj *curr_orator = OratorList[index]->Get_Game_Obj ();
-
- //
- // If this is the orator we were looking for, then set
- // its arrived state
- //
- if (curr_orator != NULL && curr_orator == orator) {
- OratorList[index]->Set_Has_Arrived (has_arrived);
- break;
- }
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Get_Orator_Location
- //
- ///////////////////////////////////////////////////////////////////////
- bool
- ActiveConversationClass::Get_Orator_Location (PhysicalGameObj *orator, Vector3 *position)
- {
- WWASSERT (orator != NULL);
- WWASSERT (position != NULL);
- bool retval = false;
-
- for (int index = 0; index < OratorList.Count (); index ++) {
- PhysicalGameObj *curr_orator = OratorList[index]->Get_Game_Obj ();
-
- //
- // If this is the orator we were looking for, then pass the
- // expected position back to the caller
- //
- if (curr_orator != NULL && curr_orator == orator) {
- (*position) = OratorList[index]->Get_Position ();
- break;
- }
- }
- return retval;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Get_Current_Orator_Location
- //
- ///////////////////////////////////////////////////////////////////////
- bool
- ActiveConversationClass::Get_Current_Orator_Location (Vector3 *position)
- {
- WWASSERT (position != NULL);
- WWASSERT (Conversation != NULL);
- bool retval = false;
- //
- // Lookup who is saying the current line
- //
- int orator_id = 0;
- if (CurrentRemark >= 0 && CurrentRemark < Conversation->Get_Remark_Count ()) {
- ConversationRemarkClass remark;
- Conversation->Get_Remark_Info (CurrentRemark, remark);
- orator_id = remark.Get_Orator_ID ();
- }
- //
- // Make sure the data is valid
- //
- WWASSERT (orator_id >= 0 && orator_id < OratorList.Count ());
- if (orator_id >= 0 && orator_id < OratorList.Count ()) {
-
- //
- // Now, return this orator's current position
- //
- PhysicalGameObj *orator = OratorList[orator_id]->Get_Game_Obj ();
- if (orator != NULL) {
- orator->Get_Position (position);
- }
- retval = true;
- }
- return retval;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Get_Current_Orator
- //
- ///////////////////////////////////////////////////////////////////////
- PhysicalGameObj *
- ActiveConversationClass::Get_Current_Orator (void)
- {
- PhysicalGameObj *current_orator = NULL;
- //
- // Lookup who is saying the current line
- //
- if (CurrentRemark >= 0 && CurrentRemark < Conversation->Get_Remark_Count ()) {
-
- ConversationRemarkClass remark;
- Conversation->Get_Remark_Info (CurrentRemark, remark);
- int orator_id = remark.Get_Orator_ID ();
- if (orator_id >= 0 && orator_id < OratorList.Count ()) {
- current_orator = OratorList[orator_id]->Get_Game_Obj ();
- }
- }
- return current_orator;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Get_Conversation_Center
- //
- ///////////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Get_Conversation_Center (Vector3 *position)
- {
- WWASSERT (position != NULL);
- (*position) = CentralPos;
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Get_Orator_Information
- //
- ///////////////////////////////////////////////////////////////////////
- OratorClass *
- ActiveConversationClass::Get_Orator_Information (PhysicalGameObj *soldier)
- {
- WWASSERT (soldier != NULL);
- OratorClass *orator = NULL;
-
- for (int index = 0; index < OratorList.Count (); index ++) {
- PhysicalGameObj *curr_soldier = OratorList[index]->Get_Game_Obj ();
-
- //
- // If this is the orator we were looking for, then pass the
- // expected information back to the caller
- //
- if (curr_soldier != NULL && curr_soldier == soldier) {
- orator = OratorList[index];
- break;
- }
- }
- return orator;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Register_Monitor
- //
- ///////////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Register_Monitor (ScriptableGameObj *game_obj)
- {
- bool found = false;
- int empty_index = -1;
- //
- // Check to ensure this game object isn't already registered as a monitor
- //
- for (int index = 0; index < MAX_MONITORS; index ++) {
- ScriptableGameObj *curr_game_obj = MonitorArray[index];
- if (curr_game_obj == game_obj) {
- found = true;
- break;
- } else if (curr_game_obj == NULL) {
- empty_index = index;
- }
- }
-
- if (found == false) {
- //
- // Insert the reference inside our array
- //
- if (empty_index != -1) {
- MonitorArray[empty_index] = game_obj;
- } else {
- Debug_Say (("Exceeded max monitors for an active conversation.\n"));
- }
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Unregister_Monitor
- //
- ///////////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Unregister_Monitor (ScriptableGameObj *game_obj)
- {
- //
- // Remove the monitor from our list
- //
- for (int index = 0; index < MAX_MONITORS; index ++) {
- ScriptableGameObj *curr_game_obj = MonitorArray[index];
- if (curr_game_obj == game_obj) {
- MonitorArray[index] = NULL;
- break;
- }
- }
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Notify_Monitors_On_End
- //
- ///////////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Notify_Monitors_On_End (ActionCompleteReason reason)
- {
- //
- // Notify all the game objects
- //
- for (int index = 0; index < MAX_MONITORS; index ++) {
- ScriptableGameObj *game_obj = MonitorArray[index];
- //
- // Notify all the observers of this game object
- //
- if (game_obj != NULL) {
- const GameObjObserverList &observer_list = game_obj->Get_Observers ();
- for (int observer_index = 0; observer_index < observer_list.Count (); observer_index ++) {
- observer_list[observer_index]->Action_Complete (game_obj, ActionID, reason);
- }
- }
- }
-
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Notify_Monitors
- //
- ///////////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Notify_Monitors (int custom_event_id, int param)
- {
- //
- // Notify all the game objects
- //
- for (int index = 0; index < MAX_MONITORS; index ++) {
- ScriptableGameObj *game_obj = MonitorArray[index];
- //
- // Notify all the observers of this game object
- //
- if (game_obj != NULL) {
- const GameObjObserverList &observer_list = game_obj->Get_Observers ();
- for (int observer_index = 0; observer_index < observer_list.Count (); observer_index ++) {
- observer_list[observer_index]->Custom (game_obj, custom_event_id, param, NULL);
- }
- }
- }
-
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Control_Orator
- //
- ///////////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Control_Orator (SoldierGameObj *soldier)
- {
- OratorClass *orator_info = Get_Orator_Information (soldier);
- if (orator_info == NULL || CurrentRemark < 0) {
- return ;
- }
- const float HEAD_HEIGHT = 1.7F;
-
- //
- // Lookup information about the current remark
- //
- ConversationRemarkClass remark;
- Conversation->Get_Remark_Info (CurrentRemark, remark);
- if (orator_info->Get_Flag (OratorClass::FLAG_DONT_TURN_HEAD) == false) {
- //
- // Now determine where this participant should be looking
- //
- PhysicalGameObj *orator = Get_Current_Orator ();
- if (orator != NULL) {
- Vector3 look_pos (0, 0, 0);
- bool is_something_to_look_at = false;
- bool force_facing = false;
- //
- // Lookup the object we are trying to look at...
- //
- int look_at_id = orator_info->Get_Look_At_Obj ();
- PhysicalGameObj *look_at_obj = NULL;
- if (look_at_id > 0) {
- look_at_obj = GameObjManager::Find_PhysicalGameObj (look_at_id);
- } else if (look_at_id == -1) {
- look_at_obj = COMBAT_STAR;
- }
-
- //
- // Now, either look at a specific object, look at the speaker, or
- // find a random person to look at...
- //
- if (look_at_obj != NULL) {
-
- look_at_obj->Get_Position (&look_pos);
- look_pos.Z += HEAD_HEIGHT;
- soldier->Look_At (look_pos, 100.0F);
- is_something_to_look_at = true;
- force_facing = true;
-
- } else if (orator != soldier) {
- Get_Current_Orator_Location (&look_pos);
- look_pos.Z += HEAD_HEIGHT;
- soldier->Look_At (look_pos, 100.0F);
- is_something_to_look_at = true;
- } else if (OratorList.Count () > 1) {
- //
- // Choose a different participant to look at
- //
- int index = remark.Get_Orator_ID () + 1;
- if (index >= OratorList.Count ()) {
- index = 0;
- }
- //
- // Look at someone else
- //
- PhysicalGameObj *someone_to_lookat = OratorList[index]->Get_Game_Obj ();
- if (someone_to_lookat != NULL) {
- someone_to_lookat->Get_Position (&look_pos);
- look_pos.Z += HEAD_HEIGHT;
- soldier->Look_At (look_pos, 100.0F);
- is_something_to_look_at = true;
- }
- }
- //
- // If the head look would turn the soldier to far, then turn the body as well
- //
- if ( is_something_to_look_at &&
- (force_facing ||
- ((orator_info->Get_Flag (OratorClass::FLAG_DONT_FACE) == false) &&
- (orator_info->Get_Flag (OratorClass::FLAG_TEMP_DONT_FACE) == false))))
- {
- //
- // Get the target relative to the head
- //
- Vector3 relative_look_pos;
- Matrix3D::Inverse_Transform_Vector( soldier->Get_Transform (), look_pos, &relative_look_pos );
- //Vector3 delta = look_pos - soldier->Get_Transform ().Get_Translation ();
- //float curr_facing = soldier->Get_Facing ();
- //float head_turn_angle = WWMath::Atan2 (delta.Y, delta.X);
- //float angle = head_turn_angle - curr_facing;
- float angle = WWMath::Atan2 (relative_look_pos.Y, relative_look_pos.X);
- if (force_facing || WWMath::Fabs (angle) > DEG_TO_RADF (15)) {
- soldier->Set_Targeting (look_pos, false);
- }
- }
- }
- }
-
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Stop_Conversation
- //
- ///////////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Stop_Conversation (ActionCompleteReason reason)
- {
- if (Is_Finished ()) {
- return ;
- }
- State = STATE_FINISHED;
- Stop_Current_Sound ();
- if (CurrentRemark >= 0 && CurrentRemark < Conversation->Get_Remark_Count ()) {
-
- //
- // Get the current speaker
- //
- ConversationRemarkClass remark;
- Conversation->Get_Remark_Info (CurrentRemark, remark);
- int orator_id = remark.Get_Orator_ID ();
- //
- // Sanity check
- //
- if (orator_id >= 0 && orator_id < OratorList.Count ()) {
- PhysicalGameObj *orator = OratorList[orator_id]->Get_Game_Obj ();
- //
- // Have the current speaker stop speaking
- //
- if (orator != NULL && orator->As_SoldierGameObj () != NULL) {
- orator->As_SoldierGameObj ()->Stop_Current_Speech ();
- }
- }
- }
-
- Free_Orator_List ();
- Notify_Monitors_On_End (reason);
- return ;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Get_Conversation_Time
- //
- ///////////////////////////////////////////////////////////////////////
- float
- ActiveConversationClass::Get_Conversation_Time (void)
- {
- float retval = 0.0F;
- for (int index = 0; index < Conversation->Get_Remark_Count (); index ++) {
- //
- // Lookup up the line of text that will be spoken
- //
- ConversationRemarkClass remark;
- Conversation->Get_Remark_Info (index, remark);
- int text_id = remark.Get_Text_ID ();
- //
- // Lookup the translation object from the strings database
- //
- TDBObjClass *translate_obj = TranslateDBClass::Find_Object (text_id);
- if (translate_obj != NULL) {
-
- //
- // Create the sound object
- //
- uint32 sound_def_id = translate_obj->Get_Sound_ID ();
- if (sound_def_id != 0) {
-
- //
- // Add the duration of the sound object to the total
- //
- AudibleSoundClass *speech = WWAudioClass::Get_Instance ()->Create_Sound (sound_def_id);
- if (speech != NULL) {
- retval += (speech->Get_Duration() / 1000.0F);
- REF_PTR_RELEASE (speech);
- }
- }
- }
- }
- return (retval > 0.0F) ? retval : 2.0F;
- }
- ///////////////////////////////////////////////////////////////////////
- //
- // Stop_Current_Sound
- //
- ///////////////////////////////////////////////////////////////////////
- void
- ActiveConversationClass::Stop_Current_Sound (void)
- {
- //
- // Kill the current sound we are making (speech, scream, grunt, etc)
- //
- if (CurrentSound != NULL) {
- CurrentSound->Stop ();
- CurrentSound->Release_Ref ();
- CurrentSound = NULL;
- }
- return ;
- }
|