This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Combat/textwindow.cpp

1012 lines
24 KiB
C++
Raw Permalink Normal View History

/*
** 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/textwindow.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 11/06/01 11:05a $*
* *
* $Revision:: 19 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "textwindow.h"
#include "render2dsentence.h"
#include "assetmgr.h"
#include "texture.h"
#include "font3d.h"
#include "objectives.h"
#include "translatedb.h"
#include "wwaudio.h"
#include "globalsettings.h"
#include "scene.h"
#include "rendobj.h"
#include "stylemgr.h"
////////////////////////////////////////////////////////////////
// Static member initialization
////////////////////////////////////////////////////////////////
SceneClass *TextWindowClass::Scene = NULL;
////////////////////////////////////////////////////////////////
// Local constants
////////////////////////////////////////////////////////////////
static const char * FONT_NAME = "Arial MT";
////////////////////////////////////////////////////////////////
//
// TextWindowClass
//
////////////////////////////////////////////////////////////////
TextWindowClass::TextWindowClass (void) :
FirstLineIndex (0),
CurrentDisplayCount (0),
IsDisplayed (false),
AreColumnsDisplayed (false),
IsViewDirty (true),
TextRect (0, 0, 0, 0),
ColumnHeight (0),
LineSpacing (0)
{
TextRenderers[0] = NULL;
TextRenderers[1] = NULL;
return ;
}
////////////////////////////////////////////////////////////////
//
// ~TextWindowClass
//
////////////////////////////////////////////////////////////////
TextWindowClass::~TextWindowClass (void)
{
Free_Backdrop ();
Free_Contents ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Initialize
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Initialize (SceneClass *scene)
{
REF_PTR_SET (Scene, scene);
return ;
}
////////////////////////////////////////////////////////////////
//
// Shutdown
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Shutdown (void)
{
REF_PTR_RELEASE (Scene);
return ;
}
////////////////////////////////////////////////////////////////
//
// Free_Backdrop
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Free_Backdrop (void)
{
//
// Reset the rest of the data that is dependent on the backdrop
//
FirstLineIndex = 0;
IsDisplayed = false;
return ;
}
////////////////////////////////////////////////////////////////
//
// Free_Contents
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Free_Contents (void)
{
//
// Free each row of renderer's
//
Delete_All_Columns ();
Free_Renderers ();
//
// Reset the rest of the data that is dependent on the contents
//
FirstLineIndex = 0;
IsDisplayed = false;
return ;
}
////////////////////////////////////////////////////////////////
//
// Free_Renderers
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Free_Renderers (void)
{
//
// Free each text renderer object
//
delete TextRenderers[0];
delete TextRenderers[1];
TextRenderers[0] = NULL;
TextRenderers[1] = NULL;
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Backdrop
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Set_Backdrop
(
const char * texture_name,
const RectClass & screen_rect,
const Vector2 & texture_size,
const RectClass & endcap_rect,
const RectClass & fadeout_rect,
const RectClass & textback_rect
)
{
Free_Backdrop ();
//
// Load the backdrop texture
//
TextureClass *texture = WW3DAssetManager::Get_Instance()->Get_Texture (texture_name, TextureClass::MIP_LEVELS_1);
if (texture != NULL) {
//
// Pass the texture onto the renderer
//
Backdrop.Reset ();
Backdrop.Set_Texture (texture);
//
// Configure the renderer
//
Backdrop.Set_Coordinate_Range (Render2DClass::Get_Screen_Resolution ());
//
// Calculate the width and height of the sections
//
int endcap_width = endcap_rect.Width ();
int fadeout_width = fadeout_rect.Width ();
int textback_width = screen_rect.Width () - ((endcap_width + fadeout_width) * 2);
int endcap_height = endcap_rect.Height ();
int fadeout_height = fadeout_rect.Height ();
int textback_height = textback_rect.Height ();
//
// Calculate the UV coordinates for each section
//
RectClass endcap_uvs1 (endcap_rect);
RectClass endcap_uvs2 (endcap_rect);
RectClass fadeout_uvs1 (fadeout_rect);
RectClass fadeout_uvs2 (fadeout_rect);
RectClass textback_uvs (textback_rect);
endcap_uvs1.Inverse_Scale (texture_size);
endcap_uvs2.Inverse_Scale (texture_size);
fadeout_uvs1.Inverse_Scale (texture_size);
fadeout_uvs2.Inverse_Scale (texture_size);
textback_uvs.Inverse_Scale (texture_size);
//
// Reverse the UVs of the endcap and fadeaways on the right
//
float temp_float = endcap_uvs2.Left;
endcap_uvs2.Left = endcap_uvs2.Right;
endcap_uvs2.Right = temp_float;
temp_float = fadeout_uvs2.Left;
fadeout_uvs2.Left = fadeout_uvs2.Right;
fadeout_uvs2.Right = temp_float;
//
// Calculate the screen rectangle (in pixels) for each of the sections
//
RectClass endcap_screen_rect1 (0, 0, endcap_width, endcap_height);
RectClass fadeout_screen_rect1 (0, 0, fadeout_width, fadeout_height);
RectClass textback_screen_rect (0, 0, textback_width, textback_height);
RectClass fadeout_screen_rect2 (0, 0, fadeout_width, fadeout_height);
RectClass endcap_screen_rect2 (0, 0, endcap_width, endcap_height);
endcap_screen_rect1 += screen_rect.Upper_Left ();
fadeout_screen_rect1 += endcap_screen_rect1.Upper_Right ();
textback_screen_rect += fadeout_screen_rect1.Upper_Right ();
fadeout_screen_rect2 += textback_screen_rect.Upper_Right ();
endcap_screen_rect2 += fadeout_screen_rect2.Upper_Right ();
//
// Determine what area we have to draw the text into
//
const float border_size = 10.0F;
TextRect = textback_screen_rect;
TextRect.Inflate (Vector2 (-border_size, -border_size));
//
// Calculate how many times we should vertically tile the text backdrop
//
int largest_height = min (endcap_height, fadeout_height);
largest_height = min (largest_height, textback_height);
int section_count = (screen_rect.Height () / largest_height);
//
// Tile the text backdrop sections
//
for (int index = 0; index < section_count; index ++) {
//
// Submit the geometry
//
Backdrop.Add_Quad (fadeout_screen_rect1, fadeout_uvs1);
Backdrop.Add_Quad (fadeout_screen_rect2, fadeout_uvs2);
Backdrop.Add_Quad (textback_screen_rect, textback_uvs);
fadeout_screen_rect1 += Vector2 (0, fadeout_height);
fadeout_screen_rect2 += Vector2 (0, fadeout_height);
textback_screen_rect += Vector2 (0, textback_height);
}
//
// Add a chopped off section of an endcap onto the end (if necessary)
//
float percent = (screen_rect.Height () / largest_height) - section_count;
if (percent > 0) {
textback_uvs.Bottom = textback_uvs.Top + (textback_uvs.Height () * percent);
fadeout_uvs1.Bottom = fadeout_uvs1.Top + (fadeout_uvs1.Height () * percent);
fadeout_uvs2.Bottom = fadeout_uvs2.Top + (fadeout_uvs2.Height () * percent);
textback_screen_rect.Bottom = textback_screen_rect.Top + (textback_screen_rect.Height () * percent);
fadeout_screen_rect1.Bottom = fadeout_screen_rect1.Top + (fadeout_screen_rect1.Height () * percent);
fadeout_screen_rect2.Bottom = fadeout_screen_rect2.Top + (fadeout_screen_rect2.Height () * percent);
//
// Submit the geometry
//
Backdrop.Add_Quad (fadeout_screen_rect1, fadeout_uvs1);
Backdrop.Add_Quad (fadeout_screen_rect2, fadeout_uvs2);
Backdrop.Add_Quad (textback_screen_rect, textback_uvs);
}
//
// Tile the endcaps appropriately
//
section_count = screen_rect.Height () / endcap_height;
for (index = 0; index < section_count; index ++) {
//
// Submit the geometry
//
Backdrop.Add_Quad (endcap_screen_rect1, endcap_uvs1);
Backdrop.Add_Quad (endcap_screen_rect2, endcap_uvs2);
endcap_screen_rect1 += Vector2 (0, endcap_height);
endcap_screen_rect2 += Vector2 (0, endcap_height);
}
//
// Add a chopped off section of an endcap onto the end (if necessary)
//
percent = (screen_rect.Height () / endcap_height) - section_count;
if (percent > 0) {
endcap_uvs1.Bottom = endcap_uvs1.Top + (endcap_uvs1.Height () * percent);
endcap_uvs2.Bottom = endcap_uvs2.Top + (endcap_uvs2.Height () * percent);
endcap_screen_rect1.Bottom = endcap_screen_rect1.Top + (endcap_screen_rect1.Height () * percent);
endcap_screen_rect2.Bottom = endcap_screen_rect2.Top + (endcap_screen_rect2.Height () * percent);
//
// Submit the geometry
//
Backdrop.Add_Quad (endcap_screen_rect1, endcap_uvs1);
Backdrop.Add_Quad (endcap_screen_rect2, endcap_uvs2);
}
//
// Release our hold on the texture
//
REF_PTR_RELEASE(texture);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Frame_Update
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::On_Frame_Update (void)
{
return ;
}
////////////////////////////////////////////////////////////////
//
// Add_Column
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Add_Column (const WCHAR *column_name, float width, const Vector3 &color)
{
//
// Create a new column and add it to the list
//
TextColumnClass *column = new TextColumnClass;
column->Set_Name (column_name);
column->Set_Width (width);
column->Set_Color (color);
Columns.Add (column);
IsViewDirty = true;
return ;
}
////////////////////////////////////////////////////////////////
//
// Remove_Column
//
////////////////////////////////////////////////////////////////
bool
TextWindowClass::Remove_Column (int index)
{
if (index < 0 || index >= Columns.Count ()) {
return false;
}
//
// Free the column
//
TextColumnClass *column = Columns[index];
delete column;
//
// Remove the column from the list
//
Columns.Delete (index);
IsViewDirty = true;
return true;
}
////////////////////////////////////////////////////////////////
//
// Delete_All_Columns
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Delete_All_Columns (void)
{
for (int index = 0; index < Columns.Count (); index ++) {
TextColumnClass *column = Columns[index];
delete column;
}
Columns.Delete_All ();
IsViewDirty = true;
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Item_Count
//
////////////////////////////////////////////////////////////////
int
TextWindowClass::Get_Item_Count (void) const
{
return Columns[0]->Get_Item_Count ();
}
////////////////////////////////////////////////////////////////
//
// Get_Display_Count
//
////////////////////////////////////////////////////////////////
int
TextWindowClass::Get_Display_Count (void)
{
if (IsViewDirty) {
Update_View ();
}
return CurrentDisplayCount;
}
////////////////////////////////////////////////////////////////
//
// Delete_Item
//
////////////////////////////////////////////////////////////////
bool
TextWindowClass::Delete_Item (int index)
{
bool retval = true;
for (int col_index = 0; col_index < Columns.Count (); col_index ++) {
retval &= Columns[col_index]->Delete_Item (index);
}
IsViewDirty = true;
return retval;
}
////////////////////////////////////////////////////////////////
//
// Insert_Item
//
////////////////////////////////////////////////////////////////
int
TextWindowClass::Insert_Item (int index, const WCHAR *text)
{
if (Columns.Count () <= 0) {
return -1;
}
//
// Insert a new entry in the first column with the given text
//
index = Columns[0]->Insert_Item (index, text);
//
// Add blank entries to all the other columns
//
for (int col_index = 1; col_index < Columns.Count (); col_index ++) {
TextColumnClass *column = Columns[col_index];
column->Insert_Item (index, L"");
}
IsViewDirty = true;
return index;
}
////////////////////////////////////////////////////////////////
//
// Set_Item_Text
//
////////////////////////////////////////////////////////////////
bool
TextWindowClass::Set_Item_Text (int index, int col_index, const WCHAR *text)
{
if (col_index < 0 || col_index >= Columns.Count ()) {
return false;
}
//
// Change the text entry in this cell
//
Columns[col_index]->Set_Item_Text (index, text);
IsViewDirty = true;
return true;
}
////////////////////////////////////////////////////////////////
//
// Set_Item_Color
//
////////////////////////////////////////////////////////////////
bool
TextWindowClass::Set_Item_Color (int index, int col_index, const Vector3 &color)
{
if (col_index < 0 || col_index >= Columns.Count ()) {
return false;
}
//
// Change the color for the item in this cell
//
Columns[col_index]->Set_Item_Color (index, color);
IsViewDirty = true;
return true;
}
////////////////////////////////////////////////////////////////
//
// Set_Item_Data
//
////////////////////////////////////////////////////////////////
bool
TextWindowClass::Set_Item_Data (int index, uint32 user_data)
{
//
// Store the user data in the first column
//
Columns[0]->Set_Item_Data (index, user_data);
return true;
}
////////////////////////////////////////////////////////////////
//
// Get_Item_Data
//
////////////////////////////////////////////////////////////////
uint32
TextWindowClass::Get_Item_Data (int index)
{
//
// Lookup the user data (its stored in the first column)
//
return Columns[0]->Get_Item_Data (index);
}
////////////////////////////////////////////////////////////////
//
// Delete_All_Items
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Delete_All_Items (void)
{
for (int index = 0; index < Columns.Count (); index ++) {
Columns[index]->Delete_All_Items ();
}
IsViewDirty = true;
return ;
}
////////////////////////////////////////////////////////////////
//
// Build_View
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Build_View (void)
{
//
// Start fresh
//
Free_Renderers ();
//const RectClass &screen_rect = Render2DClass::Get_Screen_Resolution ();
//
// Create the fonts
//
FontCharsClass *header_font = StyleMgrClass::Peek_Font( StyleMgrClass::FONT_INGAME_HEADER_TXT );
FontCharsClass *font = StyleMgrClass::Peek_Font( StyleMgrClass::FONT_INGAME_TXT );
//
// Load the font we'll use for the column headers
//
if (header_font != NULL) {
ColumnHeight = (header_font->Get_Char_Height () * 1.5F);
}
//
// Create the header and body text renderers
//
TextRenderers[0] = new Render2DSentenceClass;
TextRenderers[1] = new Render2DSentenceClass;
TextRenderers[0]->Set_Font (header_font);
TextRenderers[1]->Set_Font (font);
return ;
}
////////////////////////////////////////////////////////////////
//
// Update_View
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Update_View (float *total_height, bool info_only)
{
//
// Ensure the view has been created before we try to update it
//
if (TextRenderers[0] == NULL || TextRenderers[1] == NULL) {
Build_View ();
}
TextRenderers[0]->Reset ();
TextRenderers[1]->Reset ();
//
// Update the column headers
//
if (AreColumnsDisplayed) {
float x_pos = TextRect.Left;
for (int col_index = 0; col_index < Columns.Count (); col_index ++) {
const Vector3 &color = Columns[col_index]->Get_Color ();
TextRenderers[0]->Build_Sentence (Columns[col_index]->Get_Name ());
TextRenderers[0]->Set_Location (Vector2 (x_pos, TextRect.Top));
TextRenderers[0]->Draw_Sentence (VRGB_TO_INT32(color));
x_pos += Columns[col_index]->Get_Width () * (TextRect.Right - TextRect.Left);
}
}
//
// Move past the column headers
//
float y_pos = TextRect.Top;
if (AreColumnsDisplayed) {
y_pos += ColumnHeight;
}
//
// Update the contents
//
CurrentDisplayCount = 0;
int item_count = Columns[0]->Get_Item_Count ();
for (int item_index = FirstLineIndex; item_index < item_count; item_index ++) {
//
// Update this row of information
//
float row_height = 0;
Update_Row (item_index, y_pos, &row_height);
CurrentDisplayCount ++;
//
// Check to see if we've gone outside the client area of the text window
//
if ((y_pos + row_height) > TextRect.Right && info_only == false) {
break;
}
//
// Move down one row
//
y_pos += int(row_height + LineSpacing);
}
if (info_only == false) {
IsViewDirty = false;
}
//
// Return the height to the caller (if necessary)
//
if (total_height != NULL) {
(*total_height) = (y_pos - TextRect.Top);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Total_Display_Height
//
////////////////////////////////////////////////////////////////
float
TextWindowClass::Get_Total_Display_Height (void)
{
float total_height = 0;
Update_View (&total_height, true);
return total_height;
}
////////////////////////////////////////////////////////////////
//
// Update_Row
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Update_Row
(
int item_index,
float y_pos,
float * row_height
)
{
float x_pos = int (TextRect.Left);
//
// Loop over all the columns
//
for (int col_index = 0; col_index < Columns.Count (); col_index ++) {
//
// Alias some variables for readability
//
TextColumnClass *column = Columns[col_index];
//
// Determine what text we should display
//
const WCHAR *text = L"";
Vector3 color (0 ,0, 0);
text = column->Get_Item_Text (item_index);
color = column->Get_Item_Color (item_index);
//
// Determine how wide the column should be
//
float col_width = column->Get_Width () * (TextRect.Right - TextRect.Left);
if (col_index + 1 >= Columns.Count ()) {
col_width = TextRect.Right - x_pos;
}
TextRenderers[1]->Set_Wrapping_Width (col_width - 5);
//
// Update the text and color of this renderer
//
TextRenderers[1]->Build_Sentence (text);
TextRenderers[1]->Set_Location (Vector2 (int(x_pos), int(y_pos)));
TextRenderers[1]->Draw_Sentence (VRGB_TO_INT32(color));
//
// See if this is the 'tallest' column so far
//
RectClass extents = TextRenderers[1]->Get_Draw_Extents ();
(*row_height) = max ((*row_height), extents.Height ());
//
// Move to the next column
//
x_pos += int (column->Get_Width () * (TextRect.Right - TextRect.Left));
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Display
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Display (bool onoff)
{
if (onoff != IsDisplayed) {
FirstLineIndex = 0;
CurrentDisplayCount = 0;
IsDisplayed = onoff;
IsViewDirty = onoff;
Backdrop.Set_Hidden (!onoff);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Page_Down
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Page_Down (void)
{
FirstLineIndex += CurrentDisplayCount;
Update_View ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Page_Up
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Page_Up (void)
{
FirstLineIndex -= CurrentDisplayCount;
FirstLineIndex = max (0, FirstLineIndex);
Update_View ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Scroll_To_Top
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Scroll_To_Top (void)
{
FirstLineIndex = 0;
Update_View ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Render
//
////////////////////////////////////////////////////////////////
void
TextWindowClass::Render (void)
{
if (IsDisplayed == false) {
return ;
}
if (IsViewDirty || TextRenderers[0] == NULL) {
Update_View ();
}
//
// Render the backdrop
//
Backdrop.Render ();
//
// Render each piece of text
//
TextRenderers[0]->Render();
TextRenderers[1]->Render();
return ;
}
//********************************************************************************//
//
// Start of TextColumnClass
//
//********************************************************************************//
////////////////////////////////////////////////////////////////
//
// Free_Data
//
////////////////////////////////////////////////////////////////
void
TextColumnClass::Free_Data (void)
{
Delete_All_Items ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Reset_Contents
//
////////////////////////////////////////////////////////////////
void
TextColumnClass::Reset_Contents (void)
{
//
// Remove all the entries
//
Free_Data ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Insert_Item
//
////////////////////////////////////////////////////////////////
int
TextColumnClass::Insert_Item (int index, const WCHAR *item_name)
{
TextItemClass *item = new TextItemClass (item_name);
//
// Should we insert this item in the list or add it to the end?
//
if (index < Items.Count ()) {
Items.Insert (index + 1, item);
} else {
Items.Add (item);
index = (Items.Count () - 1);
}
return index;
}
////////////////////////////////////////////////////////////////
//
// Delete_Item
//
////////////////////////////////////////////////////////////////
bool
TextColumnClass::Delete_Item (int index)
{
bool retval = false;
//
// Delete the item if we can find it in our list
//
if (index >= 0 && index < Items.Count ()) {
delete Items[index];
Items.Delete (index);
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Delete_All_Items
//
////////////////////////////////////////////////////////////////
void
TextColumnClass::Delete_All_Items (void)
{
//
// Free each of the items in the list
//
for (int index = 0; index < Items.Count (); index ++) {
delete Items[index];
}
Items.Reset_Active();
return ;
}