490 lines
13 KiB
C++
490 lines
13 KiB
C++
|
/*
|
||
|
** 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/commando/dlgevaviewertab.cpp $*
|
||
|
* *
|
||
|
* Author:: Patrick Smith *
|
||
|
* *
|
||
|
* $Modtime:: 1/07/02 6:12p $*
|
||
|
* *
|
||
|
* $Revision:: 17 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
#include "dlgevaviewertab.h"
|
||
|
|
||
|
#include "resource.h"
|
||
|
#include "mapctrl.h"
|
||
|
#include "combat.h"
|
||
|
#include "soldier.h"
|
||
|
#include "objectives.h"
|
||
|
#include "translatedb.h"
|
||
|
#include "translateobj.h"
|
||
|
#include "listctrl.h"
|
||
|
#include "dialogtext.h"
|
||
|
#include "assets.h"
|
||
|
#include "ini.h"
|
||
|
#include "inisup.h"
|
||
|
#include "listnode.h"
|
||
|
#include "viewerctrl.h"
|
||
|
#include "rendobj.h"
|
||
|
#include "wheelvehicle.h"
|
||
|
#include "string_ids.h"
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// EvaViewerTabClass
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
EvaViewerTabClass::EvaViewerTabClass (int resource_id) :
|
||
|
ListCtrl (NULL),
|
||
|
DescriptionCtrl (NULL),
|
||
|
AffiliationCtrl (NULL),
|
||
|
ViewerCtrl (NULL),
|
||
|
EncyclopediaType (EncyclopediaMgrClass::TYPE_CHARACTER),
|
||
|
ChildDialogClass (resource_id)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// On_Init_Dialog
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
void
|
||
|
EvaViewerTabClass::On_Init_Dialog (void)
|
||
|
{
|
||
|
//
|
||
|
// Generate a set of data to display to the user (from an INI)
|
||
|
//
|
||
|
Build_Object_List ();
|
||
|
|
||
|
//
|
||
|
// Configure the list control
|
||
|
//
|
||
|
ListCtrl->Add_Column (TRANSLATE (IDS_MENU_NAME), 1.0F, Vector3 (1, 1, 1));
|
||
|
|
||
|
//
|
||
|
// Add this data to the list control
|
||
|
//
|
||
|
int total_obj_count = 0;
|
||
|
for (int index = 0; index < ObjectList.Count (); index ++) {
|
||
|
EvaViewerObjectClass &object = ObjectList[index];
|
||
|
|
||
|
//
|
||
|
// Check to see if this entry should be displayed
|
||
|
//
|
||
|
if (EncyclopediaMgrClass::Is_Object_Revealed (EncyclopediaType, object.Get_ID ())) {
|
||
|
|
||
|
//
|
||
|
// Add an entry to the list control for this object
|
||
|
//
|
||
|
int item_index = ListCtrl->Insert_Entry (index, object.Get_Name ());
|
||
|
if (item_index >= 0) {
|
||
|
ListCtrl->Set_Entry_Data (item_index, 0, (uint32)&object);
|
||
|
|
||
|
//
|
||
|
// Colorize the entry
|
||
|
//
|
||
|
Vector3 color (1.0F, 1.0F, 1.0F);
|
||
|
if (object.Get_Player_Type () == PLAYERTYPE_GDI) {
|
||
|
color.Set (1.0F, 0.832F, 0.156F);
|
||
|
} else if (object.Get_Player_Type () == PLAYERTYPE_NOD) {
|
||
|
color.Set (1.0F, 0.0F, 0.0F);
|
||
|
}
|
||
|
ListCtrl->Set_Entry_Color (item_index, 0, color);
|
||
|
|
||
|
total_obj_count ++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now, sort the players by rank
|
||
|
//
|
||
|
ListCtrl->Sort (ListSortCallback, 0);
|
||
|
|
||
|
//
|
||
|
// Select and view the first entry by default
|
||
|
//
|
||
|
if (total_obj_count > 0) {
|
||
|
ListCtrl->Set_Curr_Sel (0);
|
||
|
View_Entry (0);
|
||
|
} else {
|
||
|
|
||
|
if (AffiliationCtrl != NULL) {
|
||
|
AffiliationCtrl->Set_Text (L"");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ChildDialogClass::On_Init_Dialog ();
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// On_Destroy_Dialog
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
void
|
||
|
EvaViewerTabClass::On_Destroy_Dialog (void)
|
||
|
{
|
||
|
if (ViewerCtrl != NULL) {
|
||
|
ViewerCtrl->Set_Model ((RenderObjClass *)NULL);
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Build_Object_List
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
void
|
||
|
EvaViewerTabClass::Build_Object_List (void)
|
||
|
{
|
||
|
ObjectList.Delete_All ();
|
||
|
|
||
|
//
|
||
|
// Get the INI file which contains the data for this viewer
|
||
|
//
|
||
|
INIClass *ini_file = ::Get_INI (INIFilename);
|
||
|
if (ini_file != NULL) {
|
||
|
|
||
|
//
|
||
|
// Loop over all the sections in the INI
|
||
|
//
|
||
|
List<INISection *> §ion_list = ini_file->Get_Section_List ();
|
||
|
for ( INISection *section = section_list.First ();
|
||
|
section != NULL;
|
||
|
section = section->Next_Valid ())
|
||
|
{
|
||
|
//
|
||
|
// Read this object's data from the INI file
|
||
|
//
|
||
|
StringClass name_id (0, true);
|
||
|
StringClass description_id (0, true);
|
||
|
StringClass affiliation_id (0, true);
|
||
|
StringClass model_name (0, true);
|
||
|
StringClass anim_name (0, true);
|
||
|
StringClass definition_name (0, true);
|
||
|
StringClass player_type_str (0, true);
|
||
|
ini_file->Get_String (name_id, section->Section, "NameID");
|
||
|
ini_file->Get_String (description_id, section->Section, "DescriptionID");
|
||
|
ini_file->Get_String (affiliation_id, section->Section, "AffiliationID");
|
||
|
ini_file->Get_String (model_name, section->Section, "Model");
|
||
|
ini_file->Get_String (anim_name, section->Section, "Anim");
|
||
|
ini_file->Get_String (definition_name, section->Section, "DefinitionName");
|
||
|
ini_file->Get_String (player_type_str, section->Section, "PlayerType");
|
||
|
int id = ini_file->Get_Int (section->Section, "ID");
|
||
|
float min_dist = ini_file->Get_Float (section->Section, "MinDist");
|
||
|
|
||
|
//
|
||
|
// Determine the player type
|
||
|
//
|
||
|
int player_type = PLAYERTYPE_NEUTRAL;
|
||
|
if (player_type_str.Compare_No_Case ("GDI") == 0) {
|
||
|
player_type = PLAYERTYPE_GDI;
|
||
|
} else if (player_type_str.Compare_No_Case ("NOD") == 0) {
|
||
|
player_type = PLAYERTYPE_NOD;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Translate the data
|
||
|
//
|
||
|
const WCHAR *name = TRANSLATE_BY_DESC(name_id);
|
||
|
const WCHAR *description = TRANSLATE_BY_DESC(description_id);
|
||
|
const WCHAR *affiliation = TRANSLATE_BY_DESC(affiliation_id);
|
||
|
|
||
|
//
|
||
|
// Configure an object that will hold this data for us
|
||
|
//
|
||
|
EvaViewerObjectClass object;
|
||
|
object.Set_ID (id);
|
||
|
object.Set_Name (name);
|
||
|
object.Set_Description (description);
|
||
|
object.Set_Affiliation (affiliation);
|
||
|
object.Set_Model_Name (model_name);
|
||
|
object.Set_Anim_Name (anim_name);
|
||
|
object.Set_Definition_Name (definition_name);
|
||
|
object.Set_Min_Distance (min_dist);
|
||
|
object.Set_Player_Type (player_type);
|
||
|
|
||
|
//
|
||
|
// Add this object to the list
|
||
|
//
|
||
|
ObjectList.Add (object);
|
||
|
}
|
||
|
delete ini_file;
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// On_ListCtrl_Sel_Change
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
void
|
||
|
EvaViewerTabClass::On_ListCtrl_Sel_Change
|
||
|
(
|
||
|
ListCtrlClass * list_ctrl,
|
||
|
int ctrl_id,
|
||
|
int old_index,
|
||
|
int new_index
|
||
|
)
|
||
|
{
|
||
|
View_Entry (new_index);
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// View_Entry
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
void
|
||
|
EvaViewerTabClass::View_Entry (int entry_index)
|
||
|
{
|
||
|
WideStringClass name;
|
||
|
WideStringClass description;
|
||
|
WideStringClass affiliation;
|
||
|
StringClass model_name;
|
||
|
StringClass anim_name;
|
||
|
StringClass definition_name;
|
||
|
float min_dist = 0.0F;
|
||
|
|
||
|
//
|
||
|
// Is there new data to display?
|
||
|
//
|
||
|
if (entry_index >= 0) {
|
||
|
|
||
|
//
|
||
|
// Load the data for this selection
|
||
|
//
|
||
|
EvaViewerObjectClass *object = (EvaViewerObjectClass *)ListCtrl->Get_Entry_Data (entry_index, 0);
|
||
|
name = object->Get_Name ();
|
||
|
description = object->Get_Description ();
|
||
|
affiliation = object->Get_Affiliation ();
|
||
|
model_name = object->Get_Model_Name ();
|
||
|
anim_name = object->Get_Anim_Name ();
|
||
|
min_dist = object->Get_Min_Distance ();
|
||
|
definition_name = object->Get_Definition_Name ();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Display the data
|
||
|
//
|
||
|
if (DescriptionCtrl != NULL) {
|
||
|
DescriptionCtrl->Set_Text (description);
|
||
|
}
|
||
|
|
||
|
if (AffiliationCtrl != NULL) {
|
||
|
AffiliationCtrl->Set_Text (affiliation);
|
||
|
}
|
||
|
|
||
|
if (ViewerCtrl != NULL) {
|
||
|
ViewerCtrl->Set_Camera_Min_Dist (min_dist);
|
||
|
|
||
|
//
|
||
|
// Special-case vehicles so we can get the wheels to "drop"
|
||
|
//
|
||
|
if (EncyclopediaType == EncyclopediaMgrClass::TYPE_VEHICLE) {
|
||
|
|
||
|
RenderObjClass *new_model = NULL;
|
||
|
|
||
|
//
|
||
|
// Try to find the definition that this vehicle is defined by
|
||
|
//
|
||
|
DefinitionClass *definition = DefinitionMgrClass::Find_Typed_Definition (definition_name, CLASSID_GAME_OBJECTS);
|
||
|
if (definition != NULL) {
|
||
|
|
||
|
PhysicalGameObj *game_obj = (PhysicalGameObj *)definition->Create ();
|
||
|
if (game_obj != NULL) {
|
||
|
|
||
|
//
|
||
|
// Is the physics object associated with this game object a vehicle?
|
||
|
//
|
||
|
PhysClass *phys_obj = game_obj->Peek_Physical_Object ();
|
||
|
if (phys_obj != NULL && phys_obj->As_VehiclePhysClass () != NULL) {
|
||
|
|
||
|
//
|
||
|
// Drop the wheels on this object and clone its model
|
||
|
//
|
||
|
phys_obj->As_VehiclePhysClass ()->Non_Physical_Wheel_Update (0.7F, 0);
|
||
|
new_model = phys_obj->Peek_Model ()->Clone ();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Delete the game object
|
||
|
//
|
||
|
game_obj->Set_Delete_Pending ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the model we dug out of the definition, or
|
||
|
// simply create the object from its W3D
|
||
|
//
|
||
|
if (new_model != NULL) {
|
||
|
ViewerCtrl->Set_Model (new_model);
|
||
|
} else {
|
||
|
ViewerCtrl->Set_Model (model_name);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
ViewerCtrl->Set_Model (model_name);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Play the animation on the object
|
||
|
//
|
||
|
ViewerCtrl->Set_Animation (anim_name);
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// On_ViewerCtrl_Model_Loaded
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
void
|
||
|
EvaViewerTabClass::On_ViewerCtrl_Model_Loaded
|
||
|
(
|
||
|
ViewerCtrlClass * viewer_ctrl,
|
||
|
int ctrl_id,
|
||
|
RenderObjClass * model
|
||
|
)
|
||
|
{
|
||
|
if (model != NULL) {
|
||
|
Prepare_Model (model);
|
||
|
|
||
|
//
|
||
|
// Start the image rotating again...
|
||
|
//
|
||
|
ViewerCtrl->Set_Interface_Mode (ViewerCtrlClass::Z_ROTATION, 45.0F);
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Prepare_Model
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
void
|
||
|
EvaViewerTabClass::Prepare_Model (RenderObjClass *model)
|
||
|
{
|
||
|
if (model != NULL) {
|
||
|
|
||
|
//
|
||
|
// Attempt to hide the muzzle flashes
|
||
|
//
|
||
|
for (int index = 0; index < model->Get_Num_Sub_Objects(); index ++) {
|
||
|
RenderObjClass *sub_obj = model->Get_Sub_Object (index);
|
||
|
|
||
|
//
|
||
|
// Hide the muzzle flash (if necessary)
|
||
|
//
|
||
|
if (::strstr (sub_obj->Get_Name (),"MUZZLEFLASH") != NULL) {
|
||
|
sub_obj->Set_Hidden (true);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Recurse into this sub-object
|
||
|
//
|
||
|
Prepare_Model (sub_obj);
|
||
|
|
||
|
REF_PTR_RELEASE (sub_obj);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// ListSortCallback
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
int CALLBACK
|
||
|
EvaViewerTabClass::ListSortCallback
|
||
|
(
|
||
|
ListCtrlClass * list_ctrl,
|
||
|
int item_index1,
|
||
|
int item_index2,
|
||
|
uint32 user_param
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Lookup the data associated with these entries
|
||
|
//
|
||
|
EvaViewerObjectClass *object1 = (EvaViewerObjectClass *)list_ctrl->Get_Entry_Data (item_index1, 0);
|
||
|
EvaViewerObjectClass *object2 = (EvaViewerObjectClass *)list_ctrl->Get_Entry_Data (item_index2, 0);
|
||
|
|
||
|
int player_type1 = object1->Get_Player_Type ();
|
||
|
int player_type2 = object2->Get_Player_Type ();
|
||
|
int result = 0;
|
||
|
|
||
|
//
|
||
|
// Sort alphatically if the types are the same
|
||
|
//
|
||
|
if (player_type1 == player_type2) {
|
||
|
result = ::wcsicmp (object1->Get_Name (), object2->Get_Name ());
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Sort by type...
|
||
|
//
|
||
|
if (player_type1 == PLAYERTYPE_NEUTRAL) {
|
||
|
result = -1;
|
||
|
} else if (player_type2 == PLAYERTYPE_NEUTRAL) {
|
||
|
result = 1;
|
||
|
} else if (player_type1 == PLAYERTYPE_GDI) {
|
||
|
result = -1;
|
||
|
} else if (player_type2 == PLAYERTYPE_GDI) {
|
||
|
result = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|