/* =========================================================================== 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(); } }