/* ** 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 . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando * * * * $Archive:: /Commando/Code/Combat/objectives.cpp $* * * * $Author:: Patrick $* * * * $Modtime:: 1/24/02 5:10p $* * * * $Revision:: 38 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "objectives.h" #include "gameobjref.h" #include "debug.h" #include "radar.h" #include "scriptablegameobj.h" #include "translatedb.h" #include "combat.h" #include "messagewindow.h" #include "globalsettings.h" #include "physicalgameobj.h" #include "diaglog.h" #include "wwaudio.h" #include "hud.h" #include "string_ids.h" /* ** Static member initialization */ SimpleDynVecClass ObjectiveManager::ObjectiveList; ObjectivesViewerClass ObjectiveManager::Viewer; bool ObjectiveManager::DebugMode = false; bool ObjectiveManager::HUDUpdate = true; int ObjectiveManager::NumSpecifiedTertiaryObjectives; /* ** */ const Vector3 & Objective::Type_To_Base_Color( void ) { static Vector3 color (1.0F, 1.0F, 1.0F); if ( HUDGlobalSettingsDef::Get_Instance() != NULL ) { switch ( Type ) { case ObjectiveManager::TYPE_PRIMARY: color = HUDGlobalSettingsDef::Get_Instance()->Get_Primary_Objective_Color(); break; case ObjectiveManager::TYPE_SECONDARY: color = HUDGlobalSettingsDef::Get_Instance()->Get_Secondary_Objective_Color(); break; case ObjectiveManager::TYPE_TERTIARY: color = HUDGlobalSettingsDef::Get_Instance()->Get_Tertiary_Objective_Color(); break; } } return color; } /* ** */ const Vector3 & Objective::Type_To_Color( void ) { static Vector3 color (1.0F, 1.0F, 1.0F); color = Type_To_Base_Color (); // // Dim the colors if this objective has been accomplished // if( Status == ObjectiveManager::STATUS_ACCOMPLISHED ) { color *= 0.6F; } return color; } const WCHAR * Objective::Type_To_Name( void ) { switch ( Type ) { case ObjectiveManager::TYPE_PRIMARY: return TRANSLATE (IDS_MENU_TEXT145); case ObjectiveManager::TYPE_SECONDARY: return TRANSLATE (IDS_MENU_TEXT113); case ObjectiveManager::TYPE_TERTIARY: return TRANSLATE (IDS_MENU_TERTIARY); default: return TRANSLATE (IDS_LOCALE_UNKNOWN); } } const WCHAR * Objective::Status_To_Name( void ) { if ( Status == ObjectiveManager::STATUS_ACCOMPLISHED ) return TRANSLATE (IDS_MENU_OBJ_ACCOMPLISHED); if ( Status == ObjectiveManager::STATUS_FAILED ) return TRANSLATE (IDS_MENU_OBJ_FAILED); if ( Status == ObjectiveManager::STATUS_HIDDEN ) return TRANSLATE (IDS_MENU_OBJ_HIDDEN); return TRANSLATE (IDS_MENU_OBJ_PENDING); } const Vector3 & Objective::Status_To_Color( void ) { static const Vector3 RED(1,0,0); static const Vector3 GREEN(0,1,0); static const Vector3 YELLOW(1,1,0); static const Vector3 GREY(0.8F,0.8F,0.8F); switch ( Status ) { case ObjectiveManager::STATUS_IS_PENDING: return GREEN; case ObjectiveManager::STATUS_ACCOMPLISHED: return YELLOW; case ObjectiveManager::STATUS_FAILED: return RED; case ObjectiveManager::STATUS_HIDDEN: return GREY; default: return GREEN; } } Objective::Objective( void ) : ID( 0 ), Type( 0 ), Status( 0 ), DrawBlip( false ), Position( 0,0,0 ), BlipIntensity( 0 ), HUDMessageStringID( 0 ), LongDescriptionID( 0 ), ShortDescriptionID( 0 ), HUDPriority( 0 ), Age( 0 ) { } Objective::~Objective( void ) { Set_Object( NULL ); } int Objective::Radar_Blip_Color_Type( void ) { return Type - ObjectiveManager::TYPE_PRIMARY + RadarManager::BLIP_COLOR_TYPE_PRIMARY_OBJECTIVE; } void Objective::Set_Object( PhysicalGameObj * object ) { PhysicalGameObj * obj = (PhysicalGameObj*)Object.Get_Ptr(); if ( obj != NULL ) { obj->Reset_Radar_Blip_Shape_Type(); obj->Reset_Radar_Blip_Color_Type(); } Object = object; Update_Object_Blip(); } void Objective::Update_Object_Blip( void ) { PhysicalGameObj * obj = (PhysicalGameObj*)Object.Get_Ptr(); if ( obj != NULL ) { if ( Status == ObjectiveManager::STATUS_IS_PENDING ) { obj->Set_Radar_Blip_Shape_Type( RadarManager::BLIP_SHAPE_TYPE_OBJECTIVE ); obj->Set_Radar_Blip_Color_Type( Radar_Blip_Color_Type() ); } else { obj->Reset_Radar_Blip_Shape_Type(); obj->Reset_Radar_Blip_Color_Type(); } } } Vector3 Objective::Get_Position( void ) { if ( Object.Get_Ptr() != NULL ) { Vector3 pos; Object.Get_Ptr()->Get_Position( &pos ); return pos; } return Position; } /* ** */ enum { CHUNKID_VARIABLES = 629001440, CHUNKID_OBJECT, MICROCHUNKID_ID = 1, MICROCHUNKID_TYPE, MICROCHUNKID_STATUS, MICROCHUNKID_DESCRIPTION, MICROCHUNKID_DESCRIPTION_SOUND, XXXMICROCHUNKID_RADAR_MARKER_ID, MICROCHUNKID_DESCRIPTION_ID, MICROCHUNKID_DRAW_BLIP, MICROCHUNKID_POSITION, MICROCHUNKID_LONG_DESCRIPTION_ID, MICROCHUNKID_AGE, MICROCHUNKID_HUD_POG_TEXTURE_NAME, MICROCHUNKID_HUD_MESSAGE_STRING_ID, MICROCHUNKID_HUD_PRIORITY, MICROCHUNKID_HUD_AGE, }; bool Objective::Save( ChunkSaveClass & csave ) { csave.Begin_Chunk( CHUNKID_VARIABLES ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ID, ID ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TYPE, Type ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATUS, Status ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DESCRIPTION_ID, ShortDescriptionID ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LONG_DESCRIPTION_ID, LongDescriptionID ); WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DESCRIPTION_SOUND, DescriptionSoundFilename ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DRAW_BLIP, DrawBlip ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_POSITION, Position ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_AGE, Age ); WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_HUD_POG_TEXTURE_NAME, HUDPogTextureName ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HUD_MESSAGE_STRING_ID, HUDMessageStringID ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HUD_PRIORITY, HUDPriority ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HUD_AGE, Age ); csave.End_Chunk(); if ( Object.Get_Ptr() != NULL ) { csave.Begin_Chunk( CHUNKID_OBJECT ); Object.Save( csave ); csave.End_Chunk(); } return true; } bool Objective::Load( ChunkLoadClass &cload ) { while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_ID, ID ); READ_MICRO_CHUNK( cload, MICROCHUNKID_TYPE, Type ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STATUS, Status ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DESCRIPTION_ID, ShortDescriptionID ); READ_MICRO_CHUNK( cload, MICROCHUNKID_LONG_DESCRIPTION_ID, LongDescriptionID ); READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_DESCRIPTION_SOUND, DescriptionSoundFilename ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DRAW_BLIP, DrawBlip ); READ_MICRO_CHUNK( cload, MICROCHUNKID_POSITION, Position ); READ_MICRO_CHUNK( cload, MICROCHUNKID_AGE, Age ); READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_HUD_POG_TEXTURE_NAME, HUDPogTextureName ); READ_MICRO_CHUNK( cload, MICROCHUNKID_HUD_MESSAGE_STRING_ID, HUDMessageStringID ); READ_MICRO_CHUNK( cload, MICROCHUNKID_HUD_PRIORITY, HUDPriority ); READ_MICRO_CHUNK( cload, MICROCHUNKID_HUD_AGE, Age ); default: Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Micro_Chunk(); } break; case CHUNKID_OBJECT: Object.Load( cload ); break; default: Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Chunk(); } return true; } /* ** */ Objective * ObjectiveManager::Add_Loadable_Objective( void ) { Objective * objective = new Objective(); ObjectiveList.Add( objective ); return objective; } Objective * ObjectiveManager::Find_Objective( int id ) { for ( int i = 0; i < ObjectiveList.Count(); i++ ) { if ( ObjectiveList[i]->ID == id ) { return ObjectiveList[i]; } } return NULL; } /* ** */ void ObjectiveManager::Init( void ) { Viewer.Initialize(); HUDUpdate = true; NumSpecifiedTertiaryObjectives = 0; } void ObjectiveManager::Shutdown( void ) { Viewer.Shutdown(); Reset(); } void ObjectiveManager::Reset( void ) { HUDUpdate = true; while ( ObjectiveList.Count() != 0 ) { Objective * objective = ObjectiveList[0]; ObjectiveList.Delete( 0 ); delete objective; } } /* ** */ enum { CHUNKID_OBJECTIVE_ENTRY = 629001432, CHUNKID_MANAGER_VARIABLES, MICROCHUNKID_NUM_SPECIFIED_TERTIARY_OBJECTIVES = 1, }; /* ** */ bool ObjectiveManager::Save( ChunkSaveClass &csave ) { for ( int i = 0; i < ObjectiveList.Count(); i++ ) { csave.Begin_Chunk( CHUNKID_OBJECTIVE_ENTRY ); ObjectiveList[i]->Save( csave ); csave.End_Chunk(); } csave.Begin_Chunk( CHUNKID_MANAGER_VARIABLES ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_NUM_SPECIFIED_TERTIARY_OBJECTIVES, NumSpecifiedTertiaryObjectives ); csave.End_Chunk(); return true; } bool ObjectiveManager::Load( ChunkLoadClass &cload ) { WWASSERT( ObjectiveList.Count() == 0 ); while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_MANAGER_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_NUM_SPECIFIED_TERTIARY_OBJECTIVES, NumSpecifiedTertiaryObjectives ); default: Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Micro_Chunk(); } break; case CHUNKID_OBJECTIVE_ENTRY: { Objective * objective = Add_Loadable_Objective(); objective->Load( cload ); break; } default: Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Chunk(); } Viewer.Update (); HUDUpdate = true; return true; } /* ** */ void ObjectiveManager::Add_Objective( int id, int type, int status, int short_description_id, int long_description_id, char * description_sound_filename ) { if ( Find_Objective( id ) != NULL ) { Debug_Say(( "Adding a duplicate Objective ID\n" )); return; } Objective * objective = Add_Loadable_Objective(); objective->ID = id; objective->Type = type; objective->Status = status; objective->ShortDescriptionID = short_description_id; objective->LongDescriptionID = long_description_id; objective->DescriptionSoundFilename = description_sound_filename; #if 01 // // Update our EVA message window // if ( status != ObjectiveManager::STATUS_HIDDEN ) { WideStringClass message; WideStringClass description = TranslateDBClass::Get_String( short_description_id ); message.Format( TRANSLATE (IDS_OBJ_NEW_OBJ), objective->Type_To_Name(), description ); CombatManager::Get_Message_Window ()->Add_Message (message, objective->Type_To_Base_Color()); HUDClass::Add_Objective( type ); } const char * preset_name = NULL; switch ( type ) { case ObjectiveManager::TYPE_PRIMARY: preset_name = "EVA_Primary_Add"; break; case ObjectiveManager::TYPE_SECONDARY: preset_name = "EVA_Secondary_Add"; break; } if ( preset_name ) { // WWAudioClass::Get_Instance()->Create_Instant_Sound( preset_name, Matrix3D(1) ); } #endif Viewer.Update (); HUDUpdate = true; return ; } void ObjectiveManager::Remove_Objective( int id ) { Objective * objective = Find_Objective( id ); if ( objective != NULL ) { #if 01 WideStringClass message; message.Format( TRANSLATE (IDS_OBJ_CANCELLED), objective->Type_To_Name () ); CombatManager::Get_Message_Window ()->Add_Message( message, objective->Type_To_Base_Color() ); // WWAudioClass::Get_Instance()->Create_Instant_Sound( "EVA_Complete", Matrix3D(1) ); #endif ObjectiveList.Delete( objective ); delete objective; } else { Debug_Say(( "Objective not found to delete\n" )); } Viewer.Update (); HUDUpdate = true; return ; } void ObjectiveManager::Set_Objective_Status( int id, int status ) { Objective * objective = Find_Objective( id ); if ( objective != NULL ) { bool is_unhiding = (objective->Status == ObjectiveManager::STATUS_HIDDEN) && (status != ObjectiveManager::STATUS_HIDDEN); objective->Status = status; if ( status == STATUS_FAILED ) { DIAG_LOG(( "OBFA", "%d; %s", id, objective->Status_To_Name() )); } if ( status == STATUS_ACCOMPLISHED ) { DIAG_LOG(( "OBCO", "%d; %s", id, objective->Status_To_Name() )); } objective->Update_Object_Blip(); if ( is_unhiding ) { objective->Age = 0; // Reset age } #if 01 // // Special case changing an objective from hidden to pending... (Note: for // a completed objective, we display the normal message even if it was hidden). // WideStringClass message; if (is_unhiding && (status != ObjectiveManager::STATUS_ACCOMPLISHED)) { WideStringClass description = TranslateDBClass::Get_String( objective->ShortDescriptionID ); message.Format( TRANSLATE (IDS_OBJ_NEW_OBJ), objective->Type_To_Name(), description ); HUDClass::Add_Objective( objective->Type ); } else { if ( objective->Status != ObjectiveManager::STATUS_HIDDEN ) { message.Format( TRANSLATE (IDS_OBJ_STATUS_CHANGED), objective->Type_To_Name(), objective->Status_To_Name() ); } } CombatManager::Get_Message_Window ()->Add_Message( message, objective->Type_To_Base_Color() ); #endif } else { Debug_Say(( "Objective not found to set status\n" )); } Sort_Objectives(); Viewer.Update (); HUDUpdate = true; return ; } void ObjectiveManager::Change_Objective_Type( int id, int type ) { Objective * objective = Find_Objective( id ); if ( objective != NULL ) { objective->Type = type; objective->Update_Object_Blip(); //DebugManager::Display_Text( "Mission objective priority changed\n", objective->Type_To_Color () ); } else { Debug_Say(( "Objective not found to change type\n" )); } Viewer.Update (); HUDUpdate = true; return ; } void ObjectiveManager::Set_Objective_Radar_Blip( int id, Vector3 position ) { Objective * objective = Find_Objective( id ); if ( objective != NULL ) { objective->Set_Object( NULL ); objective->Position = position; objective->DrawBlip = true; } else { Debug_Say(( "Objective not found to set_radar_blip\n" )); } } void ObjectiveManager::Set_Objective_Radar_Blip( int id, PhysicalGameObj * object ) { Objective * objective = Find_Objective( id ); if ( objective != NULL ) { objective->Set_Object( object ); } else { Debug_Say(( "Objective not found to set_radar_blip\n" )); } } //////////////////////////////////////////////////////////////// // // fnCompareObjectivesCallback // //////////////////////////////////////////////////////////////// int __cdecl ObjectiveManager::ObjectiveSortCallback( const void *elem1, const void *elem2 ) { WWASSERT (elem1 != NULL); WWASSERT (elem2 != NULL); Objective *objective1 = *((Objective **)elem1); Objective *objective2 = *((Objective **)elem2); // Sort first on status, low first if (objective1->Status < objective2->Status) { return -1; } if (objective1->Status > objective2->Status) { return 1; } // Sort Next on Priority, high first if (objective1->HUDPriority > objective2->HUDPriority) { return -1; } if (objective1->HUDPriority < objective2->HUDPriority) { return 1; } return 0; } void ObjectiveManager::Sort_Objectives( void ) { // Assume the rest of the list is sorted // And bubble him to where he belongs if ( ObjectiveList.Count() != 0 ) { ::qsort (&ObjectiveList[0], ObjectiveList.Count(), sizeof(ObjectiveList[0]), ObjectiveSortCallback ); } } void ObjectiveManager::Set_Objective_HUD_Info( int id, float priority, const char * texture_name, int message_id ) { Objective * objective = Find_Objective( id ); if ( objective != NULL ) { objective->HUDPriority = priority; objective->HUDPogTextureName = texture_name; objective->HUDMessageStringID = message_id; Sort_Objectives(); HUDUpdate = true; } else { Debug_Say(( "Objective not found to Set_Objective_HUD_Info\n" )); } } void ObjectiveManager::Set_Objective_HUD_Info( int id, float priority, const char * texture_name, int message_id, const Vector3 & position ) { Objective * objective = Find_Objective( id ); if ( objective != NULL ) { objective->HUDPriority = priority; objective->HUDPogTextureName = texture_name; objective->HUDMessageStringID = message_id; objective->Position = position; Sort_Objectives(); HUDUpdate = true; } else { Debug_Say(( "Objective not found to Set_Objective_HUD_Info\n" )); } } int ObjectiveManager::Get_Num_HUD_Objectives( void ) { // Assume the pendings are first, in priority order int count = 0; for ( int i = 0; i < ObjectiveList.Count(); i++ ) { if ( ( ObjectiveList[i]->Status == STATUS_IS_PENDING ) && ( ObjectiveList[i]->HUDPriority > 0 ) ) { count++; } } return count; } const char * ObjectiveManager::Get_HUD_Objectives_Pog_Texture_Name( int index ) { return ObjectiveList[index]->HUDPogTextureName; } const WCHAR * ObjectiveManager::Get_HUD_Objectives_Message( int index ) { return TranslateDBClass::Get_String( ObjectiveList[index]->HUDMessageStringID ); } Vector3 ObjectiveManager::Get_HUD_Objectives_Location( int index ) { return ObjectiveList[index]->Get_Position(); } float ObjectiveManager::Get_HUD_Objectives_Age( int index ) { return ObjectiveList[index]->Age; } int ObjectiveManager::Get_Num_Objectives( int type ) { int count = 0; for ( int i = 0; i < ObjectiveList.Count(); i++ ) { if ( ( ObjectiveList[i]->Type == type ) && ( ObjectiveList[i]->Status != STATUS_HIDDEN ) ) { count++; } } if ( type == TYPE_TERTIARY ) { if ( count < NumSpecifiedTertiaryObjectives ) { count = NumSpecifiedTertiaryObjectives; } } return count; } int ObjectiveManager::Get_Num_Completed_Objectives( int type ) { int count = 0; for ( int i = 0; i < ObjectiveList.Count(); i++ ) { if ( ( ObjectiveList[i]->Type == type ) && ( ObjectiveList[i]->Status == STATUS_ACCOMPLISHED ) ) { count++; } } return count; } void ObjectiveManager::Update( float dt ) { for ( int i = 0; i < ObjectiveList.Count(); i++ ) { if ( ObjectiveList[i]->Status != STATUS_HIDDEN ) { ObjectiveList[i]->Age += dt; } } }