/*
** 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;
}
}
}