//========= Copyright � 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // // $NoKeywords: $ //============================================================================= #include "hud.h" #include "cl_util.h" #include "common/cl_entity.h" #include "common/triangleapi.h" #include "vgui_TeamFortressViewport.h" #include "vgui_SpectatorPanel.h" #include "common/hltv.h" #include "pm_shared/pm_shared.h" #include "pm_shared/pm_defs.h" #include "common/pmtrace.h" #include "common/entity_types.h" // these are included for the math functions #include "common/com_model.h" #include "common/demo_api.h" #include "common/event_api.h" #include "studio_util.h" #include "common/screenfade.h" #include "util/STLUtil.h" #include "mod/AvHTitles.h" #include "mod/AvHSprites.h" #pragma warning(disable: 4244) extern int iJumpSpectator; extern float vJumpOrigin[3]; extern float vJumpAngles[3]; extern void V_GetInEyePos(int entity, float * origin, float * angles ); extern void V_ResetChaseCam(); extern void V_GetChasePos(int target, float * cl_angles, float * origin, float * angles); extern void VectorAngles( const float *forward, float *angles ); extern "C" void NormalizeAngles( float *angles ); extern float * GetClientColor( int clientIndex ); extern vec3_t v_origin; // last view origin extern vec3_t v_angles; // last view angle extern vec3_t v_cl_angles; // last client/mouse angle extern vec3_t v_sim_org; // last sim origin void SpectatorMode(void) { if ( gEngfuncs.Cmd_Argc() <= 1 ) { gEngfuncs.Con_Printf( "usage: spec_mode \n" ); return; } // SetModes() will decide if command is executed on server or local if ( gEngfuncs.Cmd_Argc() == 2 ) gHUD.m_Spectator.SetMode( atoi( gEngfuncs.Cmd_Argv(1) )); //else if ( gEngfuncs.Cmd_Argc() == 3 ) // gHUD.m_Spectator.SetMode( atoi( gEngfuncs.Cmd_Argv(1) ), atoi( gEngfuncs.Cmd_Argv(2) ) ); } void SpectatorSpray(void) { vec3_t forward; char string[128]; if ( !gEngfuncs.IsSpectateOnly() ) return; AngleVectors(v_angles,forward,NULL,NULL); VectorScale(forward, 128, forward); VectorAdd(forward, v_origin, forward); pmtrace_t * trace = gEngfuncs.PM_TraceLine( v_origin, forward, PM_TRACELINE_PHYSENTSONLY, 2, -1 ); if ( trace->fraction != 1.0 ) { sprintf(string, "drc_spray %.2f %.2f %.2f %i", trace->endpos[0], trace->endpos[1], trace->endpos[2], trace->ent ); gEngfuncs.pfnServerCmd(string); } } void SpectatorHelp(void) { if ( gViewPort ) { gViewPort->ShowVGUIMenu( MENU_SPECHELP ); } else { char *text = CHudTextMessage::BufferedLocaliseTextString( "#Spec_Help_Text" ); if ( text ) { while ( *text ) { if ( *text != 13 ) gEngfuncs.Con_Printf( "%c", *text ); text++; } } } } void SpectatorMenu( void ) { if ( gEngfuncs.Cmd_Argc() <= 1 ) { gEngfuncs.Con_Printf( "usage: spec_menu <0|1>\n" ); return; } gViewPort->m_pSpectatorPanel->ShowMenu( atoi( gEngfuncs.Cmd_Argv(1))!=0 ); } void ToggleScores( void ) { if ( gViewPort ) { if (gViewPort->IsScoreBoardVisible() ) { gViewPort->HideScoreBoard(); } else { gViewPort->ShowScoreBoard(); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CHudSpectator::Init() { gHUD.AddHudElem(this); m_iFlags |= HUD_ACTIVE; m_flNextObserverInput = 0.0f; m_zoomDelta = 0.0f; m_moveDelta = 0.0f; iJumpSpectator = 0; memset( &m_OverviewData, 0, sizeof(m_OverviewData)); memset( &m_OverviewEntities, 0, sizeof(m_OverviewEntities)); m_lastPrimaryObject = m_lastSecondaryObject = 0; gEngfuncs.pfnAddCommand ("spec_mode", SpectatorMode ); gEngfuncs.pfnAddCommand ("spec_decal", SpectatorSpray ); gEngfuncs.pfnAddCommand ("spec_help", SpectatorHelp ); gEngfuncs.pfnAddCommand ("spec_menu", SpectatorMenu ); gEngfuncs.pfnAddCommand ("togglescores", ToggleScores ); m_drawnames = gEngfuncs.pfnRegisterVariable("spec_drawnames","1",0); m_drawcone = gEngfuncs.pfnRegisterVariable("spec_drawcone","1",0); m_drawstatus = gEngfuncs.pfnRegisterVariable("spec_drawstatus","1",0); m_autoDirector = gEngfuncs.pfnRegisterVariable("spec_autodirector","1",0); // Removed by mmcguire. m_overviewMode = false; //m_overview = gEngfuncs.pfnRegisterVariable("spec_overview","1",0); //m_pip = gEngfuncs.pfnRegisterVariable("spec_pip","1",0); if ( !m_drawnames || !m_drawcone || !m_drawstatus || !m_autoDirector /*|| !m_pip*/) { gEngfuncs.Con_Printf("ERROR! Couldn't register all spectator variables.\n"); return 0; } return 1; } //----------------------------------------------------------------------------- // UTIL_StringToVector originally from ..\dlls\util.cpp, slightly changed //----------------------------------------------------------------------------- void UTIL_StringToVector( float * pVector, const char *pString ) { char *pstr, *pfront, tempString[128]; int j; strcpy( tempString, pString ); pstr = pfront = tempString; for ( j = 0; j < 3; j++ ) { pVector[j] = atof( pfront ); while ( *pstr && *pstr != ' ' ) pstr++; if (!*pstr) break; pstr++; pfront = pstr; } if (j < 2) { for (j = j+1;j < 3; j++) pVector[j] = 0; } } int UTIL_FindEntityInMap(char * name, float * origin, float * angle) { int n,found = 0; char keyname[256]; char token[1024]; cl_entity_t * pEnt = gEngfuncs.GetEntityByIndex( 0 ); // get world model if ( !pEnt ) return 0; if ( !pEnt->model ) return 0; char * data = pEnt->model->entities; while (data) { data = gEngfuncs.COM_ParseFile(data, token); if ( (token[0] == '}') || (token[0]==0) ) break; if (!data) { gEngfuncs.Con_DPrintf("UTIL_FindEntityInMap: EOF without closing brace\n"); return 0; } if (token[0] != '{') { gEngfuncs.Con_DPrintf("UTIL_FindEntityInMap: expected {\n"); return 0; } // we parse the first { now parse entities properties while ( 1 ) { // parse key data = gEngfuncs.COM_ParseFile(data, token); if (token[0] == '}') break; // finish parsing this entity if (!data) { gEngfuncs.Con_DPrintf("UTIL_FindEntityInMap: EOF without closing brace\n"); return 0; }; strcpy (keyname, token); // another hack to fix keynames with trailing spaces n = (int)strlen(keyname); while (n && keyname[n-1] == ' ') { keyname[n-1] = 0; n--; } // parse value data = gEngfuncs.COM_ParseFile(data, token); if (!data) { gEngfuncs.Con_DPrintf("UTIL_FindEntityInMap: EOF without closing brace\n"); return 0; }; if (token[0] == '}') { gEngfuncs.Con_DPrintf("UTIL_FindEntityInMap: closing brace without data"); return 0; } if (!strcmp(keyname,"classname")) { if (!strcmp(token, name )) { found = 1; // thats our entity } }; if( !strcmp( keyname, "angle" ) ) { float y = atof( token ); if (y >= 0) { angle[0] = 0.0f; angle[1] = y; } else if ((int)y == -1) { angle[0] = -90.0f; angle[1] = 0.0f;; } else { angle[0] = 90.0f; angle[1] = 0.0f; } angle[2] = 0.0f; } if( !strcmp( keyname, "angles" ) ) { UTIL_StringToVector(angle, token); } if (!strcmp(keyname,"origin")) { UTIL_StringToVector(origin, token); }; } // while (1) if (found) return 1; } return 0; // we search all entities, but didn't found the correct } //----------------------------------------------------------------------------- // SetSpectatorStartPosition(): // Get valid map position and 'beam' spectator to this position //----------------------------------------------------------------------------- void CHudSpectator::SetSpectatorStartPosition() { // search for info_player start if ( UTIL_FindEntityInMap( "trigger_camera", m_cameraOrigin, m_cameraAngles ) ) iJumpSpectator = 1; else if ( UTIL_FindEntityInMap( "info_player_start", m_cameraOrigin, m_cameraAngles ) ) iJumpSpectator = 1; else if ( UTIL_FindEntityInMap( "info_player_deathmatch", m_cameraOrigin, m_cameraAngles ) ) iJumpSpectator = 1; else if ( UTIL_FindEntityInMap( "info_player_coop", m_cameraOrigin, m_cameraAngles ) ) iJumpSpectator = 1; else { // jump to 0,0,0 if no better position was found VectorCopy(vec3_origin, m_cameraOrigin); VectorCopy(vec3_origin, m_cameraAngles); } VectorCopy(m_cameraOrigin, vJumpOrigin); VectorCopy(m_cameraAngles, vJumpAngles); iJumpSpectator = 1; // jump anyway } //----------------------------------------------------------------------------- // Purpose: Loads new icons //----------------------------------------------------------------------------- int CHudSpectator::VidInit() { m_hsprPlayerMarine = SPR_Load("sprites/iplayerm.spr"); m_hsprPlayerAlien = SPR_Load("sprites/iplayera.spr"); m_hsprPlayerDead = SPR_Load("sprites/iplayerdead.spr"); m_hsprUnkownMap = SPR_Load("sprites/tile.spr"); //m_hsprBeam = SPR_Load("sprites/laserbeam.spr"); //m_hsprCamera = SPR_Load("sprites/camera.spr"); m_hCrosshair = SPR_Load("sprites/crosshairs.spr"); m_hsprWhite = SPR_Load(kWhiteSprite); return 1; } //----------------------------------------------------------------------------- // Purpose: // Input : flTime - // intermission - //----------------------------------------------------------------------------- int CHudSpectator::Draw(float flTime) { // draw only in spectator mode if ( !g_iUser1 ) return 0; // string error; // gHUD.Update( flTime, error); // Removed by mmcguire. /* // if user pressed zoom, aplly changes if ( (m_zoomDelta != 0.0f) && ( g_iUser1 == OBS_MAP_FREE ) ) { m_mapZoom += m_zoomDelta; if ( m_mapZoom > 3.0f ) m_mapZoom = 3.0f; if ( m_mapZoom < 0.5f ) m_mapZoom = 0.5f; } // if user moves in map mode, change map origin if ( (m_moveDelta != 0.0f) && (g_iUser1 != OBS_ROAMING) ) { vec3_t right; AngleVectors(v_angles, NULL, right, NULL); VectorNormalize(right); VectorScale(right, m_moveDelta, right ); VectorAdd( m_mapOrigin, right, m_mapOrigin ) } */ //DrawOverviewMap(); /* if (!IsInOverviewMode()) { return 0; } if (m_hsprWhite != NULL) { float bgColor[] = { 0.1, 0.1, 0.1, 1 }; float borderColor[] = { 1, 1, 1, 1 }; gEngfuncs.pTriAPI->RenderMode(kRenderNormal); gEngfuncs.pTriAPI->CullFace(TRI_NONE); gEngfuncs.pTriAPI->SpriteTexture((struct model_s*)(gEngfuncs.GetSpritePointer(m_hsprWhite)), 0); float gammaScale = 1.0f / gHUD.GetGammaSlope(); // Draw the border on the overview map and the inset view. gEngfuncs.pTriAPI->RenderMode(kRenderNormal); gEngfuncs.pTriAPI->CullFace(TRI_NONE); gEngfuncs.pTriAPI->SpriteTexture((struct model_s*)(gEngfuncs.GetSpritePointer(m_hsprWhite)), 0); gEngfuncs.pTriAPI->Color4f(gammaScale * borderColor[0], gammaScale * borderColor[1], gammaScale * borderColor[2], borderColor[3]); gEngfuncs.pTriAPI->Begin(TRI_LINES); int insetX1 = XRES(m_OverviewData.insetWindowX); int insetY1 = YRES(m_OverviewData.insetWindowY); int insetX2 = insetX1 + XRES(m_OverviewData.insetWindowWidth); int insetY2 = insetY1 + YRES(m_OverviewData.insetWindowHeight); gEngfuncs.pTriAPI->Vertex3f(insetX1, insetY1, 1); gEngfuncs.pTriAPI->Vertex3f(insetX2, insetY1, 1); gEngfuncs.pTriAPI->Vertex3f(insetX2, insetY1, 1); gEngfuncs.pTriAPI->Vertex3f(insetX2, insetY2, 1); gEngfuncs.pTriAPI->Vertex3f(insetX2, insetY2, 1); gEngfuncs.pTriAPI->Vertex3f(insetX1, insetY2, 1); gEngfuncs.pTriAPI->Vertex3f(insetX1, insetY2, 1); gEngfuncs.pTriAPI->Vertex3f(insetX1, insetY1, 1); gEngfuncs.pTriAPI->End(); }*/ /* // Only draw the icon names only if map mode is in Main Mode if ( g_iUser1 < OBS_MAP_FREE ) return 1; if ( !m_drawnames->value ) return 1; // make sure we have player info gViewPort->GetAllPlayersInfo(); */ return 1; } bool CHudSpectator::IsInOverviewMode() const { return g_iUser1 && m_overviewMode && gHUD.GetIsNSMode(); } void CHudSpectator::SetOverviewMode(bool overviewMode) { m_overviewMode = overviewMode; } void CHudSpectator::DrawOverviewMap() { // draw only in spectator mode if (!IsInOverviewMode()) { return; } AvHOverviewMap& theOverviewMap = gHUD.GetOverviewMap(); AvHOverviewMap::DrawInfo theDrawInfo; theDrawInfo.mX = XRES(m_OverviewData.insetWindowX + m_OverviewData.insetWindowWidth + 4); theDrawInfo.mY = YRES(SPECTATOR_PANEL_HEIGHT + 4); theDrawInfo.mWidth = ScreenWidth() - theDrawInfo.mX - XRES(4); theDrawInfo.mHeight = ScreenHeight() - YRES(SPECTATOR_PANEL_HEIGHT + 4) - theDrawInfo.mY; theDrawInfo.mZoomScale = 1.0f; AvHMapExtents theMapExtents; theOverviewMap.GetMapExtents(theMapExtents); theDrawInfo.mFullScreen = true; float worldWidth = theMapExtents.GetMaxMapX() - theMapExtents.GetMinMapX(); float worldHeight = theMapExtents.GetMaxMapY() - theMapExtents.GetMinMapY(); float xScale; float yScale; float aspect1 = worldWidth / worldHeight; float aspect2 = ((float)theDrawInfo.mWidth) / theDrawInfo.mHeight; if (aspect1 > aspect2) { xScale = 1; yScale = 1 / aspect2; } else { xScale = aspect2; yScale = 1; } float centerX = (theMapExtents.GetMinMapX() + theMapExtents.GetMaxMapX()) / 2; float centerY = (theMapExtents.GetMinMapY() + theMapExtents.GetMaxMapY()) / 2; theDrawInfo.mViewWorldMinX = centerX - worldWidth * xScale * 0.5; theDrawInfo.mViewWorldMinY = centerY - worldHeight * yScale * 0.5; theDrawInfo.mViewWorldMaxX = centerX + worldWidth * xScale * 0.5; theDrawInfo.mViewWorldMaxY = centerY + worldHeight * yScale * 0.5; if (m_hsprWhite != NULL) { float bgColor[] = { 0.1, 0.1, 0.1, 1 }; float borderColor[] = { 1, 1, 1, 1 }; gEngfuncs.pTriAPI->RenderMode(kRenderNormal); gEngfuncs.pTriAPI->CullFace(TRI_NONE); gEngfuncs.pTriAPI->SpriteTexture((struct model_s*)(gEngfuncs.GetSpritePointer(m_hsprWhite)), 0); float gammaScale = 1.0f / gHUD.GetGammaSlope(); // Draw the background. gEngfuncs.pTriAPI->Color4f(gammaScale * bgColor[0], gammaScale * bgColor[1], gammaScale * bgColor[2], bgColor[3]); gEngfuncs.pTriAPI->Begin(TRI_QUADS); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX, theDrawInfo.mY, 1); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX, theDrawInfo.mY + theDrawInfo.mHeight, 1); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX + theDrawInfo.mWidth, theDrawInfo.mY + theDrawInfo.mHeight, 1); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX + theDrawInfo.mWidth, theDrawInfo.mY, 1); gEngfuncs.pTriAPI->End(); // Draw the overview map. theOverviewMap.Draw(theDrawInfo); // Draw the border on the overview map and the inset view. gEngfuncs.pTriAPI->RenderMode(kRenderNormal); gEngfuncs.pTriAPI->CullFace(TRI_NONE); gEngfuncs.pTriAPI->SpriteTexture((struct model_s*)(gEngfuncs.GetSpritePointer(m_hsprWhite)), 0); gEngfuncs.pTriAPI->Color4f(gammaScale * borderColor[0], gammaScale * borderColor[1], gammaScale * borderColor[2], borderColor[3]); gEngfuncs.pTriAPI->Begin(TRI_LINES); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX, theDrawInfo.mY, 1); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX, theDrawInfo.mY + theDrawInfo.mHeight, 1); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX, theDrawInfo.mY + theDrawInfo.mHeight, 1); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX + theDrawInfo.mWidth, theDrawInfo.mY + theDrawInfo.mHeight, 1); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX + theDrawInfo.mWidth, theDrawInfo.mY + theDrawInfo.mHeight, 1); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX + theDrawInfo.mWidth, theDrawInfo.mY, 1); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX + theDrawInfo.mWidth, theDrawInfo.mY, 1); gEngfuncs.pTriAPI->Vertex3f(theDrawInfo.mX, theDrawInfo.mY, 1); int insetX1 = XRES(m_OverviewData.insetWindowX); int insetY1 = YRES(m_OverviewData.insetWindowY); int insetX2 = insetX1 + XRES(m_OverviewData.insetWindowWidth); int insetY2 = insetY1 + YRES(m_OverviewData.insetWindowHeight); gEngfuncs.pTriAPI->Vertex3f(insetX1, insetY1, 1); gEngfuncs.pTriAPI->Vertex3f(insetX2, insetY1, 1); gEngfuncs.pTriAPI->Vertex3f(insetX2, insetY1, 1); gEngfuncs.pTriAPI->Vertex3f(insetX2, insetY2, 1); gEngfuncs.pTriAPI->Vertex3f(insetX2, insetY2, 1); gEngfuncs.pTriAPI->Vertex3f(insetX1, insetY2, 1); gEngfuncs.pTriAPI->Vertex3f(insetX1, insetY2, 1); gEngfuncs.pTriAPI->Vertex3f(insetX1, insetY1, 1); gEngfuncs.pTriAPI->End(); } } #include "parsemsg.h" void CHudSpectator::DirectorMessage( int iSize, void *pbuf ) { float value; char * string; BEGIN_READ( pbuf, iSize ); int cmd = READ_BYTE(); switch ( cmd ) // director command byte { case DRC_CMD_START : // now we have to do some things clientside, since the proxy doesn't know our mod g_iPlayerClass = 0; g_iTeamNumber = 0; // fake a InitHUD & ResetHUD message gHUD.MsgFunc_InitHUD(NULL,0, NULL); gHUD.MsgFunc_ResetHUD(NULL, 0, NULL); break; case DRC_CMD_EVENT : m_lastPrimaryObject = READ_WORD(); m_lastSecondaryObject = READ_WORD(); m_iObserverFlags = READ_LONG(); if ( m_autoDirector->value ) { if ( (g_iUser2 != m_lastPrimaryObject) || (g_iUser3 != m_lastSecondaryObject) ) V_ResetChaseCam(); g_iUser2 = m_lastPrimaryObject; g_iUser3 = m_lastSecondaryObject; } break; case DRC_CMD_MODE : if ( m_autoDirector->value ) { SetMode( READ_BYTE()); } break; case DRC_CMD_CAMERA : if ( m_autoDirector->value ) { vJumpOrigin[0] = READ_COORD(); // position vJumpOrigin[1] = READ_COORD(); vJumpOrigin[2] = READ_COORD(); vJumpAngles[0] = READ_COORD(); // view angle vJumpAngles[1] = READ_COORD(); vJumpAngles[2] = READ_COORD(); gEngfuncs.SetViewAngles( vJumpAngles ); iJumpSpectator = 1; } break; case DRC_CMD_MESSAGE: { client_textmessage_t * msg = &m_HUDMessages[m_lastHudMessage]; msg->effect = READ_BYTE(); // effect UnpackRGB( (int&)msg->r1, (int&)msg->g1, (int&)msg->b1, READ_LONG() ); // color msg->r2 = msg->r1; msg->g2 = msg->g1; msg->b2 = msg->b1; msg->a2 = msg->a1 = 0xFF; // not transparent msg->x = READ_FLOAT(); // x pos msg->y = READ_FLOAT(); // y pos msg->fadein = READ_FLOAT(); // fadein msg->fadeout = READ_FLOAT(); // fadeout msg->holdtime = READ_FLOAT(); // holdtime msg->fxtime = READ_FLOAT(); // fxtime; strncpy( m_HUDMessageText[m_lastHudMessage], READ_STRING(), 128 ); m_HUDMessageText[m_lastHudMessage][127]=0; // text msg->pMessage = m_HUDMessageText[m_lastHudMessage]; msg->pName = "HUD_MESSAGE"; gHUD.m_Message.MessageAdd( msg->pName, gHUD.m_flTime); m_lastHudMessage++; m_lastHudMessage %= MAX_SPEC_HUD_MESSAGES; } break; case DRC_CMD_SOUND : string = READ_STRING(); value = READ_FLOAT(); gEngfuncs.pEventAPI->EV_PlaySound(0, v_origin, CHAN_BODY, string, value, ATTN_NORM, 0, PITCH_NORM ); break; case DRC_CMD_TIMESCALE : value = READ_FLOAT(); break; case DRC_CMD_STATUS: READ_LONG(); // total number of spectator slots m_iSpectatorNumber = READ_LONG(); // total number of spectator READ_WORD(); // total number of relay proxies gViewPort->UpdateSpectatorPanel(); break; case DRC_CMD_BANNER: // gEngfuncs.Con_DPrintf("GUI: Banner %s\n",READ_STRING() ); // name of banner tga eg gfx/temp/7454562234563475.tga gViewPort->m_pSpectatorPanel->m_TopBanner->LoadImage( READ_STRING() ); gViewPort->UpdateSpectatorPanel(); break; case DRC_CMD_FADE: break; case DRC_CMD_STUFFTEXT: ClientCmd( READ_STRING() ); break; default : gEngfuncs.Con_DPrintf("CHudSpectator::DirectorMessage: unknown command %i.\n", cmd ); } } void CHudSpectator::FindNextPlayer(bool bReverse) { // MOD AUTHORS: Modify the logic of this function if you want to restrict the observer to watching // only a subset of the players. e.g. Make it check the target's team. int iStart; cl_entity_t * pEnt = NULL; // if we are NOT in HLTV mode, spectator targets are set on server if ( !gEngfuncs.IsSpectateOnly() ) { char cmdstring[32]; // forward command to server sprintf(cmdstring,"follownext %i",bReverse?1:0); gEngfuncs.pfnServerCmd(cmdstring); return; } if ( g_iUser2 ) iStart = g_iUser2; else iStart = 1; g_iUser2 = 0; int iCurrent = iStart; int iDir = bReverse ? -1 : 1; // make sure we have player info gViewPort->GetAllPlayersInfo(); do { iCurrent += iDir; // Loop through the clients if (iCurrent > MAX_PLAYERS) iCurrent = 1; if (iCurrent < 1) iCurrent = MAX_PLAYERS; pEnt = gEngfuncs.GetEntityByIndex( iCurrent ); if ( !IsActivePlayer( pEnt ) ) continue; // MOD AUTHORS: Add checks on target here. g_iUser2 = iCurrent; break; } while ( iCurrent != iStart ); // Did we find a target? if ( !g_iUser2 ) { gEngfuncs.Con_DPrintf( "No observer targets.\n" ); // take save camera position VectorCopy(m_cameraOrigin, vJumpOrigin); VectorCopy(m_cameraAngles, vJumpAngles); } else { // use new entity position for roaming VectorCopy ( pEnt->origin, vJumpOrigin ); VectorCopy ( pEnt->angles, vJumpAngles ); } iJumpSpectator = 1; } void CHudSpectator::HandleButtonsDown( int ButtonPressed ) { if ( !gViewPort ) return; //Not in intermission. if ( gHUD.m_iIntermission ) return; if ( !g_iUser1 ) return; // dont do anything if not in spectator mode // don't handle buttons during normal demo playback if ( gEngfuncs.pDemoAPI->IsPlayingback() && !gEngfuncs.IsSpectateOnly() ) return; int theNewMainMode = g_iUser1; // Jump changes main window modes if ( ButtonPressed & IN_JUMP ) { bool theFirstPerson = (g_iUser1 == OBS_IN_EYE); bool theInOverviewMode = gHUD.m_Spectator.IsInOverviewMode(); // NS if(gHUD.GetIsNSMode()) { // First-person full -> chase camera full -> firstperson with overview -> chase camera with overview if(theFirstPerson && !theInOverviewMode) { gHUD.m_Spectator.SetMode(OBS_CHASE_LOCKED); //gHUD.m_Spectator.SetOverviewMode(false); } else if(!theFirstPerson && !theInOverviewMode) { gHUD.m_Spectator.SetMode(OBS_IN_EYE); gHUD.m_Spectator.SetOverviewMode(true); } else if(theFirstPerson && theInOverviewMode) { gHUD.m_Spectator.SetMode(OBS_CHASE_LOCKED); //gHUD.m_Spectator.SetOverviewMode(true); } else if(!theFirstPerson && theInOverviewMode) { gHUD.m_Spectator.SetMode(OBS_IN_EYE); gHUD.m_Spectator.SetOverviewMode(false); } } // Combat else { // First-person full -> chase camera full if(theFirstPerson) { gHUD.m_Spectator.SetMode(OBS_CHASE_LOCKED); gHUD.m_Spectator.SetOverviewMode(false); } else { gHUD.m_Spectator.SetMode(OBS_IN_EYE); gHUD.m_Spectator.SetOverviewMode(false); } } } //g_iUser1 = theNewMainMode; // Attack moves to the next player if ( ButtonPressed & (IN_MOVELEFT | IN_MOVERIGHT) ) { FindNextPlayer( (ButtonPressed & IN_MOVELEFT) ? true:false ); // if ( g_iUser1 == OBS_ROAMING ) // { // gEngfuncs.SetViewAngles( vJumpAngles ); // iJumpSpectator = 1; // // } // lease directed mode if player want to see another player m_autoDirector->value = 0.0f; } /* double time = gEngfuncs.GetClientTime(); int newMainMode = g_iUser1; int newInsetMode = m_pip->value; // gEngfuncs.Con_Printf(" HandleButtons:%i\n", ButtonPressed ); if ( !gViewPort ) return; //Not in intermission. if ( gHUD.m_iIntermission ) return; if ( !g_iUser1 ) return; // dont do anything if not in spectator mode // don't handle buttons during normal demo playback if ( gEngfuncs.pDemoAPI->IsPlayingback() && !gEngfuncs.IsSpectateOnly() ) return; // Slow down mouse clicks. if ( m_flNextObserverInput > time ) return; // enable spectator screen if ( ButtonPressed & IN_DUCK ) { gViewPort->m_pSpectatorPanel->ShowMenu(!gViewPort->m_pSpectatorPanel->m_menuVisible); } // 'Use' changes inset window mode if ( ButtonPressed & IN_USE ) { newInsetMode = ToggleInset(true); } // if not in HLTV mode, buttons are handled server side if ( gEngfuncs.IsSpectateOnly() ) { // changing target or chase mode not in overviewmode without inset window // Jump changes main window modes if ( ButtonPressed & IN_JUMP ) { if ( g_iUser1 == OBS_CHASE_LOCKED ) newMainMode = OBS_CHASE_FREE; else if ( g_iUser1 == OBS_CHASE_FREE ) newMainMode = OBS_IN_EYE; else if ( g_iUser1 == OBS_IN_EYE ) newMainMode = OBS_ROAMING; else if ( g_iUser1 == OBS_ROAMING ) newMainMode = OBS_MAP_FREE; else if ( g_iUser1 == OBS_MAP_FREE ) newMainMode = OBS_MAP_CHASE; else newMainMode = OBS_CHASE_FREE; // don't use OBS_CHASE_LOCKED anymore } // Attack moves to the next player if ( ButtonPressed & (IN_ATTACK | IN_ATTACK2) ) { FindNextPlayer( (ButtonPressed & IN_ATTACK2) ? true:false ); if ( g_iUser1 == OBS_ROAMING ) { gEngfuncs.SetViewAngles( vJumpAngles ); iJumpSpectator = 1; } // lease directed mode if player want to see another player m_autoDirector->value = 0.0f; } } SetModes(newMainMode, newInsetMode); if ( g_iUser1 == OBS_MAP_FREE ) { if ( ButtonPressed & IN_FORWARD ) m_zoomDelta = 0.01f; if ( ButtonPressed & IN_BACK ) m_zoomDelta = -0.01f; if ( ButtonPressed & IN_MOVELEFT ) m_moveDelta = -12.0f; if ( ButtonPressed & IN_MOVERIGHT ) m_moveDelta = 12.0f; } m_flNextObserverInput = time + 0.2; */ } void CHudSpectator::HandleButtonsUp( int ButtonPressed ) { if ( !gViewPort ) return; if ( !gViewPort->m_pSpectatorPanel->isVisible() ) return; // dont do anything if not in spectator mode if ( ButtonPressed & (IN_FORWARD | IN_BACK) ) m_zoomDelta = 0.0f; if ( ButtonPressed & (IN_MOVELEFT | IN_MOVERIGHT) ) m_moveDelta = 0.0f; } void CHudSpectator::SetMode(int iNewMainMode) { // if value == -1 keep old value if ( iNewMainMode == -1 ) iNewMainMode = g_iUser1; // main modes ettings will override inset window settings if ( iNewMainMode != g_iUser1 ) { // if we are NOT in HLTV mode, main spectator mode is set on server if ( !gEngfuncs.IsSpectateOnly() ) { char cmdstring[32]; // forward command to server sprintf(cmdstring,"specmode %i",iNewMainMode ); gEngfuncs.pfnServerCmd(cmdstring); return; } else { if ( !g_iUser2 && (iNewMainMode !=OBS_ROAMING ) ) // make sure we have a target { // choose last Director object if still available if ( IsActivePlayer( gEngfuncs.GetEntityByIndex( m_lastPrimaryObject ) ) ) { g_iUser2 = m_lastPrimaryObject; g_iUser3 = m_lastSecondaryObject; } else { FindNextPlayer(false); // find any target } } switch ( iNewMainMode ) { case OBS_CHASE_LOCKED: g_iUser1 = OBS_CHASE_LOCKED; break; case OBS_CHASE_FREE: g_iUser1 = OBS_CHASE_FREE; break; case OBS_ROAMING : // jump to current vJumpOrigin/angle g_iUser1 = OBS_ROAMING; if ( g_iUser2 ) { V_GetChasePos( g_iUser2, v_cl_angles, vJumpOrigin, vJumpAngles ); gEngfuncs.SetViewAngles( vJumpAngles ); iJumpSpectator = 1; } break; case OBS_IN_EYE: g_iUser1 = OBS_IN_EYE; break; /* case OBS_MAP_FREE : g_iUser1 = OBS_MAP_FREE; // reset user values m_mapZoom = m_OverviewData.zoom; m_mapOrigin = m_OverviewData.origin; break; case OBS_MAP_CHASE : g_iUser1 = OBS_MAP_CHASE; // reset user values m_mapZoom = m_OverviewData.zoom; m_mapOrigin = m_OverviewData.origin; break; */ } if ( (g_iUser1 == OBS_IN_EYE) || (g_iUser1 == OBS_ROAMING) ) { m_crosshairRect.left = 24; m_crosshairRect.top = 0; m_crosshairRect.right = 48; m_crosshairRect.bottom = 24; gHUD.SetCurrentCrosshair( m_hCrosshair, m_crosshairRect, 255, 255, 255 ); } else { memset( &m_crosshairRect,0,sizeof(m_crosshairRect) ); gHUD.SetCurrentCrosshair( 0, m_crosshairRect, 0, 0, 0 ); } //char string[128]; //sprintf(string, "#Spec_Mode%d", g_iUser1 ); //sprintf(string, "%c%s", HUD_PRINTCENTER, CHudTextMessage::BufferedLocaliseTextString( string )); //gHUD.m_TextMessage.MsgFunc_TextMsg(NULL, strlen(string)+1, string ); } } gViewPort->UpdateSpectatorPanel(); } bool CHudSpectator::IsActivePlayer(cl_entity_t * ent) { return ( ent && ent->player && ent->curstate.solid != SOLID_NOT && ent != gEngfuncs.GetLocalPlayer() && g_PlayerInfoList[ent->index].name != NULL ); } bool CHudSpectator::ParseOverviewFile( ) { //char filename[255]; //char levelname[255]; //char token[1024]; //float height; char *pfile = NULL; memset( &m_OverviewData, 0, sizeof(m_OverviewData)); // fill in standrd values m_OverviewData.insetWindowX = 4; // upper left corner m_OverviewData.insetWindowY = 4 + SPECTATOR_PANEL_HEIGHT; m_OverviewData.insetWindowHeight = 180; m_OverviewData.insetWindowWidth = 240; m_OverviewData.origin[0] = 0.0f; m_OverviewData.origin[1] = 0.0f; m_OverviewData.origin[2] = 0.0f; m_OverviewData.zoom = 1.0f; m_OverviewData.layers = 0; m_OverviewData.layersHeights[0] = 0.0f; strcpy( m_OverviewData.map, gEngfuncs.pfnGetLevelName() ); if ( strlen( m_OverviewData.map ) == 0 ) return false; // not active yet /* strcpy(levelname, m_OverviewData.map + 5); levelname[strlen(levelname)-4] = 0; sprintf(filename, "overviews/%s.txt", levelname ); pfile = (char *)gEngfuncs.COM_LoadFile( filename, 5, NULL); if (!pfile) { gEngfuncs.Con_Printf("Couldn't open file %s. Using default values for overiew mode.\n", filename ); return false; } while (true) { pfile = gEngfuncs.COM_ParseFile(pfile, token); if (!pfile) break; if ( !stricmp( token, "global" ) ) { // parse the global data pfile = gEngfuncs.COM_ParseFile(pfile, token); if ( stricmp( token, "{" ) ) { gEngfuncs.Con_Printf("Error parsing overview file %s. (expected { )\n", filename ); return false; } pfile = gEngfuncs.COM_ParseFile(pfile,token); while (stricmp( token, "}") ) { if ( !stricmp( token, "zoom" ) ) { pfile = gEngfuncs.COM_ParseFile(pfile,token); m_OverviewData.zoom = atof( token ); } else if ( !stricmp( token, "origin" ) ) { pfile = gEngfuncs.COM_ParseFile(pfile, token); m_OverviewData.origin[0] = atof( token ); pfile = gEngfuncs.COM_ParseFile(pfile,token); m_OverviewData.origin[1] = atof( token ); pfile = gEngfuncs.COM_ParseFile(pfile, token); m_OverviewData.origin[2] = atof( token ); } else if ( !stricmp( token, "rotated" ) ) { pfile = gEngfuncs.COM_ParseFile(pfile,token); m_OverviewData.rotated = atoi( token ); } else if ( !stricmp( token, "inset" ) ) { // Removed by mmcguire. // This isn't supported anymore. pfile = gEngfuncs.COM_ParseFile(pfile,token); //m_OverviewData.insetWindowX = atof( token ); pfile = gEngfuncs.COM_ParseFile(pfile,token); //m_OverviewData.insetWindowY = atof( token ); pfile = gEngfuncs.COM_ParseFile(pfile,token); //m_OverviewData.insetWindowWidth = atof( token ); pfile = gEngfuncs.COM_ParseFile(pfile,token); //m_OverviewData.insetWindowHeight = atof( token ); } else { gEngfuncs.Con_Printf("Error parsing overview file %s. (%s unkown)\n", filename, token ); return false; } pfile = gEngfuncs.COM_ParseFile(pfile,token); // parse next token } } else if ( !stricmp( token, "layer" ) ) { // parse a layer data if ( m_OverviewData.layers == OVERVIEW_MAX_LAYERS ) { gEngfuncs.Con_Printf("Error parsing overview file %s. ( too many layers )\n", filename ); return false; } pfile = gEngfuncs.COM_ParseFile(pfile,token); if ( stricmp( token, "{" ) ) { gEngfuncs.Con_Printf("Error parsing overview file %s. (expected { )\n", filename ); return false; } pfile = gEngfuncs.COM_ParseFile(pfile,token); while (stricmp( token, "}") ) { if ( !stricmp( token, "image" ) ) { pfile = gEngfuncs.COM_ParseFile(pfile,token); strcpy(m_OverviewData.layersImages[ m_OverviewData.layers ], token); } else if ( !stricmp( token, "height" ) ) { pfile = gEngfuncs.COM_ParseFile(pfile,token); height = atof(token); m_OverviewData.layersHeights[ m_OverviewData.layers ] = height; } else { gEngfuncs.Con_Printf("Error parsing overview file %s. (%s unkown)\n", filename, token ); return false; } pfile = gEngfuncs.COM_ParseFile(pfile,token); // parse next token } m_OverviewData.layers++; } } gEngfuncs.COM_FreeFile( pfile ); */ m_mapZoom = m_OverviewData.zoom; m_mapOrigin = m_OverviewData.origin; return true; } void CHudSpectator::LoadMapSprites() { // right now only support for one map layer if (m_OverviewData.layers > 0 ) { m_MapSprite = gEngfuncs.LoadMapSprite( m_OverviewData.layersImages[0] ); } else m_MapSprite = NULL; // the standard "unkown map" sprite will be used instead } void CHudSpectator::DrawOverviewLayer() { float screenaspect, xs, ys, xStep, yStep, x,y,z; int ix,iy,i,xTiles,yTiles,frame; qboolean hasMapImage = m_MapSprite?TRUE:FALSE; model_t * dummySprite = (struct model_s *)gEngfuncs.GetSpritePointer( m_hsprUnkownMap); if ( hasMapImage) { i = m_MapSprite->numframes / (4*3); i = sqrt((float)i); xTiles = i*4; yTiles = i*3; } else { xTiles = 8; yTiles = 6; } screenaspect = 4.0f/3.0f; xs = m_OverviewData.origin[0]; ys = m_OverviewData.origin[1]; z = ( 90.0f - v_angles[0] ) / 90.0f; z *= m_OverviewData.layersHeights[0]; // gOverviewData.z_min - 32; // i = r_overviewTexture + ( layer*OVERVIEW_X_TILES*OVERVIEW_Y_TILES ); gEngfuncs.pTriAPI->RenderMode( kRenderTransTexture ); gEngfuncs.pTriAPI->CullFace( TRI_NONE ); gEngfuncs.pTriAPI->Color4f( 1.0, 1.0, 1.0, 1.0 ); frame = 0; // rotated view ? if ( m_OverviewData.rotated ) { xStep = (2*4096.0f / m_OverviewData.zoom ) / xTiles; yStep = -(2*4096.0f / (m_OverviewData.zoom* screenaspect) ) / yTiles; y = ys + (4096.0f / (m_OverviewData.zoom * screenaspect)); for (iy = 0; iy < yTiles; iy++) { x = xs - (4096.0f / (m_OverviewData.zoom)); for (ix = 0; ix < xTiles; ix++) { if (hasMapImage) gEngfuncs.pTriAPI->SpriteTexture( m_MapSprite, frame ); else gEngfuncs.pTriAPI->SpriteTexture( dummySprite, 0 ); gEngfuncs.pTriAPI->Begin( TRI_QUADS ); gEngfuncs.pTriAPI->TexCoord2f( 0, 0 ); gEngfuncs.pTriAPI->Vertex3f (x, y, z); gEngfuncs.pTriAPI->TexCoord2f( 1, 0 ); gEngfuncs.pTriAPI->Vertex3f (x+xStep ,y, z); gEngfuncs.pTriAPI->TexCoord2f( 1, 1 ); gEngfuncs.pTriAPI->Vertex3f (x+xStep, y+yStep, z); gEngfuncs.pTriAPI->TexCoord2f( 0, 1 ); gEngfuncs.pTriAPI->Vertex3f (x, y+yStep, z); gEngfuncs.pTriAPI->End(); frame++; x+= xStep; } y+=yStep; } } else { xStep = -(2*4096.0f / m_OverviewData.zoom ) / xTiles; yStep = -(2*4096.0f / (m_OverviewData.zoom* screenaspect) ) / yTiles; x = xs + (4096.0f / (m_OverviewData.zoom * screenaspect )); for (ix = 0; ix < yTiles; ix++) { y = ys + (4096.0f / (m_OverviewData.zoom)); for (iy = 0; iy < xTiles; iy++) { if (hasMapImage) gEngfuncs.pTriAPI->SpriteTexture( m_MapSprite, frame ); else gEngfuncs.pTriAPI->SpriteTexture( dummySprite, 0 ); gEngfuncs.pTriAPI->Begin( TRI_QUADS ); gEngfuncs.pTriAPI->TexCoord2f( 0, 0 ); gEngfuncs.pTriAPI->Vertex3f (x, y, z); gEngfuncs.pTriAPI->TexCoord2f( 0, 1 ); gEngfuncs.pTriAPI->Vertex3f (x+xStep ,y, z); gEngfuncs.pTriAPI->TexCoord2f( 1, 1 ); gEngfuncs.pTriAPI->Vertex3f (x+xStep, y+yStep, z); gEngfuncs.pTriAPI->TexCoord2f( 1, 0 ); gEngfuncs.pTriAPI->Vertex3f (x, y+yStep, z); gEngfuncs.pTriAPI->End(); frame++; y+=yStep; } x+= xStep; } } } void CHudSpectator::DrawOverviewEntities() { /* int i,ir,ig,ib; struct model_s *hSpriteModel; vec3_t origin, angles, point, forward, right, left, up, world, screen, offset; float x,y,z, r,g,b, sizeScale = 4.0f; cl_entity_t * ent; float rmatrix[3][4]; // transformation matrix float zScale = (90.0f - v_angles[0] ) / 90.0f; z = m_OverviewData.layersHeights[0] * zScale; // get yellow/brown HUD color //UnpackRGB(ir,ig,ib, RGB_YELLOWISH); gHUD.GetPrimaryHudColor(ir, ig, ib); r = (float)ir/255.0f; g = (float)ig/255.0f; b = (float)ib/255.0f; gEngfuncs.pTriAPI->CullFace( TRI_NONE ); for (i=0; i < MAX_PLAYERS; i++ ) m_vPlayerPos[i][2] = -1; // mark as invisible // draw all players float depthOffset = 0; for (i=MAX_OVERVIEW_ENTITIES - 1; i >= 0; i--) { if ( !m_OverviewEntities[i].hSprite ) continue; hSpriteModel = (struct model_s *)gEngfuncs.GetSpritePointer( m_OverviewEntities[i].hSprite ); ent = m_OverviewEntities[i].entity; int theSpriteFrame = m_OverviewEntities[i].mFrame; gEngfuncs.pTriAPI->SpriteTexture( hSpriteModel, theSpriteFrame); gEngfuncs.pTriAPI->RenderMode( kRenderTransAdd); // see R_DrawSpriteModel // draws players sprite AngleVectors(ent->angles, right, up, NULL ); VectorCopy(ent->origin,origin); // Set origin of blip to just above map height, so blips are all drawn on map origin.z = m_OverviewData.layersHeights[0] + kOverviewEntityZHeight + depthOffset; gEngfuncs.pTriAPI->Begin( TRI_QUADS ); float gammaSlope = gHUD.GetGammaSlope(); gEngfuncs.pTriAPI->Color4f( m_OverviewEntities[i].mColorR / gammaSlope, m_OverviewEntities[i].mColorG / gammaSlope, m_OverviewEntities[i].mColorB / gammaSlope, 1); gEngfuncs.pTriAPI->TexCoord2f (1, 0); VectorMA (origin, 16.0f * sizeScale, up, point); VectorMA (point, 16.0f * sizeScale, right, point); point[2] *= zScale; gEngfuncs.pTriAPI->Vertex3fv (point); gEngfuncs.pTriAPI->TexCoord2f (0, 0); VectorMA (origin, 16.0f * sizeScale, up, point); VectorMA (point, -16.0f * sizeScale, right, point); point[2] *= zScale; gEngfuncs.pTriAPI->Vertex3fv (point); gEngfuncs.pTriAPI->TexCoord2f (0,1); VectorMA (origin, -16.0f * sizeScale, up, point); VectorMA (point, -16.0f * sizeScale, right, point); point[2] *= zScale; gEngfuncs.pTriAPI->Vertex3fv (point); gEngfuncs.pTriAPI->TexCoord2f (1,1); VectorMA (origin, -16.0f * sizeScale, up, point); VectorMA (point, 16.0f * sizeScale, right, point); point[2] *= zScale; gEngfuncs.pTriAPI->Vertex3fv (point); gEngfuncs.pTriAPI->End (); if ( !ent->player) continue; // draw line under player icons origin[2] *= zScale; gEngfuncs.pTriAPI->RenderMode( kRenderTransAdd ); hSpriteModel = (struct model_s *)gEngfuncs.GetSpritePointer( m_hsprBeam ); gEngfuncs.pTriAPI->SpriteTexture( hSpriteModel, 0 ); gEngfuncs.pTriAPI->Color4f(r, g, b, 0.3); gEngfuncs.pTriAPI->Begin ( TRI_QUADS ); gEngfuncs.pTriAPI->TexCoord2f (1, 0); gEngfuncs.pTriAPI->Vertex3f (origin[0]+4, origin[1]+4, origin[2]-zScale); gEngfuncs.pTriAPI->TexCoord2f (0, 0); gEngfuncs.pTriAPI->Vertex3f (origin[0]-4, origin[1]-4, origin[2]-zScale); gEngfuncs.pTriAPI->TexCoord2f (0, 1); gEngfuncs.pTriAPI->Vertex3f (origin[0]-4, origin[1]-4,z); gEngfuncs.pTriAPI->TexCoord2f (1, 1); gEngfuncs.pTriAPI->Vertex3f (origin[0]+4, origin[1]+4,z); gEngfuncs.pTriAPI->End (); gEngfuncs.pTriAPI->Begin ( TRI_QUADS ); gEngfuncs.pTriAPI->TexCoord2f (1, 0); gEngfuncs.pTriAPI->Vertex3f (origin[0]-4, origin[1]+4, origin[2]-zScale); gEngfuncs.pTriAPI->TexCoord2f (0, 0); gEngfuncs.pTriAPI->Vertex3f (origin[0]+4, origin[1]-4, origin[2]-zScale); gEngfuncs.pTriAPI->TexCoord2f (0, 1); gEngfuncs.pTriAPI->Vertex3f (origin[0]+4, origin[1]-4,z); gEngfuncs.pTriAPI->TexCoord2f (1, 1); gEngfuncs.pTriAPI->Vertex3f (origin[0]-4, origin[1]+4,z); gEngfuncs.pTriAPI->End (); // calculate screen position for name and infromation in hud::draw() if ( gEngfuncs.pTriAPI->WorldToScreen(origin,screen) ) continue; // object is behind viewer screen[0] = XPROJECT(screen[0]); screen[1] = YPROJECT(screen[1]); screen[2] = 0.0f; // calculate some offset under the icon origin[0]+=32.0f; origin[1]+=32.0f; gEngfuncs.pTriAPI->WorldToScreen(origin,offset); offset[0] = XPROJECT(offset[0]); offset[1] = YPROJECT(offset[1]); offset[2] = 0.0f; VectorSubtract(offset, screen, offset ); int playerNum = ent->index - 1; m_vPlayerPos[playerNum][0] = screen[0]; m_vPlayerPos[playerNum][1] = screen[1] + Length(offset); m_vPlayerPos[playerNum][2] = 1; // mark player as visible } if ( !m_pip || !m_drawcone->value ) return; // get current camera position and angle if ( m_pip == INSET_IN_EYE || g_iUser1 == OBS_IN_EYE ) { V_GetInEyePos( g_iUser2, origin, angles ); } else if ( m_pip == INSET_CHASE_FREE || g_iUser1 == OBS_CHASE_FREE ) { V_GetChasePos( g_iUser2, v_cl_angles, origin, angles ); } else if ( g_iUser1 == OBS_ROAMING ) { VectorCopy( v_sim_org, origin ); VectorCopy( v_cl_angles, angles ); } else V_GetChasePos( g_iUser2, NULL, origin, angles ); // draw camera sprite x = origin[0]; y = origin[1]; z = origin[2]; // Set origin of cone to just above map height, so blips are all drawn on map z = m_OverviewData.layersHeights[0] + kOverviewEntityZHeight; angles[0] = 0; // always show horizontal camera sprite hSpriteModel = (struct model_s *)gEngfuncs.GetSpritePointer( m_hsprCamera ); gEngfuncs.pTriAPI->RenderMode( kRenderTransAdd ); gEngfuncs.pTriAPI->SpriteTexture( hSpriteModel, 0 ); gEngfuncs.pTriAPI->Color4f( r, g, b, 1.0 ); AngleVectors(angles, forward, NULL, NULL ); VectorScale (forward, 512.0f, forward); offset[0] = 0.0f; offset[1] = 45.0f; offset[2] = 0.0f; AngleMatrix(offset, rmatrix ); VectorTransform(forward, rmatrix , right ); offset[1]= -45.0f; AngleMatrix(offset, rmatrix ); VectorTransform(forward, rmatrix , left ); gEngfuncs.pTriAPI->Begin (TRI_TRIANGLES); gEngfuncs.pTriAPI->TexCoord2f( 0, 0 ); gEngfuncs.pTriAPI->Vertex3f (x+right[0], y+right[1], (z+right[2]) * zScale); gEngfuncs.pTriAPI->TexCoord2f( 0, 1 ); gEngfuncs.pTriAPI->Vertex3f (x, y, z * zScale); gEngfuncs.pTriAPI->TexCoord2f( 1, 1 ); gEngfuncs.pTriAPI->Vertex3f (x+left[0], y+left[1], (z+left[2]) * zScale); gEngfuncs.pTriAPI->End (); */ } void CHudSpectator::DrawOverview() { /* // draw only in sepctator mode if ( !g_iUser1 ) return; // Only draw the overview if Map Mode is selected for this view if ( m_iDrawCycle == 0 && ( (g_iUser1 != OBS_MAP_FREE) && (g_iUser1 != OBS_MAP_CHASE) ) ) return; if ( m_iDrawCycle == 1 && m_pip->value < INSET_MAP_FREE ) return; DrawOverviewLayer(); DrawOverviewEntities(); CheckOverviewEntities(); */ } void CHudSpectator::CheckOverviewEntities() { double time = gEngfuncs.GetClientTime(); // removes old entities from list for ( int i = 0; i< MAX_OVERVIEW_ENTITIES; i++ ) { // remove entity from list if it is too old if ( m_OverviewEntities[i].killTime < time ) { memset( &m_OverviewEntities[i], 0, sizeof (overviewEntity_t) ); } } } bool CHudSpectator::AddOverviewEntity( int type, struct cl_entity_s *ent, const char *modelname) { HSPRITE hSprite = 0; double duration = -1.0f; // duration -1 means show it only this frame; int theFrame = 0; bool theSuccess = false; int theRenderMode; if ( ent ) { if (ent->curstate.solid != SOLID_NOT) { gHUD.GetSpriteForUser3(AvHUser3(ent->curstate.iuser3), hSprite, theFrame, theRenderMode); } /* if ( type == ET_PLAYER ) { if ( ent->curstate.solid != SOLID_NOT) { int thePlayerClass = g_PlayerExtraInfo[ent->index].playerclass; switch(thePlayerClass) { case PLAYERCLASS_ALIVE_MARINE: hSprite = this->m_hsprPlayerMarine; theFrame = 0; break; case PLAYERCLASS_ALIVE_HEAVY_MARINE: hSprite = this->m_hsprPlayerMarine; theFrame = 1; break; case PLAYERCLASS_COMMANDER: hSprite = this->m_hsprPlayerMarine; theFrame = 2; break; case PLAYERCLASS_ALIVE_LEVEL1: hSprite = this->m_hsprPlayerAlien; theFrame = 0; break; case PLAYERCLASS_ALIVE_LEVEL2: hSprite = this->m_hsprPlayerAlien; theFrame = 1; break; case PLAYERCLASS_ALIVE_LEVEL3: hSprite = this->m_hsprPlayerAlien; theFrame = 2; break; case PLAYERCLASS_ALIVE_LEVEL4: hSprite = this->m_hsprPlayerAlien; theFrame = 3; break; case PLAYERCLASS_ALIVE_LEVEL5: hSprite = this->m_hsprPlayerAlien; theFrame = 4; break; case PLAYERCLASS_ALIVE_GESTATING: hSprite = this->m_hsprPlayerAlien; theFrame = 5; break; case PLAYERCLASS_ALIVE_DIGESTING: break; } } else { // it's an spectator } } else if (type == ET_NORMAL) { // Now help icons if(hSprite == 0) { AvHUser3 theUser3 = AvHUser3(ent->curstate.iuser3); theFrame = gHUD.GetHelpIconFrameFromUser3(theUser3); if(theFrame != -1) { hSprite = gHUD.GetHelpSprite(); } } } */ } if(hSprite > 0) { int theTeam = ent->curstate.team; float theR = kFTeamColors[theTeam][0]; float theG = kFTeamColors[theTeam][1]; float theB = kFTeamColors[theTeam][2]; theSuccess = AddOverviewEntityToList(hSprite, ent, gEngfuncs.GetClientTime() + duration, theFrame, theRenderMode, theR, theG, theB); } return theSuccess; } void CHudSpectator::DeathMessage(int victim) { // find out where the victim is cl_entity_t *pl = gEngfuncs.GetEntityByIndex(victim); if (pl && pl->player) AddOverviewEntityToList(m_hsprPlayerDead, pl, gEngfuncs.GetClientTime() + 2.0f, 0, kRenderTransTexture, 1, 1, 1); } bool CHudSpectator::AddOverviewEntityToList(HSPRITE sprite, cl_entity_t *ent, double killTime, int inFrame, int inRenderMode, float r, float g, float b) { for ( int i = 0; i< MAX_OVERVIEW_ENTITIES; i++ ) { // find empty entity slot if ( m_OverviewEntities[i].entity == NULL) { m_OverviewEntities[i].entity = ent; m_OverviewEntities[i].hSprite = sprite; m_OverviewEntities[i].killTime = killTime; m_OverviewEntities[i].mFrame = inFrame; m_OverviewEntities[i].mRenderMode = inRenderMode; m_OverviewEntities[i].mColorR = r; m_OverviewEntities[i].mColorG = g; m_OverviewEntities[i].mColorB = b; return true; } } return false; // maximum overview entities reached } void CHudSpectator::CheckSettings() { // disallow same inset mode as main mode: //m_pip->value = floor(m_pip->value); // Removed by mmcguire. /* if ( ( g_iUser1 < OBS_MAP_FREE ) && ( m_pip->value == INSET_CHASE_LOCKED || m_pip->value == INSET_IN_EYE ) ) { // otherwise both would show in World picures m_pip->value = INSET_OFF; } // disble in intermission screen if ( gHUD.m_iIntermission ) m_pip->value = INSET_OFF; */ // check chat mode if ( m_chatEnabled != (gHUD.m_SayText.m_HUD_saytext->value!=0) ) { // hud_saytext changed m_chatEnabled = (gHUD.m_SayText.m_HUD_saytext->value!=0); if ( gEngfuncs.IsSpectateOnly() ) { // tell proxy our new chat mode char chatcmd[32]; sprintf(chatcmd, "ignoremsg %i", m_chatEnabled?0:1 ); gEngfuncs.pfnServerCmd(chatcmd); } } // HL/TFC has no oberserver corsshair, so set it client side if ( g_iUser1 == OBS_IN_EYE ) { m_crosshairRect.left = 24; m_crosshairRect.top = 0; m_crosshairRect.right = 48; m_crosshairRect.bottom = 24; gHUD.SetCurrentCrosshair( m_hCrosshair, m_crosshairRect, 255, 255, 255 ); } else { memset( &m_crosshairRect,0,sizeof(m_crosshairRect) ); gHUD.SetCurrentCrosshair( 0, m_crosshairRect, 0, 0, 0 ); } // Removed by mmcguire. /* // if we are a real player on server don't allow inset window // in First Person mode since this is our resticted forcecamera mode 2 // team number 3 = SPECTATOR see player.h if ( ( (g_iTeamNumber == 1) || (g_iTeamNumber == 2)) && (g_iUser1 == OBS_IN_EYE) ) m_pip->value = INSET_OFF; */ // draw small border around inset view, adjust upper black bar //gViewPort->m_pSpectatorPanel->EnableInsetView( m_pip->value != INSET_OFF ); gViewPort->m_pSpectatorPanel->EnableInsetView( IsInOverviewMode() ); } void CHudSpectator::Reset() { // Reset HUD if ( strcmp( m_OverviewData.map, gEngfuncs.pfnGetLevelName() ) ) { // update level overview if level changed ParseOverviewFile(); LoadMapSprites(); } memset( &m_OverviewEntities, 0, sizeof(m_OverviewEntities)); SetSpectatorStartPosition(); } void CHudSpectator::InitHUDData() { gHUD.InitHUDData(); m_lastPrimaryObject = m_lastSecondaryObject = 0; m_flNextObserverInput = 0.0f; m_lastHudMessage = 0; m_iSpectatorNumber = 0; iJumpSpectator = 0; g_iUser1 = g_iUser2 = 0; memset( &m_OverviewData, 0, sizeof(m_OverviewData)); memset( &m_OverviewEntities, 0, sizeof(m_OverviewEntities)); if ( gEngfuncs.IsSpectateOnly() || gEngfuncs.pDemoAPI->IsPlayingback() ) m_autoDirector->value = 1.0f; else m_autoDirector->value = 0.0f; Reset(); SetMode( OBS_CHASE_FREE); g_iUser2 = 0; // fake not target until first camera command // reset HUD FOV gHUD.m_iFOV = CVAR_GET_FLOAT("default_fov"); }