Paranoia2/mainui/basemenu.cpp

1609 lines
36 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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 ё
if( ch == 0xB8 ) ch = (byte)'е';
if( ch == 0xA8 ) ch = (byte)'Е';
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 ));
}