/*
===========================================================================
Wolfenstein: Enemy Territory GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
This file is part of the Wolfenstein: Enemy Territory GPL Source Code (Wolf ET Source Code).
Wolf ET Source Code 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.
Wolf ET Source Code 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 Wolf ET Source Code. If not, see .
In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
// cg_window.c: cgame window handling
// ----------------------------------
// 24 Jul 02
// rhea@OrangeSmoothie.org
//
#include "cg_local.h"
#include "../ui/ui_shared.h"
extern pmove_t cg_pmove; // cg_predict.c
vec4_t colorClear = { 0.0f, 0.0f, 0.0f, 0.0f }; // Transparent
vec4_t colorBrown1 = { 0.3f, 0.2f, 0.1f, 0.9f }; // Brown
vec4_t colorGreen1 = { 0.21f, 0.3f, 0.0f, 0.9f }; // Greenish (darker)
vec4_t colorGreen2 = { 0.305f, 0.475f, 0.305f, 0.48f }; // Slightly off from default fill
void CG_createStatsWindow( void ) {
cg_window_t *sw = CG_windowAlloc( WFX_TEXTSIZING | WFX_FADEIN | /*WFX_SCROLLUP|*/ WFX_TRUETYPE, 110 );
cg.statsWindow = sw;
if ( sw == NULL ) {
return;
}
// Window specific
sw->id = WID_STATS;
sw->fontScaleX = cf_wstats.value * 0.2f;
sw->fontScaleY = cf_wstats.value * 0.2f;
// sw->x = (cg.snap->ps.pm_type == PM_INTERMISSION) ? 10 : 160;
// sw->y = (cg.snap->ps.pm_type == PM_INTERMISSION) ? -20 : -7; // Align from bottom minus offset and height
sw->x = ( cg.snap->ps.pm_type == PM_INTERMISSION ) ? 10 : 4;
sw->y = ( cg.snap->ps.pm_type == PM_INTERMISSION ) ? -20 : -100; // Align from bottom minus offset and height
}
void CG_createTopShotsWindow( void ) {
cg_window_t *sw = CG_windowAlloc( WFX_TEXTSIZING | WFX_FLASH | WFX_FADEIN | WFX_SCROLLUP | WFX_TRUETYPE, 190 );
cg.topshotsWindow = sw;
if ( sw == NULL ) {
return;
}
// Window specific
sw->id = WID_TOPSHOTS;
sw->fontScaleX = cf_wtopshots.value * 0.2f;
sw->fontScaleY = cf_wtopshots.value * 0.2f;
sw->x = ( cg.snap->ps.pm_type == PM_INTERMISSION ) ? -10 : -20;
sw->y = ( cg.snap->ps.pm_type == PM_INTERMISSION ) ? -20 : -60; // Align from bottom minus offset and height
sw->flashMidpoint = sw->flashPeriod * 0.8f;
memcpy( &sw->colorBackground2, &colorGreen2, sizeof( vec4_t ) );
}
void CG_createMOTDWindow( void ) {
const char *str = CG_ConfigString( CS_CUSTMOTD + 0 );
if ( str != NULL && *str != 0 ) {
int i;
cg_window_t *sw = CG_windowAlloc( WFX_TEXTSIZING | WFX_FADEIN, 500 );
cg.motdWindow = sw;
if ( sw == NULL ) {
return;
}
// Window specific
sw->id = WID_MOTD;
sw->fontScaleX = 1.0f;
sw->fontScaleY = 1.0f;
sw->x = 10;
sw->y = -36;
sw->flashMidpoint = sw->flashPeriod * 0.8f;
memcpy( &sw->colorBackground2, &colorGreen2, sizeof( vec4_t ) );
// Copy all MOTD info into the window
cg.windowCurrent = sw;
for ( i = 0; i < MAX_MOTDLINES; i++ ) {
str = CG_ConfigString( CS_CUSTMOTD + i );
if ( str != NULL && *str != 0 ) {
CG_printWindow( (char*)str );
} else {
break;
}
}
}
}
/*
void CG_createWstatsMsgWindow(void)
{
cg_window_t *sw = CG_windowAlloc(WFX_TEXTSIZING|WFX_FLASH, 0);
cg.msgWstatsWindow = sw;
if(sw == NULL) return;
// Window specific
sw->id = WID_NONE;
sw->fontScaleX = 1.0f;
sw->fontScaleY = 1.0f;
sw->x = 300;
sw->y = -1;
sw->flashPeriod = 800;
sw->flashMidpoint = sw->flashPeriod * 0.5f;
memcpy(&sw->colorBorder, &colorClear, sizeof(vec4_t));
memcpy(&sw->colorBackground, &colorBrown1, sizeof(vec4_t));
memcpy(&sw->colorBackground2, &colorGreen1, sizeof(vec4_t));
cg.windowCurrent = sw;
CG_printWindow("^3Stats (+wstats)");
}
void CG_createWtopshotsMsgWindow(void)
{
cg_window_t *sw = CG_windowAlloc(WFX_TEXTSIZING|WFX_FLASH, 0);
cg.msgWtopshotsWindow = sw;
if(sw == NULL) return;
// Window specific
sw->id = WID_NONE;
sw->fontScaleX = 1.0f;
sw->fontScaleY = 1.0f;
sw->x = -20;
sw->y = -1;
sw->flashPeriod = 1000;
sw->flashMidpoint = sw->flashPeriod * 0.7f;
memcpy(&sw->colorBorder, &colorClear, sizeof(vec4_t));
memcpy(&sw->colorBackground, &colorBrown1, sizeof(vec4_t));
memcpy(&sw->colorBackground2, &colorGreen1, sizeof(vec4_t));
cg.windowCurrent = sw;
CG_printWindow("^3Rankings (+wtopshots)");
}
*/
/*
void CG_createDemoHelpWindow(void)
{
int i;
cg_window_t *sw = CG_windowAlloc(WFX_TEXTSIZING|WFX_FADEIN|WFX_TRUETYPE, 250);
const char *help[] = {
"^2TAB ^Nscores",
"^2F1-F5 ^Navidemo record",
"^2F11-F12 ^Nscreenshot",
" ", " ",
"^2KP_DOWN ^Nslow down (--)",
"^2KP_LEFT ^Nslow down (-)",
"^2KP_UP ^Nspeed up (++)",
"^2KP_RIGHT ^Nspeed up (+)",
"^2SPACE ^Nnormal speed",
" ", " ",
"^2ENTER ^NExternal view",
"^2LFT/RGHT ^NChange angle",
"^2UP/DOWN ^NMove in/out"
};
const char *mvhelp[] = {
" ", " ",
"^2MOUSE1 ^NSelect/move view",
"^2MOUSE2 ^NSwap w/main view",
"^2MOUSE3 ^NToggle on/off",
"^2SHIFT ^NHold to resize",
"^2KP_PGUP ^NEnable a view",
"^2KP_PGDN ^NClose a view"
};
cg.demohelpWindow = sw;
if(sw == NULL) return;
// Window specific
sw->id = WID_DEMOHELP;
sw->fontScaleX = 0.2f;
sw->fontScaleY = 0.3f;
sw->x = 2;
sw->y = 155;
sw->flashMidpoint = sw->flashPeriod * 0.8f;
memcpy(&sw->colorBackground2, &colorGreen2, sizeof(vec4_t));
cg.windowCurrent = sw;
for(i=0; i 1) {
for(i=0; iid = WID_SPECHELP;
sw->fontScaleX = 0.2f;
sw->fontScaleY = 0.3f;
sw->x = 2;
sw->y = 155;
sw->flashMidpoint = sw->flashPeriod * 0.8f;
memcpy(&sw->colorBackground2, &colorGreen2, sizeof(vec4_t));
for(i=0; i maxlen) {
maxlen = len;
}
}
}
cg.windowCurrent = sw;
format = va("^2%%%ds ^N%%s", maxlen);
for(i=0; ieffects = fx;
w->fontScaleX = 0.25;
w->fontScaleY = 0.25;
w->flashPeriod = 1000;
w->flashMidpoint = w->flashPeriod / 2;
w->id = WID_NONE;
w->inuse = qtrue;
w->lineCount = 0;
w->state = ( fx >= WFX_FADEIN ) ? WSTATE_START : WSTATE_COMPLETE;
w->targetTime = ( startupLength > 0 ) ? startupLength : 0;
w->time = trap_Milliseconds();
w->x = 0;
w->y = 0;
memcpy( &w->colorBorder, &colorGeneralBorder, sizeof( vec4_t ) );
memcpy( &w->colorBackground, &colorGeneralFill, sizeof( vec4_t ) );
}
// Reserve a window
cg_window_t *CG_windowAlloc( int fx, int startupLength ) {
int i;
cg_window_t *w;
cg_windowHandler_t *wh = &cg.winHandler;
if ( wh->numActiveWindows == MAX_WINDOW_COUNT ) {
return( NULL );
}
for ( i = 0; i < MAX_WINDOW_COUNT; i++ ) {
w = &wh->window[i];
if ( w->inuse == qfalse ) {
CG_windowReset( w, fx, startupLength );
wh->activeWindows[wh->numActiveWindows++] = i;
return( w );
}
}
// Fail if we're a full airplane
return( NULL );
}
// Free up a window reservation
void CG_windowFree( cg_window_t *w ) {
int i, j;
cg_windowHandler_t *wh = &cg.winHandler;
if ( w == NULL ) {
return;
}
if ( w->effects >= WFX_FADEIN && w->state != WSTATE_OFF && w->inuse == qtrue ) {
w->state = WSTATE_SHUTDOWN;
w->time = trap_Milliseconds();
return;
}
for ( i = 0; i < wh->numActiveWindows; i++ ) {
if ( w == &wh->window[wh->activeWindows[i]] ) {
for ( j = i; j < wh->numActiveWindows; j++ ) {
if ( j + 1 < wh->numActiveWindows ) {
wh->activeWindows[j] = wh->activeWindows[j + 1];
}
}
w->id = WID_NONE;
w->inuse = qfalse;
w->state = WSTATE_OFF;
CG_removeStrings( w );
wh->numActiveWindows--;
break;
}
}
}
void CG_windowCleanup( void ) {
int i;
cg_window_t *w;
cg_windowHandler_t *wh = &cg.winHandler;
for ( i = 0; i < wh->numActiveWindows; i++ ) {
w = &wh->window[wh->activeWindows[i]];
if ( !w->inuse || w->state == WSTATE_OFF ) {
CG_windowFree( w );
i--;
}
}
}
void CG_demoAviFPSDraw( void ) {
qboolean fKeyDown = cgs.fKeyPressed[K_F1] | cgs.fKeyPressed[K_F2] | cgs.fKeyPressed[K_F3] | cgs.fKeyPressed[K_F4] | cgs.fKeyPressed[K_F5];
if ( cg.demoPlayback && fKeyDown && cgs.aviDemoRate >= 0 ) {
CG_DrawStringExt( 42, 425,
( ( cgs.aviDemoRate > 0 ) ? va( "^3Record AVI @ ^7%d^2fps", cgs.aviDemoRate ) : "^1Stop AVI Recording" ),
colorWhite, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT - 2, 0 );
}
}
void CG_demoTimescaleDraw( void ) {
if ( cg.demoPlayback && cgs.timescaleUpdate > cg.time && demo_drawTimeScale.integer != 0 ) {
char *s = va( "^3TimeScale: ^7%.1f", cg_timescale.value );
int w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH;
CG_FillRect( 42 - 2, 400, w + 5, SMALLCHAR_HEIGHT + 3, colorDkGreen );
CG_DrawRect( 42 - 2, 400, w + 5, SMALLCHAR_HEIGHT + 3, 1, colorMdYellow );
CG_DrawStringExt( 42, 400, s, colorWhite, qfalse, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 );
}
}
// Main window-drawing handler
void CG_windowDraw( void ) {
int h, x, y, i, j, milli, t_offset, tmp;
cg_window_t *w;
qboolean fCleanup = qfalse;
// Gordon: FIXME, the limbomenu var no longer exists
qboolean fAllowMV = ( cg.snap != NULL && cg.snap->ps.pm_type != PM_INTERMISSION /*&& !cg.limboMenu*/ );
vec4_t *bg;
vec4_t textColor, borderColor, bgColor;
if ( cg.winHandler.numActiveWindows == 0 ) {
// Draw these for demoplayback no matter what
CG_demoAviFPSDraw();
CG_demoTimescaleDraw();
return;
}
milli = trap_Milliseconds();
memcpy( textColor, colorWhite, sizeof( vec4_t ) );
// Mouse cursor position for MV highlighting (offset for cursor pointer position)
// Also allow for swingcam toggling
if ( cg.mvTotalClients > 0 && fAllowMV ) {
CG_cursorUpdate();
}
for ( i = 0; i < cg.winHandler.numActiveWindows; i++ ) {
w = &cg.winHandler.window[cg.winHandler.activeWindows[i]];
if ( !w->inuse || w->state == WSTATE_OFF ) {
fCleanup = qtrue;
continue;
}
// Multiview rendering has its own handling
if ( w->effects & WFX_MULTIVIEW ) {
if ( w != cg.mvCurrentMainview && fAllowMV ) {
CG_mvDraw( w );
}
continue;
}
if ( w->effects & WFX_TEXTSIZING ) {
CG_windowNormalizeOnText( w );
w->effects &= ~WFX_TEXTSIZING;
}
bg = ( ( w->effects & WFX_FLASH ) && ( milli % w->flashPeriod ) > w->flashMidpoint ) ? &w->colorBackground2 : &w->colorBackground;
h = w->h;
x = w->x;
y = w->y;
t_offset = milli - w->time;
textColor[3] = 1.0f;
memcpy( &borderColor, w->colorBorder, sizeof( vec4_t ) );
memcpy( &bgColor, bg, sizeof( vec4_t ) );
// TODO: Add in support for ALL scrolling effects
if ( w->state == WSTATE_START ) {
tmp = w->targetTime - t_offset;
if ( w->effects & WFX_SCROLLUP ) {
if ( tmp > 0 ) {
y += ( 480 - y ) * tmp / w->targetTime; //(100 * tmp / w->targetTime) / 100;
} else {
w->state = WSTATE_COMPLETE;
}
w->curY = y;
}
if ( w->effects & WFX_FADEIN ) {
if ( tmp > 0 ) {
textColor[3] = (float)( (float)t_offset / (float)w->targetTime );
} else { w->state = WSTATE_COMPLETE;}
}
} else if ( w->state == WSTATE_SHUTDOWN ) {
tmp = w->targetTime - t_offset;
if ( w->effects & WFX_SCROLLUP ) {
if ( tmp > 0 ) {
y = w->curY + ( 480 - w->y ) * t_offset / w->targetTime; //(100 * t_offset / w->targetTime) / 100;
}
if ( tmp < 0 || y >= 480 ) {
w->state = WSTATE_OFF;
fCleanup = qtrue;
continue;
}
}
if ( w->effects & WFX_FADEIN ) {
if ( tmp > 0 ) {
textColor[3] -= (float)( (float)t_offset / (float)w->targetTime );
} else {
textColor[3] = 0.0f;
w->state = WSTATE_OFF;
}
}
}
borderColor[3] *= textColor[3];
bgColor[3] *= textColor[3];
CG_FillRect( x, y, w->w, h, bgColor );
CG_DrawRect( x, y, w->w, h, 1, borderColor );
x += 5;
y -= ( w->effects & WFX_TRUETYPE ) ? 3 : 0;
for ( j = w->lineCount - 1; j >= 0; j-- ) {
if ( w->effects & WFX_TRUETYPE ) {
// CG_Text_Paint(x, y + h, w->fontScale, textColor, (char*)w->lineText[j], 0.0f, 0, 0);
CG_Text_Paint_Ext( x, y + h, w->fontScaleX, w->fontScaleY, textColor,
(char*)w->lineText[j], 0.0f, 0, 0, &cgs.media.limboFont2 );
}
h -= ( w->lineHeight[j] + 3 );
if ( !( w->effects & WFX_TRUETYPE ) ) {
CG_DrawStringExt2( x, y + h, (char*)w->lineText[j], textColor,
qfalse, qtrue, w->fontWidth, w->fontHeight, 0 );
}
}
}
// Wedge in MV info overlay
if ( cg.mvTotalClients > 0 && fAllowMV ) {
CG_mvOverlayDisplay();
}
// Extra rate info
CG_demoAviFPSDraw();
CG_demoTimescaleDraw();
// Mouse cursor lays on top of everything
if ( cg.mvTotalClients > 0 && cg.time < cgs.cursorUpdate && fAllowMV ) {
//CG_DrawPic(cgs.cursorX - CURSOR_OFFSETX, cgs.cursorY - CURSOR_OFFSETY, 32, 32, cgs.media.cursor);
CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon );
}
if ( fCleanup ) {
CG_windowCleanup();
}
}
// Set the window width and height based on the windows text/font parameters
void CG_windowNormalizeOnText( cg_window_t *w ) {
int i, tmp;
if ( w == NULL ) {
return;
}
w->w = 0;
w->h = 0;
if ( !( w->effects & WFX_TRUETYPE ) ) {
w->fontWidth = w->fontScaleX * WINDOW_FONTWIDTH;
w->fontHeight = w->fontScaleY * WINDOW_FONTHEIGHT;
}
for ( i = 0; i < w->lineCount; i++ ) {
if ( w->effects & WFX_TRUETYPE ) {
tmp = CG_Text_Width_Ext( (char*)w->lineText[i], w->fontScaleX, 0, &cgs.media.limboFont2 );
} else {
tmp = CG_DrawStrlen( (char*)w->lineText[i] ) * w->fontWidth;
}
if ( tmp > w->w ) {
w->w = tmp;
}
}
for ( i = 0; i < w->lineCount; i++ ) {
if ( w->effects & WFX_TRUETYPE ) {
w->lineHeight[i] = CG_Text_Height_Ext( (char*)w->lineText[i], w->fontScaleY, 0, &cgs.media.limboFont2 );
} else {
w->lineHeight[i] = w->fontHeight;
}
w->h += w->lineHeight[i] + 3;
}
// Border + margins
w->w += 10;
w->h += 3;
// Set up bottom alignment
if ( w->x < 0 ) {
w->x += 640 - w->w;
}
if ( w->y < 0 ) {
w->y += 480 - w->h;
}
}
void CG_printWindow( char *str ) {
int pos = 0, pos2 = 0;
char buf[MAX_STRING_CHARS];
cg_window_t *w = cg.windowCurrent;
if ( w == NULL ) {
return;
}
// Silly logic for a strict format
Q_strncpyz( buf, str, MAX_STRING_CHARS );
while ( buf[pos] > 0 && w->lineCount < MAX_WINDOW_LINES ) {
if ( buf[pos] == '\n' ) {
if ( pos2 == pos ) {
if ( !CG_addString( w, " " ) ) {
return;
}
} else {
buf[pos] = 0;
if ( !CG_addString( w, buf + pos2 ) ) {
return;
}
}
pos2 = ++pos;
continue;
}
pos++;
}
if ( pos2 < pos ) {
CG_addString( w, buf + pos2 );
}
}
//
// String buffer handling
//
void CG_initStrings( void ) {
int i;
for ( i = 0; i < MAX_STRINGS; i++ ) {
cg.aStringPool[i].fActive = qfalse;
cg.aStringPool[i].str[0] = 0;
}
}
qboolean CG_addString( cg_window_t *w, char *buf ) {
int i;
// Check if we're reusing the current buf
if ( w->lineText[w->lineCount] != NULL ) {
for ( i = 0; i < MAX_STRINGS; i++ ) {
if ( !cg.aStringPool[i].fActive ) {
continue;
}
if ( w->lineText[w->lineCount] == (char *)&cg.aStringPool[i].str ) {
w->lineCount++;
cg.aStringPool[i].fActive = qtrue;
strcpy( cg.aStringPool[i].str, buf );
return( qtrue );
}
}
}
for ( i = 0; i < MAX_STRINGS; i++ ) {
if ( !cg.aStringPool[i].fActive ) {
cg.aStringPool[i].fActive = qtrue;
strcpy( cg.aStringPool[i].str, buf );
w->lineText[w->lineCount++] = (char *)&cg.aStringPool[i].str;
return( qtrue );
}
}
return( qfalse );
}
void CG_removeStrings( cg_window_t *w ) {
int i, j;
for ( i = 0; i < w->lineCount; i++ ) {
char *ref = w->lineText[i];
for ( j = 0; j < MAX_STRINGS; j++ ) {
if ( !cg.aStringPool[j].fActive ) {
continue;
}
if ( ref == (char *)&cg.aStringPool[j].str ) {
w->lineText[i] = NULL;
cg.aStringPool[j].fActive = qfalse;
cg.aStringPool[j].str[0] = 0;
break;
}
}
}
}
//
// cgame cursor handling
//
// Mouse overlay for controlling multiview windows
void CG_cursorUpdate( void ) {
int i, j, x;
float nx, ny;
int nSelectedWindow = -1;
cg_window_t *w;
cg_windowHandler_t *wh = &cg.winHandler;
qboolean fFound = qfalse, fUpdateOverlay = qfalse;
qboolean fSelect, fResize;
// Get cursor current position (when connected to a server)
if ( !cg.demoPlayback ) {
// Allow for limbo'd updates as well
trap_GetUserCmd( trap_GetCurrentCmdNumber(), &cg_pmove.cmd );
nx = 640.0 * ( 65536.0 - cg_pmove.cmd.angles[1] ) / 65536.0;
ny = 480.0 / 65536.0 * ( ( int_m_pitch.value < 0.0 ) ? ( 65536.0 - cg_pmove.cmd.angles[0] ) : cg_pmove.cmd.angles[0] );
fSelect = ( ( cg_pmove.cmd.buttons & BUTTON_ATTACK ) != 0 );
if ( cgs.cursorX == (int)nx && cgs.cursorY == (int)ny && !fSelect ) {
return;
}
fResize = ( ( cg_pmove.cmd.buttons & BUTTON_SPRINT ) != 0 );
cgs.cursorUpdate = cg.time + 5000;
cgs.cursorX = nx;
cgs.cursorY = ny;
} else {
// Already updated in the keycatcher
nx = cgs.cursorX;
ny = cgs.cursorY;
fSelect = cgs.fSelect;
fResize = cgs.fResize;
}
// For mm4
cg.mvCurrentActive = cg.mvCurrentMainview;
// For overlay highlights
for ( i = 0; i < cg.mvTotalClients; i++ ) {
cg.mvOverlay[i].fActive = qfalse;
}
for ( i = wh->numActiveWindows - 1; i >= 0; i-- ) {
w = &wh->window[wh->activeWindows[i]];
if ( ( w->effects & WFX_MULTIVIEW ) && w != cg.mvCurrentMainview ) {
// Mouse/window detection
// If the current window is selected, and the button is down, then allow the update
// to occur, as quick mouse movements can move it past the window borders
if ( !fFound &&
(
( ( w->mvInfo & MV_SELECTED ) && fSelect ) ||
( !fSelect && nx >= w->x && nx < w->x + w->w && ny >= w->y && ny < w->y + w->h )
) ) {
if ( !( w->mvInfo & MV_SELECTED ) ) {
w->mvInfo |= MV_SELECTED;
nSelectedWindow = i;
}
// If not dragging/resizing, prime for later update
if ( !fSelect ) {
w->m_x = -1.0f;
w->m_y = -1.0f;
} else {
if ( w->m_x > 0 && w->m_y > 0 ) {
if ( fResize ) {
w->w += nx - w->m_x;
if ( w->x + w->w > 640 - 2 ) {
w->w = 640 - 2 - w->x;
}
if ( w->w < 64 ) {
w->w = 64;
}
w->h += ny - w->m_y;
if ( w->y + w->h > 480 - 2 ) {
w->h = 480 - 2 - w->y;
}
if ( w->h < 48 ) {
w->h = 48;
}
} else {
w->x += nx - w->m_x;
if ( w->x + w->w > 640 - 2 ) {
w->x = 640 - 2 - w->w;
}
if ( w->x < 2 ) {
w->x = 2;
}
w->y += ny - w->m_y;
if ( w->y + w->h > 480 - 2 ) {
w->y = 480 - 2 - w->h;
}
if ( w->y < 2 ) {
w->y = 2;
}
}
}
w->m_x = nx;
w->m_y = ny;
}
fFound = qtrue;
cg.mvCurrentActive = w;
// Reset mouse info for window if it loses focuse
} else if ( w->mvInfo & MV_SELECTED ) {
fUpdateOverlay = qtrue;
w->m_x = -1.0f;
w->m_y = -1.0f;
w->mvInfo &= ~MV_SELECTED;
if ( fFound ) {
break; // Small optimization: we've found a new window, and cleared the old focus
}
}
}
}
nx = (float)( MVINFO_RIGHT - ( MVINFO_TEXTSIZE * 3 ) );
ny = (float)( MVINFO_TOP + ( MVINFO_TEXTSIZE + 1 ) );
// Highlight corresponding active window's overlay element
if ( fFound ) {
for ( i = 0; i < cg.mvTotalClients; i++ ) {
if ( cg.mvOverlay[i].pID == ( cg.mvCurrentActive->mvInfo & MV_PID ) ) {
cg.mvOverlay[i].fActive = qtrue;
break;
}
}
}
// Check MV overlay detection here for better perf with more text elements
// General boundary check
else {
// Ugh, have to loop through BOTH team lists
int vOffset = 0;
for ( i = TEAM_AXIS; i <= TEAM_ALLIES; i++ ) {
if ( cg.mvTotalTeam[i] == 0 ) {
continue;
}
if ( cgs.cursorX >= nx && cgs.cursorY >= ny && cgs.cursorX < MVINFO_RIGHT &&
cgs.cursorY < ny + ( cg.mvTotalTeam[i] * ( MVINFO_TEXTSIZE + 1 ) ) ) {
int pos = (int)( cgs.cursorY - ny ) / ( MVINFO_TEXTSIZE + 1 );
if ( pos < cg.mvTotalTeam[i] ) {
int x = MVINFO_RIGHT - cg.mvOverlay[( cg.mvTeamList[i][pos] )].width;
int y = MVINFO_TOP + vOffset + ( ( pos + 1 ) * ( MVINFO_TEXTSIZE + 1 ) );
// See if we're really over something
if ( cgs.cursorX >= x && cgs.cursorY >= y &&
cgs.cursorX <= MVINFO_RIGHT &&
cgs.cursorY <= y + MVINFO_TEXTSIZE ) {
// Perform any other window handling here for MV
// views based on element selection
cg.mvOverlay[( cg.mvTeamList[i][pos] )].fActive = qtrue;
w = CG_mvClientLocate( cg.mvOverlay[( cg.mvTeamList[i][pos] )].pID );
if ( w != NULL ) {
cg.mvCurrentActive = w;
}
if ( fSelect ) {
if ( w != NULL ) {
// Swap window-view with mainview
if ( w != cg.mvCurrentMainview ) {
CG_mvMainviewSwap( w );
}
} else {
// Swap non-view with mainview
cg.mvCurrentMainview->mvInfo = ( cg.mvCurrentMainview->mvInfo & ~MV_PID ) |
( cg.mvOverlay[cg.mvTeamList[i][pos]].pID & MV_PID );
fUpdateOverlay = qtrue;
}
}
}
}
}
vOffset += ( cg.mvTotalTeam[i] + 2 ) * ( MVINFO_TEXTSIZE + 1 );
ny += vOffset;
}
}
// If we have a new highlight, reorder so our highlight is always
// drawn last (on top of all other windows)
if ( nSelectedWindow >= 0 ) {
fUpdateOverlay = qtrue;
x = wh->activeWindows[nSelectedWindow];
for ( j = nSelectedWindow; j < wh->numActiveWindows - 1; j++ ) {
wh->activeWindows[j] = wh->activeWindows[j + 1];
}
wh->activeWindows[wh->numActiveWindows - 1] = x;
}
// Finally, sync the overlay, if needed
if ( fUpdateOverlay ) {
CG_mvOverlayUpdate();
}
}