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/wwui/menuentryctrl.cpp

789 lines
18 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/wwui/menuentryctrl.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 1/02/02 11:01a $*
* *
* $Revision:: 19 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "menuentryctrl.h"
#include "assetmgr.h"
#include "font3d.h"
#include "dialogbase.h"
#include "mousemgr.h"
#include "stylemgr.h"
#include "dialogmgr.h"
#include "texture.h"
#include "ww3d.h"
////////////////////////////////////////////////////////////////
// Static member initialization
////////////////////////////////////////////////////////////////
int MenuEntryCtrlClass::MaxDefaultRedValue = 9;
int MenuEntryCtrlClass::MaxHilightRedValue = 16;
////////////////////////////////////////////////////////////////
//
// MenuEntryCtrlClass
//
////////////////////////////////////////////////////////////////
MenuEntryCtrlClass::MenuEntryCtrlClass (void) :
CurrState (UP),
WasButtonPressedOnMe (false),
IsMouseOverMe (false),
CurrRadiusX (5),
CurrRadiusY (5),
StartTime (0),
EndTime (0),
CurrColor (RGB_TO_INT32 (0, MaxDefaultRedValue, 0))
{
//
// Get the current bits-per-pixel of the display
//
int foo = 0;
int bits = 0;
bool windowed = 0;
WW3D::Get_Device_Resolution (foo, foo, bits, windowed);
//
// If we are running at anything less then 32bpp, then
// scale the glow colors
//
if (bits < 32) {
MaxDefaultRedValue = 11;
MaxHilightRedValue = 22;
}
//
// Set the font for the text renderer
//
StyleMgrClass::Assign_Font (&TextRenderer, StyleMgrClass::FONT_MENU);
StyleMgrClass::Assign_Font (&GlowRenderer, StyleMgrClass::FONT_MENU);
return ;
}
////////////////////////////////////////////////////////////////
//
// ~MenuEntryCtrlClass
//
////////////////////////////////////////////////////////////////
MenuEntryCtrlClass::~MenuEntryCtrlClass (void)
{
return ;
}
////////////////////////////////////////////////////////////////
//
// Render
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::Render (void)
{
//
// Refresh the cached renderer (if necessary)
//
if (Is_Dirty ()) {
Create_Text_Renderer ();
}
//
// Render the menu text
//
GlowRenderer.Render ();
TextRenderer.Render ();
DialogControlClass::Render ();
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Frame_Update
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::On_Frame_Update (void)
{
//
// Allow the current state to update
//
Update_State ();
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Create
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::On_Create (void)
{
if ((Style & 0xF) == BS_OWNERDRAW) {
//
// Set the font for the text renderer
//
StyleMgrClass::Assign_Font (&TextRenderer, StyleMgrClass::FONT_SM_MENU);
StyleMgrClass::Assign_Font (&GlowRenderer, StyleMgrClass::FONT_SM_MENU);
/*FontCharsClass *font = WW3DAssetManager::Get_Instance()->Get_FontChars ("Lucida Sans Unicode", 12);
TextRenderer.Set_Font (font);
GlowRenderer.Set_Font (font);
font->Release_Ref ();*/
}
TextRenderer.Build_Sentence (Title);
GlowRenderer.Build_Sentence (Title);
//
// Determine what rectangle should be clickable
//
Vector2 extents = TextRenderer.Get_Text_Extents (Title);
Rect = MaxRect;
//
// Should we left justify?
//
if ((Style & 0xF00) == BS_LEFT) {
Rect.Right = Rect.Left + extents.X + TextRenderer.Get_Text_Extents (L"W").X;
} else {
Rect.Left = int(Rect.Left + (Rect.Width () / 2) - (extents.X / 2));
Rect.Right = Rect.Left + extents.X + TextRenderer.Get_Text_Extents (L"W").X;
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Update_Client_Rect
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::Update_Client_Rect (void)
{
MaxRect = Rect;
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Mouse_Wheel
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::On_Mouse_Wheel (int direction)
{
if (HasFocus) {
//
// Find the next control to set the focus to...
//
DialogControlClass *control = Parent->Find_Next_Group_Control (this, direction);
if (control != NULL) {
control->Set_Focus ();
control->Center_Mouse ();
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_LButton_Down
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::On_LButton_Down (const Vector2 &mouse_pos)
{
Set_Capture ();
//
// Update our mouse flags
//
WasButtonPressedOnMe = true;
IsMouseOverMe = Rect.Contains (mouse_pos);
if (IsMouseOverMe) {
Set_State (DOWN);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_LButton_Up
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::On_LButton_Up (const Vector2 &mouse_pos)
{
Release_Capture ();
//
// Update our mouse flags
//
WasButtonPressedOnMe = false;
IsMouseOverMe = Rect.Contains (mouse_pos);
//
// Switch states
//
if (CurrState != DOWN) {
if (HasFocus) {
if (IsMouseOverMe == false || CurrState != DOWN) {
Set_State (HILIGHT);
}
} else {
Set_State (UP);
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Mouse_Move
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::On_Mouse_Move (const Vector2 &mouse_pos)
{
//
// Force focus onto the control
//
Set_Focus ();
//
// Check to see whether or not the mouse is over the control
//
IsMouseOverMe = Rect.Contains (mouse_pos);
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_State
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::Set_State (int new_state)
{
if (new_state != CurrState) {
//
// Update the state and force a repaint
//
CurrState = new_state;
Set_Dirty ();
//
// Switch to the new state
//
switch (CurrState)
{
case UP:
CurrRadiusX = 5;
CurrRadiusY = 5;
break;
case DOWN:
StartTime = DialogMgrClass::Get_Time ();
EndTime = StartTime + 300;
CurrColor = RGB_TO_INT32 (MaxHilightRedValue, 0, 0);
//
// Play the sound effect
//
StyleMgrClass::Play_Sound (StyleMgrClass::EVENT_MOUSE_CLICK);
break;
case HILIGHT:
StartTime = DialogMgrClass::Get_Time ();
EndTime = StartTime + 1000;
CurrColor = RGB_TO_INT32 (MaxHilightRedValue, 0, 0);
break;
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Update_State
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::Update_State (void)
{
switch (CurrState)
{
case UP:
break;
case DOWN:
{
//
// Do we need to animate the glow?
//
int curr_time = DialogMgrClass::Get_Time ();
if (curr_time < EndTime) {
//
// Expand the glow
//
float percent = float(curr_time - StartTime) / float(EndTime - StartTime);
CurrRadiusX = int(5.0F + (155.0F * percent));
CurrRadiusY = int(5.0F + (25.0F * percent));
CurrColor = RGB_TO_INT32 (MaxHilightRedValue * 3, 0, 0);
uint32 time1 = StartTime;
uint32 time2 = time1 + (EndTime - StartTime) / 2;
uint32 time3 = EndTime;
if (curr_time >= (int)time2) {
Vector3 start_color (MaxHilightRedValue * 3, MaxHilightRedValue * 3.0F * 0.6F, 0);
Vector3 end_color (0, 0, 0);
//
// Transition the color from start to finish
//
uint32 start_time = time2;
uint32 end_time = time3;
float percent = float(float(curr_time - start_time) / float(end_time - start_time));
Vector3 color = start_color + (end_color - start_color) * percent;
CurrColor = RGB_TO_INT32 (color.X, color.Y, color.Z);
} else {
Vector3 start_color (MaxHilightRedValue * 3, 0, 0);
Vector3 end_color (MaxHilightRedValue * 3, MaxHilightRedValue * 3.0F * 0.6F, 0);
uint32 start_time = time1;
uint32 end_time = time2;
float percent = float(float(curr_time - start_time) / float(end_time - start_time));
Vector3 color = start_color + (end_color - start_color) * percent;
CurrColor = RGB_TO_INT32 (color.X, color.Y, color.Z);
}
//
// Force update the display
//
Set_Dirty ();
} else {
if (CurrRadiusX != 160.0F) {
//
// Snap the glow to its max
//
CurrRadiusX = 160.0F;
CurrRadiusY = 60.0F;
CurrColor = RGB_TO_INT32 (0, 0, 0);
}
//
// Send the command (if necessary)
//
On_Pushed ();
if (HasFocus) {
Set_State (HILIGHT);
} else {
Set_State (UP);
}
}
break;
}
case HILIGHT:
{
//
// Do we need to animate the glow?
//
int curr_time = DialogMgrClass::Get_Time ();
if (curr_time < EndTime) {
//
// Expand the glow
//
float percent = float(curr_time - StartTime) / float(EndTime - StartTime);
CurrRadiusX = int(5.0F + (55.0F * percent));
CurrRadiusY = CurrRadiusX;
//
// Fade out the color
//
if (curr_time > (EndTime - 500)) {
float start_time = EndTime - 500;
float red_percent = float(float(curr_time - start_time) / float(EndTime - start_time));
int red_value = (1.0F - red_percent) * MaxHilightRedValue;
CurrColor = RGB_TO_INT32 (red_value, 0, 0);
}
//
// Force update the display
//
Set_Dirty ();
} else {
//
// Snap the glow to its max
//
CurrRadiusX = 5;
CurrRadiusY = 5;
CurrColor = RGB_TO_INT32 (MaxHilightRedValue, 0, 0);
//
// Restart the animation
//
StartTime = curr_time;
EndTime = curr_time + 1000;
}
break;
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Create_Text_Renderer
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::Create_Text_Renderer (void)
{
TextRenderer.Reset_Polys ();
GlowRenderer.Reset_Polys ();
//
// Get the extents of the text we will be drawing
//
Vector2 text_extent = TextRenderer.Get_Text_Extents (Title);
//
// Assume cenetered text
//
int x_pos = int(Rect.Left + (Rect.Width () / 2) - (text_extent.X / 2));
int y_pos = int(Rect.Top + (Rect.Height () / 2) - (text_extent.Y / 2));
//
// Should we left justify?
//
if ((Style & 0xF00) == BS_LEFT) {
x_pos = int(Rect.Left + 1);
}
if (CurrState == UP) {
//
// Render the glow
//
Create_Glow (CurrRadiusX, CurrRadiusY, RGB_TO_INT32 (MaxDefaultRedValue, 0, 0));
//
// Render the text
//
TextRenderer.Set_Location (Vector2 (x_pos, y_pos));
TextRenderer.Draw_Sentence (StyleMgrClass::Get_Text_Color ());
} else if (CurrState == DOWN) {
//
// Render the glow
//
Create_Glow (CurrRadiusX, CurrRadiusY, CurrColor);
//
// Render the text
//
TextRenderer.Set_Location (Vector2 (x_pos + 1, y_pos + 1));
TextRenderer.Draw_Sentence (RGB_TO_INT32 (0, 0, 0));
TextRenderer.Set_Location (Vector2 (x_pos, y_pos));
TextRenderer.Draw_Sentence (RGB_TO_INT32 (0, 0, 0));
} else if (CurrState == HILIGHT) {
//
// Render the glow
//
Create_Glow (CurrRadiusX, CurrRadiusY, CurrColor);
TextRenderer.Set_Location (Vector2 (x_pos-1, y_pos-1));
TextRenderer.Draw_Sentence (StyleMgrClass::Get_Text_Color ());
TextRenderer.Set_Location (Vector2 (x_pos, y_pos));
TextRenderer.Draw_Sentence (RGB_TO_INT32 (0, 0, 0));
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Set_Cursor
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::On_Set_Cursor (const Vector2 &mouse_pos)
{
//
// Change the mouse cursor if necessary
//
if (Rect.Contains (mouse_pos)) {
MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ACTION);
} else {
MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ARROW);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Create_Glow
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::Create_Glow (int radiusx, int radiusy, int color)
{
GlowRenderer.Reset_Polys ();
//
// Determine how to justify the text
//
StyleMgrClass::JUSTIFICATION justification = StyleMgrClass::CENTER_JUSTIFY;
if ((Style & 0xF00) == BS_LEFT) {
justification = StyleMgrClass::LEFT_JUSTIFY;
}
//
// Render the text in "glow" fashion
//
StyleMgrClass::Render_Glow (Title, &GlowRenderer, Rect, radiusx, radiusy,
color, justification);
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Set_Focus
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::On_Set_Focus (void)
{
//
// Set the state to hilight if the mouse button
// isn't down
//
if (WasButtonPressedOnMe == false) {
Set_State (HILIGHT);
//
// Play the sound effect
//
StyleMgrClass::Play_Sound (StyleMgrClass::EVENT_MOUSE_OVER);
}
DialogControlClass::On_Set_Focus ();
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Kill_Focus
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::On_Kill_Focus (DialogControlClass *focus)
{
//
// Restore the entry to its default state
//
if (CurrState != DOWN) {
Set_State (UP);
}
WasButtonPressedOnMe = false;
DialogControlClass::On_Kill_Focus (focus);
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Key_Down
//
////////////////////////////////////////////////////////////////
bool
MenuEntryCtrlClass::On_Key_Down (uint32 key_id, uint32 key_data)
{
bool handled = true;
switch (key_id)
{
case VK_RETURN:
case VK_SPACE:
Set_State (DOWN);
break;
case VK_LEFT:
case VK_UP:
{
//
// Set the focus to the previous control in our group
//
DialogControlClass *control = Parent->Find_Next_Group_Control (this, -1);
if (control != NULL) {
control->Set_Focus ();
}
break;
}
case VK_RIGHT:
case VK_DOWN:
{
//
// Set the focus to the next control in our group
//
DialogControlClass *control = Parent->Find_Next_Group_Control (this, 1);
if (control != NULL) {
control->Set_Focus ();
}
break;
}
default:
handled = false;
break;
}
return handled;
}
////////////////////////////////////////////////////////////////
//
// On_Key_Up
//
////////////////////////////////////////////////////////////////
bool
MenuEntryCtrlClass::On_Key_Up (uint32 key_id)
{
return false;
}
////////////////////////////////////////////////////////////////
//
// On_Pushed
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::On_Pushed (void)
{
Parent->On_Command (ID, BN_CLICKED, 0);
return ;
}
////////////////////////////////////////////////////////////////
//
// Center_Mouse
//
////////////////////////////////////////////////////////////////
void
MenuEntryCtrlClass::Center_Mouse (void)
{
//
// Get the extents of the text we will be drawing
//
Vector2 text_extent = TextRenderer.Get_Text_Extents (Title);
int x_pos = 0;
//
// Should we left justify?
//
if ((Style & 0xF00) == BS_LEFT) {
x_pos = int(Rect.Left + (text_extent.X / 2));
} else {
x_pos = int(Rect.Left + (Rect.Width () / 2) - (text_extent.X / 2));
}
int y_pos = int(Rect.Top + (Rect.Height () / 2));
//
// Put the mouse cursor in the middle of this control
//
Vector3 mouse_pos = DialogMgrClass::Get_Mouse_Pos ();
mouse_pos.X = x_pos;
mouse_pos.Y = y_pos;
DialogMgrClass::Set_Mouse_Pos (mouse_pos);
return ;
}