Paranoia2/mainui/basemenu.cpp

1609 lines
37 KiB
C++
Raw Blame History

/*
Copyright (C) 1997-2001 Id Software, 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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// ui_menu.c -- main menu interface
#define OEMRESOURCE // for OCR_* cursor junk
#include "extdll.h"
#include "basemenu.h"
#include "keydefs.h"
#include "menufont.h" // built-in menu font
#include "utils.h"
#include "menu_btnsbmp_table.h"
//CR
#include "ui_title_anim.h"
cvar_t *ui_precache;
cvar_t *ui_showmodels;
cvar_t *ui_videoconfig;
uiStatic_t uiStatic;
char uiEmptyString[256];
const char *uiSoundIn = "media/launch_upmenu1.wav";
const char *uiSoundOut = "media/launch_dnmenu1.wav";
const char *uiSoundLaunch = "media/launch_select2.wav";
const char *uiSoundGlow = "media/launch_glow1.wav";
const char *uiSoundBuzz = "media/launch_deny2.wav";
const char *uiSoundKey = "media/launch_select1.wav";
const char *uiSoundRemoveKey = "media/launch_deny1.wav";
const char *uiSoundMove = ""; // Xash3D not use movesound
const char *uiSoundNull = "";
int uiColorHelp = 0xFFFFFFFF; // 255, 255, 255, 255 // hint letters color
int uiPromptBgColor = 0xFF404040; // 64, 64, 64, 255 // dialog background color
int uiPromptTextColor = 0xFFF0B418; // 255, 160, 0, 255 // dialog or button letters color
int uiPromptFocusColor = 0xFFFFFF00; // 255, 255, 0, 255 // dialog or button focus letters color
int uiInputTextColor = 0xFFC0C0C0; // 192, 192, 192, 255
int uiInputBgColor = 0xFF404040; // 64, 64, 64, 255 // field, scrollist, checkbox background color
int uiInputFgColor = 0xFF555555; // 85, 85, 85, 255 // field, scrollist, checkbox foreground color
int uiColorWhite = 0xFFFFFFFF; // 255, 255, 255, 255 // useful for bitmaps
int uiColorDkGrey = 0xFF404040; // 64, 64, 64, 255 // shadow and grayed items
int uiColorBlack = 0xFF000000; // 0, 0, 0, 255 // some controls background
int uiColorConsole = 0xFFF0B418; // just for reference
// color presets (this is nasty hack to allow color presets to part of text)
const int g_iColorTable[8] =
{
0xFF000000, // black
0xFFFF0000, // red
0xFF00FF00, // green
0xFFFFFF00, // yellow
0xFF0000FF, // blue
0xFF00FFFF, // cyan
0xFFF0B418, // dialog or button letters color
0xFFFFFFFF, // white
};
/*
=================
UI_ScaleCoords
Any parameter can be NULL if you don't want it
=================
*/
void UI_ScaleCoords( int *x, int *y, int *w, int *h )
{
if( x ) *x *= uiStatic.scaleX;
if( y ) *y *= uiStatic.scaleY;
if( w ) *w *= uiStatic.scaleX;
if( h ) *h *= uiStatic.scaleY;
}
/*
=================
UI_CursorInRect
=================
*/
int UI_CursorInRect( int x, int y, int w, int h )
{
if( uiStatic.cursorX < x )
return FALSE;
if( uiStatic.cursorX > x + w )
return FALSE;
if( uiStatic.cursorY < y )
return FALSE;
if( uiStatic.cursorY > y + h )
return FALSE;
return TRUE;
}
/*
=================
UI_DrawPic
=================
*/
void UI_DrawPic( int x, int y, int width, int height, const int color, const char *pic )
{
HIMAGE hPic = PIC_Load( pic );
int r, g, b, a;
UnpackRGBA( r, g, b, a, color );
PIC_Set( hPic, r, g, b, a );
PIC_Draw( x, y, width, height );
}
/*
=================
UI_DrawPicAdditive
=================
*/
void UI_DrawPicAdditive( int x, int y, int width, int height, const int color, const char *pic )
{
HIMAGE hPic = PIC_Load( pic );
int r, g, b, a;
UnpackRGBA( r, g, b, a, color );
PIC_Set( hPic, r, g, b, a );
PIC_DrawAdditive( x, y, width, height );
}
/*
=================
UI_FillRect
=================
*/
void UI_FillRect( int x, int y, int width, int height, const int color )
{
int r, g, b, a;
UnpackRGBA( r, g, b, a, color );
FillRGBA( x, y, width, height, r, g, b, a );
}
/*
=================
UI_DrawRectangleExt
=================
*/
void UI_DrawRectangleExt( int in_x, int in_y, int in_w, int in_h, const int color, int outlineWidth )
{
int x, y, w, h;
x = in_x - outlineWidth;
y = in_y - outlineWidth;
w = outlineWidth;
h = in_h + outlineWidth + outlineWidth;
// draw left
UI_FillRect( x, y, w, h, color );
x = in_x + in_w;
y = in_y - outlineWidth;
w = outlineWidth;
h = in_h + outlineWidth + outlineWidth;
// draw right
UI_FillRect( x, y, w, h, color );
x = in_x;
y = in_y - outlineWidth;
w = in_w;
h = outlineWidth;
// draw top
UI_FillRect( x, y, w, h, color );
// draw bottom
x = in_x;
y = in_y + in_h;
w = in_w;
h = outlineWidth;
UI_FillRect( x, y, w, h, color );
}
/*
=================
UI_DrawString
=================
*/
void UI_DrawString( int x, int y, int w, int h, const char *string, const int color, int forceColor, int charW, int charH, int justify, int shadow )
{
int modulate, shadowModulate;
char line[1024], *l;
int xx, yy, ofsX, ofsY, len, ch;
if( !string || !string[0] )
return;
#if 0 // g-cont. disabled 29/06/2011
// this code do a bad things with prompt dialogues
// vertically centered
if( !strchr( string, '\n' ))
y = y + (( h - charH ) / 2 );
#endif
if( shadow )
{
shadowModulate = PackAlpha( uiColorBlack, UnpackAlpha( color ));
ofsX = charW / 8;
ofsY = charH / 8;
}
modulate = color;
yy = y;
while( *string )
{
// get a line of text
len = 0;
while( *string )
{
if( *string == '\n' )
{
string++;
break;
}
line[len++] = *string++;
if( len == sizeof( line ) - 1 )
break;
}
line[len] = 0;
// align the text as appropriate
if( justify == 0 ) xx = x;
if( justify == 1 ) xx = x + ((w - (ColorStrlen( line ) * charW )) / 2);
if( justify == 2 ) xx = x + (w - (ColorStrlen( line ) * charW ));
// draw it
l = line;
while( *l )
{
if( IsColorString( l ))
{
int colorNum = ColorIndex( *(l+1) );
if( colorNum == 7 && color != 0 )
{
modulate = color;
}
else if( !forceColor )
{
modulate = PackAlpha( g_iColorTable[colorNum], UnpackAlpha( color ));
}
l += 2;
continue;
}
ch = *l++;
ch &= 255;
// fix for letter <20>
if( ch == 0xB8 ) ch = (byte)'<EFBFBD>';
if( ch == 0xA8 ) ch = (byte)'<EFBFBD>';
if( ch != ' ' )
{
if( shadow ) TextMessageDrawChar( xx + ofsX, yy + ofsY, charW, charH, ch, shadowModulate, uiStatic.hFont );
TextMessageDrawChar( xx, yy, charW, charH, ch, modulate, uiStatic.hFont );
}
xx += charW;
}
yy += charH;
}
}
/*
=================
UI_DrawMouseCursor
=================
*/
void UI_DrawMouseCursor( void )
{
menuCommon_s *item;
HICON hCursor = NULL;
int i;
if( uiStatic.hideCursor ) return;
for( i = 0; i < uiStatic.menuActive->numItems; i++ )
{
item = (menuCommon_s *)uiStatic.menuActive->items[i];
if ( item->flags & (QMF_INACTIVE|QMF_HIDDEN))
continue;
if ( !UI_CursorInRect( item->x, item->y, item->width, item->height ))
continue;
if ( item->flags & QMF_GRAYED )
{
hCursor = (HICON)LoadCursor( NULL, (LPCTSTR)OCR_NO );
}
else
{
if( item->type == QMTYPE_FIELD )
hCursor = (HICON)LoadCursor( NULL, (LPCTSTR)OCR_IBEAM );
}
break;
}
if( !hCursor ) hCursor = (HICON)LoadCursor( NULL, (LPCTSTR)OCR_NORMAL );
SET_CURSOR( hCursor );
}
/*
=================
UI_DrawBackground_Callback
=================
*/
void UI_DrawBackground_Callback( void *self )
{
if (!uiStatic.m_fHaveSteamBackground)
{
menuCommon_s *item = (menuCommon_s *)self;
UI_DrawPic( item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic );
return;
}
int xpos, ypos;
float xScale, yScale;
// work out scaling factors
xScale = ScreenWidth / uiStatic.m_flTotalWidth;
yScale = ScreenHeight / uiStatic.m_flTotalHeight;
// iterate and draw all the background pieces
ypos = 0;
for (int y = 0; y < BACKGROUND_ROWS; y++)
{
xpos = 0;
for (int x = 0; x < BACKGROUND_COLUMNS; x++)
{
bimage_t &bimage = uiStatic.m_SteamBackground[y][x];
int dx = (int)ceil(xpos * xScale);
int dy = (int)ceil(ypos * yScale);
int dw = (int)ceil(bimage.width * xScale);
int dt = (int)ceil(bimage.height * yScale);
if (x == 0) dx = 0;
if (y == 0) dy = 0;
PIC_Set( bimage.hImage, 255, 255, 255, 255 );
PIC_Draw( dx, dy, dw, dt );
xpos += bimage.width;
}
ypos += uiStatic.m_SteamBackground[y][0].height;
}
}
/*
=================
UI_LoadBackgroundImage
=================
*/
void UI_LoadBackgroundImage( void )
{
int num_background_images = 0;
char filename[512];
for( int y = 0; y < BACKGROUND_ROWS; y++ )
{
for( int x = 0; x < BACKGROUND_COLUMNS; x++ )
{
sprintf( filename, "resource/background/800_%d_%c_loading.tga", y + 1, 'a' + x );
if (g_engfuncs.pfnFileExists( filename, TRUE ))
num_background_images++;
}
}
if (num_background_images == (BACKGROUND_COLUMNS * BACKGROUND_ROWS))
uiStatic.m_fHaveSteamBackground = TRUE;
else uiStatic.m_fHaveSteamBackground = FALSE;
if (uiStatic.m_fHaveSteamBackground)
{
uiStatic.m_flTotalWidth = uiStatic.m_flTotalHeight = 0.0f;
for( int y = 0; y < BACKGROUND_ROWS; y++ )
{
for( int x = 0; x < BACKGROUND_COLUMNS; x++ )
{
bimage_t &bimage = uiStatic.m_SteamBackground[y][x];
sprintf(filename, "resource/background/800_%d_%c_loading.tga", y + 1, 'a' + x);
bimage.hImage = PIC_Load( filename, PIC_NOFLIP_TGA );
bimage.width = PIC_Width( bimage.hImage );
bimage.height = PIC_Height( bimage.hImage );
if (y==0) uiStatic.m_flTotalWidth += bimage.width;
if (x==0) uiStatic.m_flTotalHeight += bimage.height;
}
}
}
else
{
if( g_engfuncs.pfnFileExists( "gfx/shell/splash.bmp", TRUE ))
{
// if we doesn't have logo.avi in gamedir we don't want to draw it
if( !g_engfuncs.pfnFileExists( "media/logo.avi", TRUE ))
uiStatic.m_fDisableLogo = TRUE;
}
}
}
/*
=================
UI_StartSound
=================
*/
void UI_StartSound( const char *sound )
{
PLAY_SOUND( sound );
}
// =====================================================================
/*
=================
UI_AddItem
=================
*/
void UI_AddItem( menuFramework_s *menu, void *item )
{
menuCommon_s *generic = (menuCommon_s *)item;
if( menu->numItems >= UI_MAX_MENUITEMS )
HOST_ERROR( "UI_AddItem: UI_MAX_MENUITEMS limit exceeded\n" );
menu->items[menu->numItems] = item;
((menuCommon_s *)menu->items[menu->numItems])->parent = menu;
((menuCommon_s *)menu->items[menu->numItems])->flags &= ~QMF_HASMOUSEFOCUS;
menu->numItems++;
switch( generic->type )
{
case QMTYPE_SCROLLLIST:
UI_ScrollList_Init((menuScrollList_s *)item );
break;
case QMTYPE_SPINCONTROL:
UI_SpinControl_Init((menuSpinControl_s *)item );
break;
case QMTYPE_CHECKBOX:
UI_CheckBox_Init((menuCheckBox_s *)item );
break;
case QMTYPE_SLIDER:
UI_Slider_Init((menuSlider_s *)item );
break;
case QMTYPE_FIELD:
UI_Field_Init((menuField_s *)item );
break;
case QMTYPE_ACTION:
UI_Action_Init((menuAction_s *)item );
break;
case QMTYPE_BITMAP:
UI_Bitmap_Init((menuBitmap_s *)item );
break;
case QMTYPE_BM_BUTTON:
UI_PicButton_Init((menuPicButton_s *)item );
break;
default:
HOST_ERROR( "UI_AddItem: unknown item type (%i)\n", generic->type );
}
}
/*
=================
UI_CursorMoved
=================
*/
void UI_CursorMoved( menuFramework_s *menu )
{
void (*callback)( void *self, int event );
if( menu->cursor == menu->cursorPrev )
return;
if( menu->cursorPrev >= 0 && menu->cursorPrev < menu->numItems )
{
callback = ((menuCommon_s *)menu->items[menu->cursorPrev])->callback;
if( callback ) callback( menu->items[menu->cursorPrev], QM_LOSTFOCUS );
}
if( menu->cursor >= 0 && menu->cursor < menu->numItems )
{
callback = ((menuCommon_s *)menu->items[menu->cursor])->callback;
if( callback ) callback( menu->items[menu->cursor], QM_GOTFOCUS );
}
}
/*
=================
UI_SetCursor
=================
*/
void UI_SetCursor( menuFramework_s *menu, int cursor )
{
if(((menuCommon_s *)(menu->items[cursor]))->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN))
return;
menu->cursorPrev = menu->cursor;
menu->cursor = cursor;
UI_CursorMoved( menu );
}
/*
=================
UI_SetCursorToItem
=================
*/
void UI_SetCursorToItem( menuFramework_s *menu, void *item )
{
for( int i = 0; i < menu->numItems; i++ )
{
if( menu->items[i] == item )
{
UI_SetCursor( menu, i );
return;
}
}
}
/*
=================
UI_ItemAtCursor
=================
*/
void *UI_ItemAtCursor( menuFramework_s *menu )
{
if( menu->cursor < 0 || menu->cursor >= menu->numItems )
return 0;
// inactive items can't be has focus
if( ((menuCommon_s *)menu->items[menu->cursor])->flags & QMF_INACTIVE )
return 0;
return menu->items[menu->cursor];
}
/*
=================
UI_AdjustCursor
This functiont takes the given menu, the direction, and attempts to
adjust the menu's cursor so that it's at the next available slot
=================
*/
void UI_AdjustCursor( menuFramework_s *menu, int dir )
{
menuCommon_s *item;
int wrapped = false;
wrap:
while( menu->cursor >= 0 && menu->cursor < menu->numItems )
{
item = (menuCommon_s *)menu->items[menu->cursor];
if( item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN|QMF_MOUSEONLY))
menu->cursor += dir;
else break;
}
if( dir == 1 )
{
if( menu->cursor >= menu->numItems )
{
if( wrapped )
{
menu->cursor = menu->cursorPrev;
return;
}
menu->cursor = 0;
wrapped = true;
goto wrap;
}
}
else if( dir == -1 )
{
if( menu->cursor < 0 )
{
if( wrapped )
{
menu->cursor = menu->cursorPrev;
return;
}
menu->cursor = menu->numItems - 1;
wrapped = true;
goto wrap;
}
}
}
/*
=================
UI_DrawMenu
=================
*/
void UI_DrawMenu( menuFramework_s *menu )
{
static long statusFadeTime;
static menuCommon_s *lastItem;
menuCommon_s *item;
int i;
// draw contents
for( i = 0; i < menu->numItems; i++ )
{
item = (menuCommon_s *)menu->items[i];
if( item->flags & QMF_HIDDEN )
continue;
if( item->ownerdraw )
{
// total subclassing, owner draws everything
item->ownerdraw( item );
continue;
}
switch( item->type )
{
case QMTYPE_SCROLLLIST:
UI_ScrollList_Draw((menuScrollList_s *)item );
break;
case QMTYPE_SPINCONTROL:
UI_SpinControl_Draw((menuSpinControl_s *)item );
break;
case QMTYPE_CHECKBOX:
UI_CheckBox_Draw((menuCheckBox_s *)item );
break;
case QMTYPE_SLIDER:
UI_Slider_Draw((menuSlider_s *)item );
break;
case QMTYPE_FIELD:
UI_Field_Draw((menuField_s *)item );
break;
case QMTYPE_ACTION:
UI_Action_Draw((menuAction_s *)item );
break;
case QMTYPE_BITMAP:
UI_Bitmap_Draw((menuBitmap_s *)item );
break;
case QMTYPE_BM_BUTTON:
UI_PicButton_Draw((menuPicButton_s *)item );
break;
}
}
// draw status bar
item = (menuCommon_s *)UI_ItemAtCursor( menu );
if( item != lastItem )
{
// flash on selected button (like in GoldSrc)
if( item ) item->lastFocusTime = uiStatic.realTime;
statusFadeTime = uiStatic.realTime;
lastItem = item;
}
if( item && ( item->flags & QMF_HASMOUSEFOCUS && !( item->flags & QMF_NOTIFY )) && ( item->statusText != NULL ))
{
// fade it in, but wait a second
int alpha = bound( 0, ((( uiStatic.realTime - statusFadeTime ) - 1000 ) * 0.001f ) * 255, 255 );
int r, g, b, x, len;
GetConsoleStringSize( item->statusText, &len, NULL );
UnpackRGB( r, g, b, uiColorHelp );
TextMessageSetColor( r, g, b, alpha );
x = ( ScreenWidth - len ) * 0.5; // centering
DrawConsoleString( x, 720 * uiStatic.scaleY, item->statusText );
}
else statusFadeTime = uiStatic.realTime;
}
/*
=================
UI_DefaultKey
=================
*/
const char *UI_DefaultKey( menuFramework_s *menu, int key, int down )
{
const char *sound = NULL;
menuCommon_s *item;
int cursorPrev;
// menu system key
if( down && key == K_ESCAPE )
{
UI_PopMenu();
return uiSoundOut;
}
if( !menu || !menu->numItems )
return 0;
item = (menuCommon_s *)UI_ItemAtCursor( menu );
if( item && !(item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN)))
{
switch( item->type )
{
case QMTYPE_SCROLLLIST:
sound = UI_ScrollList_Key((menuScrollList_s *)item, key, down );
break;
case QMTYPE_SPINCONTROL:
sound = UI_SpinControl_Key((menuSpinControl_s *)item, key, down );
break;
case QMTYPE_CHECKBOX:
sound = UI_CheckBox_Key((menuCheckBox_s *)item, key, down );
break;
case QMTYPE_SLIDER:
sound = UI_Slider_Key((menuSlider_s *)item, key, down );
break;
case QMTYPE_FIELD:
sound = UI_Field_Key((menuField_s *)item, key, down );
break;
case QMTYPE_ACTION:
sound = UI_Action_Key((menuAction_s *)item, key, down );
break;
case QMTYPE_BITMAP:
sound = UI_Bitmap_Key((menuBitmap_s *)item, key, down );
break;
case QMTYPE_BM_BUTTON:
sound = UI_PicButton_Key((menuPicButton_s *)item, key, down );
break;
}
if( sound ) return sound; // key was handled
}
// system keys are always wait for keys down and never keys up
if( !down ) return 0;
// default handling
switch( key )
{
case K_UPARROW:
case K_KP_UPARROW:
case K_LEFTARROW:
case K_KP_LEFTARROW:
cursorPrev = menu->cursor;
menu->cursorPrev = menu->cursor;
menu->cursor--;
UI_AdjustCursor( menu, -1 );
if( cursorPrev != menu->cursor )
{
UI_CursorMoved( menu );
if( !(((menuCommon_s *)menu->items[menu->cursor])->flags & QMF_SILENT ))
sound = uiSoundMove;
}
break;
case K_DOWNARROW:
case K_KP_DOWNARROW:
case K_RIGHTARROW:
case K_KP_RIGHTARROW:
case K_TAB:
cursorPrev = menu->cursor;
menu->cursorPrev = menu->cursor;
menu->cursor++;
UI_AdjustCursor(menu, 1);
if( cursorPrev != menu->cursor )
{
UI_CursorMoved(menu);
if( !(((menuCommon_s *)menu->items[menu->cursor])->flags & QMF_SILENT ))
sound = uiSoundMove;
}
break;
case K_MOUSE1:
if( item )
{
if((item->flags & QMF_HASMOUSEFOCUS) && !(item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN)))
return UI_ActivateItem( menu, item );
}
break;
case K_ENTER:
case K_KP_ENTER:
if( item )
{
if( !(item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN|QMF_MOUSEONLY)))
return UI_ActivateItem( menu, item );
}
break;
}
return sound;
}
/*
=================
UI_ActivateItem
=================
*/
const char *UI_ActivateItem( menuFramework_s *menu, menuCommon_s *item )
{
if( item->callback )
{
item->callback( item, QM_ACTIVATED );
if( !( item->flags & QMF_SILENT ))
return uiSoundMove;
}
return 0;
}
/*
=================
UI_RefreshServerList
=================
*/
void UI_RefreshServerList( void )
{
uiStatic.numServers = 0;
memset( uiStatic.serverAddresses, 0, sizeof( uiStatic.serverAddresses ));
memset( uiStatic.serverNames, 0, sizeof( uiStatic.serverNames ));
CLIENT_COMMAND( FALSE, "localservers\n" );
}
/*
=================
UI_RefreshInternetServerList
=================
*/
void UI_RefreshInternetServerList( void )
{
uiStatic.numServers = 0;
memset( uiStatic.serverAddresses, 0, sizeof( uiStatic.serverAddresses ));
memset( uiStatic.serverNames, 0, sizeof( uiStatic.serverNames ));
CLIENT_COMMAND( FALSE, "internetservers\n" );
}
/*
=================
UI_StartBackGroundMap
=================
*/
bool UI_StartBackGroundMap( void )
{
static bool first = TRUE;
if( !first ) return FALSE;
first = FALSE;
// some map is already running
if( !uiStatic.bgmapcount || CL_IsActive() || gpGlobals->demoplayback )
return FALSE;
int bgmapid = RANDOM_LONG( 0, uiStatic.bgmapcount - 1 );
char cmd[128];
sprintf( cmd, "maps/%s.bsp", uiStatic.bgmaps[bgmapid] );
if( !FILE_EXISTS( cmd )) return FALSE;
sprintf( cmd, "map_background %s\n", uiStatic.bgmaps[bgmapid] );
CLIENT_COMMAND( FALSE, cmd );
return TRUE;
}
// =====================================================================
/*
=================
UI_CloseMenu
=================
*/
void UI_CloseMenu( void )
{
uiStatic.menuActive = NULL;
uiStatic.menuDepth = 0;
uiStatic.visible = false;
// clearing serverlist
uiStatic.numServers = 0;
memset( uiStatic.serverAddresses, 0, sizeof( uiStatic.serverAddresses ));
memset( uiStatic.serverNames, 0, sizeof( uiStatic.serverNames ));
UI_ClearButtonStack ();
// KEY_ClearStates ();
KEY_SetDest ( KEY_GAME );
}
/*
=================
UI_PushMenu
=================
*/
void UI_PushMenu( menuFramework_s *menu )
{
int i;
menuCommon_s *item;
// if this menu is already present, drop back to that level to avoid stacking menus by hotkeys
for( i = 0; i < uiStatic.menuDepth; i++ )
{
if( uiStatic.menuStack[i] == menu )
{
uiStatic.menuDepth = i;
break;
}
}
if( i == uiStatic.menuDepth )
{
if( uiStatic.menuDepth >= UI_MAX_MENUDEPTH )
HOST_ERROR( "UI_PushMenu: menu stack overflow\n" );
uiStatic.menuStack[uiStatic.menuDepth++] = menu;
}
uiStatic.menuActive = menu;
uiStatic.firstDraw = true;
uiStatic.enterSound = gpGlobals->time + 0.15; // make some delay
uiStatic.visible = true;
KEY_SetDest ( KEY_MENU );
menu->cursor = 0;
menu->cursorPrev = 0;
// force first available item to have focus
for( i = 0; i < menu->numItems; i++ )
{
item = (menuCommon_s *)menu->items[i];
if( item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN|QMF_MOUSEONLY))
continue;
menu->cursorPrev = -1;
UI_SetCursor( menu, i );
break;
}
}
/*
=================
UI_PopMenu
=================
*/
void UI_PopMenu( void )
{
UI_StartSound( uiSoundOut );
uiStatic.menuDepth--;
if( uiStatic.menuDepth < 0 )
HOST_ERROR( "UI_PopMenu: menu stack underflow\n" );
UI_PopPButtonStack();
if( uiStatic.menuDepth )
{
uiStatic.menuActive = uiStatic.menuStack[uiStatic.menuDepth-1];
uiStatic.firstDraw = true;
}
else if ( CL_IsActive( ))
{
UI_CloseMenu();
}
else
{
// never trying the close menu when client isn't connected
KEY_SetDest( KEY_MENU );
UI_Main_Menu();
}
if( uiStatic.m_fDemosPlayed && uiStatic.m_iOldMenuDepth == uiStatic.menuDepth )
{
CLIENT_COMMAND( FALSE, "demos\n" );
uiStatic.m_fDemosPlayed = false;
uiStatic.m_iOldMenuDepth = 0;
}
}
// =====================================================================
/*
=================
UI_UpdateMenu
=================
*/
void UI_UpdateMenu( float flTime )
{
if( !uiStatic.initialized )
return;
UI_DrawFinalCredits ();
if( !uiStatic.visible )
return;
if( !uiStatic.menuActive )
return;
uiStatic.realTime = flTime * 1000;
uiStatic.framecount++;
if( CVAR_GET_FLOAT( "cl_background" ) && !g_engfuncs.pfnClientInGame())
return; // don't draw menu while level is loading
if( uiStatic.firstDraw )
{
// we loading background so skip SCR_Update
if( UI_StartBackGroundMap( )) return;
if( uiStatic.menuActive->activateFunc )
uiStatic.menuActive->activateFunc();
}
// draw menu
if( uiStatic.menuActive->drawFunc )
uiStatic.menuActive->drawFunc();
else UI_DrawMenu( uiStatic.menuActive );
if( uiStatic.firstDraw )
{
uiStatic.firstDraw = false;
static int first = TRUE;
if( first )
{
// if game was launched with commandline e.g. +map or +load ignore the music
if( !CL_IsActive( ))
BACKGROUND_TRACK( "gamestartup", "gamestartup" );
first = FALSE;
}
}
//CR
UI_DrawTitleAnim();
//
// draw cursor
UI_DrawMouseCursor();
// delay playing the enter sound until after the menu has been
// drawn, to avoid delay while caching images
if( uiStatic.enterSound > 0.0f && uiStatic.enterSound <= gpGlobals->time )
{
UI_StartSound( uiSoundIn );
uiStatic.enterSound = -1;
}
}
/*
=================
UI_KeyEvent
=================
*/
void UI_KeyEvent( int key, int down )
{
const char *sound;
if( !uiStatic.initialized )
return;
if( !uiStatic.visible )
return;
if( !uiStatic.menuActive )
return;
if( uiStatic.menuActive->keyFunc )
sound = uiStatic.menuActive->keyFunc( key, down );
else sound = UI_DefaultKey( uiStatic.menuActive, key, down );
if( !down ) return;
if( sound && sound != uiSoundNull )
UI_StartSound( sound );
}
/*
=================
UI_CharEvent
=================
*/
void UI_CharEvent( int key )
{
menuFramework_s *menu;
menuCommon_s *item;
if( !uiStatic.initialized )
return;
if( !uiStatic.visible )
return;
if( !uiStatic.menuActive )
return;
menu = uiStatic.menuActive;
if( !menu || !menu->numItems )
return;
item = (menuCommon_s *)UI_ItemAtCursor( menu );
if( item && !(item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN)))
{
switch( item->type )
{
case QMTYPE_FIELD:
UI_Field_Char((menuField_s *)item, key );
break;
}
}
}
/*
=================
UI_MouseMove
=================
*/
void UI_MouseMove( int x, int y )
{
int i;
menuCommon_s *item;
if( !uiStatic.initialized )
return;
if( !uiStatic.visible )
return;
if( !uiStatic.menuActive )
return;
// now menu uses absolute coordinates
uiStatic.cursorX = x;
uiStatic.cursorY = y;
if( UI_CursorInRect( 1, 1, ScreenWidth - 1, ScreenHeight - 1 ))
uiStatic.mouseInRect = true;
else uiStatic.mouseInRect = false;
uiStatic.cursorX = bound( 0, uiStatic.cursorX, ScreenWidth );
uiStatic.cursorY = bound( 0, uiStatic.cursorY, ScreenHeight );
// region test the active menu items
for( i = 0; i < uiStatic.menuActive->numItems; i++ )
{
item = (menuCommon_s *)uiStatic.menuActive->items[i];
if( item->flags & (QMF_GRAYED|QMF_INACTIVE|QMF_HIDDEN))
{
if( item->flags & QMF_HASMOUSEFOCUS )
{
if( !UI_CursorInRect( item->x, item->y, item->width, item->height ))
item->flags &= ~QMF_HASMOUSEFOCUS;
else item->lastFocusTime = uiStatic.realTime;
}
continue;
}
if( !UI_CursorInRect( item->x, item->y, item->width, item->height ))
{
item->bPressed = false;
continue;
}
// set focus to item at cursor
if( uiStatic.menuActive->cursor != i )
{
UI_SetCursor( uiStatic.menuActive, i );
((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursorPrev]))->flags &= ~QMF_HASMOUSEFOCUS;
if (!(((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->flags & QMF_SILENT ))
UI_StartSound( uiSoundMove );
}
((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->flags |= QMF_HASMOUSEFOCUS;
((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->lastFocusTime = uiStatic.realTime;
return;
}
// out of any region
if( uiStatic.menuActive->numItems )
{
((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->flags &= ~QMF_HASMOUSEFOCUS;
((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->bPressed = false;
// a mouse only item restores focus to the previous item
if(((menuCommon_s *)(uiStatic.menuActive->items[uiStatic.menuActive->cursor]))->flags & QMF_MOUSEONLY )
{
if( uiStatic.menuActive->cursorPrev != -1 )
uiStatic.menuActive->cursor = uiStatic.menuActive->cursorPrev;
}
}
}
/*
=================
UI_SetActiveMenu
=================
*/
void UI_SetActiveMenu( int fActive )
{
if( !uiStatic.initialized )
return;
// don't continue firing if we leave game
KEY_ClearStates();
uiStatic.framecount = 0;
if( fActive )
{
KEY_SetDest( KEY_MENU );
UI_Main_Menu();
}
else
{
UI_CloseMenu();
}
}
/*
=================
UI_AddServerToList
=================
*/
void UI_AddServerToList( netadr_t adr, const char *info )
{
int i;
if( !uiStatic.initialized )
return;
if( uiStatic.numServers == UI_MAX_SERVERS )
return; // full
if( stricmp( gMenu.m_gameinfo.gamefolder, Info_ValueForKey( info, "gamedir" )))
return;
// ignore if duplicated
for( i = 0; i < uiStatic.numServers; i++ )
{
if( !stricmp( uiStatic.serverNames[i], info ))
return;
}
// add it to the list
uiStatic.updateServers = true; // info has been updated
uiStatic.serverAddresses[uiStatic.numServers] = adr;
strncpy( uiStatic.serverNames[uiStatic.numServers], info, sizeof( uiStatic.serverNames[uiStatic.numServers] ));
uiStatic.numServers++;
}
/*
=================
UI_IsVisible
Some systems may need to know if it is visible or not
=================
*/
int UI_IsVisible( void )
{
if( !uiStatic.initialized )
return false;
return uiStatic.visible;
}
void UI_GetCursorPos( int *pos_x, int *pos_y )
{
if( pos_x ) *pos_x = uiStatic.cursorX;
if( pos_y ) *pos_y = uiStatic.cursorY;
}
void UI_SetCursorPos( int pos_x, int pos_y )
{
// uiStatic.cursorX = bound( 0, pos_x, ScreenWidth );
// uiStatic.cursorY = bound( 0, pos_y, ScreenHeight );
uiStatic.mouseInRect = true;
}
void UI_ShowCursor( int show )
{
uiStatic.hideCursor = (show) ? false : true;
}
int UI_MouseInRect( void )
{
return uiStatic.mouseInRect;
}
/*
=================
UI_Precache
=================
*/
void UI_Precache( void )
{
if( !uiStatic.initialized )
return;
if( !ui_precache->value )
return;
PIC_Load( UI_LEFTARROW );
PIC_Load( UI_LEFTARROWFOCUS );
PIC_Load( UI_RIGHTARROW );
PIC_Load( UI_RIGHTARROWFOCUS );
PIC_Load( UI_UPARROW );
PIC_Load( UI_UPARROWFOCUS );
PIC_Load( UI_DOWNARROW );
PIC_Load( UI_DOWNARROWFOCUS );
if( ui_precache->value == 1 )
return;
UI_Main_Precache();
UI_NewGame_Precache();
UI_LoadGame_Precache();
UI_SaveGame_Precache();
UI_SaveLoad_Precache();
UI_MultiPlayer_Precache();
UI_Options_Precache();
UI_InternetGames_Precache();
UI_LanGame_Precache();
UI_PlayerSetup_Precache();
UI_Controls_Precache();
UI_AdvControls_Precache();
UI_GameOptions_Precache();
UI_CreateGame_Precache();
UI_Audio_Precache();
UI_Video_Precache();
UI_VidOptions_Precache();
UI_VidModes_Precache();
UI_CustomGame_Precache();
UI_Credits_Precache();
}
void UI_ParseColor( char *&pfile, int *outColor )
{
int i, color[3];
char token[1024];
memset( color, 0xFF, sizeof( color ));
for( i = 0; i < 3; i++ )
{
pfile = COM_ParseFile( pfile, token );
if( !pfile ) break;
color[i] = atoi( token );
}
*outColor = PackRGB( color[0], color[1], color[2] );
}
void UI_ApplyCustomColors( void )
{
char *afile = (char *)LOAD_FILE( "gfx/shell/colors.lst", NULL );
char *pfile = afile;
char token[1024];
if( !afile )
{
// not error, not warning, just notify
Con_Printf( "UI_SetColors: colors.lst not found\n" );
return;
}
while(( pfile = COM_ParseFile( pfile, token )) != NULL )
{
if( !stricmp( token, "HELP_COLOR" ))
{
UI_ParseColor( pfile, &uiColorHelp );
}
else if( !stricmp( token, "PROMPT_BG_COLOR" ))
{
UI_ParseColor( pfile, &uiPromptBgColor );
}
else if( !stricmp( token, "PROMPT_TEXT_COLOR" ))
{
UI_ParseColor( pfile, &uiPromptTextColor );
}
else if( !stricmp( token, "PROMPT_FOCUS_COLOR" ))
{
UI_ParseColor( pfile, &uiPromptFocusColor );
}
else if( !stricmp( token, "INPUT_TEXT_COLOR" ))
{
UI_ParseColor( pfile, &uiInputTextColor );
}
else if( !stricmp( token, "INPUT_BG_COLOR" ))
{
UI_ParseColor( pfile, &uiInputBgColor );
}
else if( !stricmp( token, "INPUT_FG_COLOR" ))
{
UI_ParseColor( pfile, &uiInputFgColor );
}
else if( !stricmp( token, "CON_TEXT_COLOR" ))
{
UI_ParseColor( pfile, &uiColorConsole );
}
}
int r, g, b;
UnpackRGB( r, g, b, uiColorConsole );
ConsoleSetColor( r, g, b );
FREE_FILE( afile );
}
static void UI_LoadBackgroundMapList( void )
{
if( !g_engfuncs.pfnFileExists( "scripts/chapterbackgrounds.txt", TRUE ))
return;
char *afile = (char *)LOAD_FILE( "scripts/chapterbackgrounds.txt", NULL );
char *pfile = afile;
char token[1024];
uiStatic.bgmapcount = 0;
if( !afile )
{
Con_Printf( "UI_LoadBackgroundMapList: chapterbackgrounds.txt not found\n" );
return;
}
while(( pfile = COM_ParseFile( pfile, token )) != NULL )
{
// skip the numbers (old format list)
if( isdigit( token[0] )) continue;
strncpy( uiStatic.bgmaps[uiStatic.bgmapcount], token, sizeof( uiStatic.bgmaps[0] ) - 1 );
if( ++uiStatic.bgmapcount > UI_MAX_BGMAPS )
break; // list is full
}
FREE_FILE( afile );
}
/*
=================
UI_VidInit
=================
*/
int UI_VidInit( void )
{
UI_Precache ();
uiStatic.scaleX = ScreenWidth / 1024.0f;
uiStatic.scaleY = ScreenHeight / 768.0f;
// move cursor to screen center
uiStatic.cursorX = ScreenWidth >> 1;
uiStatic.cursorY = ScreenHeight >> 1;
uiStatic.outlineWidth = 4;
uiStatic.sliderWidth = 6;
// all menu buttons have the same view sizes
uiStatic.buttons_draw_width = UI_BUTTONS_WIDTH;
uiStatic.buttons_draw_height = UI_BUTTONS_HEIGHT;
UI_ScaleCoords( NULL, NULL, &uiStatic.outlineWidth, NULL );
UI_ScaleCoords( NULL, NULL, &uiStatic.sliderWidth, NULL );
UI_ScaleCoords( NULL, NULL, &uiStatic.buttons_draw_width, &uiStatic.buttons_draw_height );
// trying to load colors.lst
UI_ApplyCustomColors ();
// trying to load chapterbackgrounds.txt
UI_LoadBackgroundMapList ();
// register menu font
uiStatic.hFont = PIC_Load( "#XASH_SYSTEMFONT_001.bmp", menufont_bmp, sizeof( menufont_bmp ));
UI_LoadBackgroundImage ();
#if 0
FILE *f;
// dump menufont onto disk
f = fopen( "menufont.bmp", "wb" );
fwrite( menufont_bmp, sizeof( menufont_bmp ), 1, f );
fclose( f );
#endif
// reload all menu buttons
UI_LoadBmpButtons ();
// now recalc all the menus in stack
for( int i = 0; i < uiStatic.menuDepth; i++ )
{
menuFramework_s *item = uiStatic.menuStack[i];
// do vid restart for all pushed elements
if( item && item->vidInitFunc )
item->vidInitFunc();
}
return 1;
}
/*
=================
UI_Init
=================
*/
void UI_Init( void )
{
// register our cvars and commands
ui_precache = CVAR_REGISTER( "ui_precache", "0", FCVAR_ARCHIVE );
ui_showmodels = CVAR_REGISTER( "ui_showmodels", "0", FCVAR_ARCHIVE );
ui_videoconfig = CVAR_REGISTER( "video_config", "high", FCVAR_ARCHIVE );
Cmd_AddCommand( "menu_main", UI_Main_Menu );
Cmd_AddCommand( "menu_newgame", UI_NewGame_Menu );
Cmd_AddCommand( "menu_loadgame", UI_LoadGame_Menu );
Cmd_AddCommand( "menu_savegame", UI_SaveGame_Menu );
Cmd_AddCommand( "menu_saveload", UI_SaveLoad_Menu );
Cmd_AddCommand( "menu_multiplayer", UI_MultiPlayer_Menu );
Cmd_AddCommand( "menu_options", UI_Options_Menu );
Cmd_AddCommand( "menu_langame", UI_LanGame_Menu );
Cmd_AddCommand( "menu_internetgames", UI_InternetGames_Menu );
Cmd_AddCommand( "menu_playersetup", UI_PlayerSetup_Menu );
Cmd_AddCommand( "menu_controls", UI_Controls_Menu );
Cmd_AddCommand( "menu_advcontrols", UI_AdvControls_Menu );
Cmd_AddCommand( "menu_gameoptions", UI_GameOptions_Menu );
Cmd_AddCommand( "menu_creategame", UI_CreateGame_Menu );
Cmd_AddCommand( "menu_audio", UI_Audio_Menu );
Cmd_AddCommand( "menu_video", UI_Video_Menu );
Cmd_AddCommand( "menu_vidoptions", UI_VidOptions_Menu );
Cmd_AddCommand( "menu_vidmodes", UI_VidModes_Menu );
Cmd_AddCommand( "menu_customgame", UI_CustomGame_Menu );
CHECK_MAP_LIST( TRUE );
memset( uiEmptyString, ' ', sizeof( uiEmptyString )); // HACKHACK
uiStatic.initialized = true;
// setup game info
GetGameInfo( &gMenu.m_gameinfo );
// load custom strings
UI_LoadCustomStrings();
//CR
UI_InitTitleAnim();
}
/*
=================
UI_Shutdown
=================
*/
void UI_Shutdown( void )
{
if( !uiStatic.initialized )
return;
Cmd_RemoveCommand( "menu_main" );
Cmd_RemoveCommand( "menu_newgame" );
Cmd_RemoveCommand( "menu_loadgame" );
Cmd_RemoveCommand( "menu_savegame" );
Cmd_RemoveCommand( "menu_saveload" );
Cmd_RemoveCommand( "menu_multiplayer" );
Cmd_RemoveCommand( "menu_options" );
Cmd_RemoveCommand( "menu_internetgames" );
Cmd_RemoveCommand( "menu_langame" );
Cmd_RemoveCommand( "menu_playersetup" );
Cmd_RemoveCommand( "menu_controls" );
Cmd_RemoveCommand( "menu_advcontrols" );
Cmd_RemoveCommand( "menu_gameoptions" );
Cmd_RemoveCommand( "menu_creategame" );
Cmd_RemoveCommand( "menu_audio" );
Cmd_RemoveCommand( "menu_video" );
Cmd_RemoveCommand( "menu_vidoptions" );
Cmd_RemoveCommand( "menu_vidmodes" );
Cmd_RemoveCommand( "menu_advanced" );
Cmd_RemoveCommand( "menu_performance" );
Cmd_RemoveCommand( "menu_network" );
Cmd_RemoveCommand( "menu_defaults" );
Cmd_RemoveCommand( "menu_cinematics" );
Cmd_RemoveCommand( "menu_customgame" );
memset( &uiStatic, 0, sizeof( uiStatic_t ));
}