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