//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $NoKeywords: $ //===========================================================================// #include "cbase.h" #include "hud_chat.h" #include "clientmode_commander.h" #include "vgui_int.h" #include "ivmodemanager.h" #include "iinput.h" #include "kbutton.h" #include "usercmd.h" #include "c_basetfplayer.h" #include "view_shared.h" #include "in_main.h" #include "commanderoverlaypanel.h" #include "iviewrender.h" #include #include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" enum { VISIBLE_STATIC_PROP_HEIGHT = 7500 }; extern Vector g_vecRenderOrigin; extern QAngle g_vecRenderAngles; static ConVar Commander_SlueSpeed( "commander_speed", "800.0", 0 ); static ConVar Commander_MouseSpeed( "commander_mousespeed", "200.0", 0 ); static ConVar Commander_RightMoveSpeedScale( "commander_rightmovespeedscale", "2.0", 0 ); static ConVar Commander_InvertMouse( "commander_invertmouse", "1.0", 0 ); // Public version of the commander mode; IClientMode *ClientModeCommander() { // TF2 Commander View Mode static CClientModeCommander g_ClientModeCommander; return &g_ClientModeCommander; } //----------------------------------------------------------------------------- // Purpose: // Input : *commander - //----------------------------------------------------------------------------- void CCommanderViewportPanel::SetCommanderView( CClientModeCommander *commander ) { m_pCommanderView = commander; if ( m_pOverlayPanel ) { m_pOverlayPanel->SetCommanderView( m_pCommanderView ); } } //----------------------------------------------------------------------------- // Purpose: // Output : CCommanderOverlayPanel //----------------------------------------------------------------------------- CCommanderOverlayPanel *CCommanderViewportPanel::GetCommanderOverlayPanel( void ) { return m_pOverlayPanel; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCommanderViewportPanel::CCommanderViewportPanel( void ) : m_CursorCommander( vgui::dc_arrow ), m_CursorRightMouseMove(vgui::dc_hand) { m_pOverlayPanel = new CCommanderOverlayPanel(); m_pOverlayPanel->SetParent( this ); SetPaintEnabled( false ); SetPaintBorderEnabled( false ); SetPaintBackgroundEnabled( false ); SetCursor( m_CursorCommander ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCommanderViewportPanel::~CCommanderViewportPanel( void ) { } //----------------------------------------------------------------------------- // call these when commander view is enabled/disabled //----------------------------------------------------------------------------- void CCommanderViewportPanel::Enable() { vgui::VPANEL pRoot = VGui_GetClientDLLRootPanel(); SetCursor(m_CursorCommander); vgui::surface()->SetCursor( m_CursorCommander ); // Make the viewport fill the root panel. if ( pRoot) { int wide, tall; vgui::ipanel()->GetSize(pRoot, wide, tall); SetBounds(0, 0, wide, tall); } C_BaseEntity *ent = cl_entitylist->GetEnt( 0 ); if ( m_pOverlayPanel && ent ) { m_pOverlayPanel->Enable(); } SetVisible( true ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCommanderViewportPanel::Disable() { if ( m_pOverlayPanel ) { m_pOverlayPanel->Disable(); } SetVisible( false ); } void CCommanderViewportPanel::MinimapClicked( const Vector& clickWorldPos ) { // Don't use Z... our current z is what we want Vector actualOrigin, offset; VectorCopy( clickWorldPos, actualOrigin ); actualOrigin.z = m_pOverlayPanel->TacticalOrigin().z; m_pOverlayPanel->ActualToVisibleOffset( offset ); VectorSubtract( actualOrigin, offset, actualOrigin ); m_pOverlayPanel->BoundOrigin( actualOrigin ); m_pOverlayPanel->TacticalOrigin() = actualOrigin; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CClientModeCommander::CClientModeCommander() : BaseClass() { m_pClear = NULL; m_pSkyBox = NULL; m_ScaledSlueSpeed = 10; m_Log_BaseEto2 = 1.4427f; // factor to convert from a logarithm of base E to base 2. m_pViewport = new CCommanderViewportPanel; GetCommanderViewport()->SetCommanderView( this ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CClientModeCommander::~CClientModeCommander() { } CCommanderViewportPanel *CClientModeCommander::GetCommanderViewport() { Assert( m_pViewport ); return static_cast< CCommanderViewportPanel * >( m_pViewport ); } //----------------------------------------------------------------------------- // Purpose: Called once at dll load time //----------------------------------------------------------------------------- void CClientModeCommander::Init( void ) { BaseClass::Init(); GetCommanderViewport()->RequestFocus(); } //----------------------------------------------------------------------------- // Purpose: // Output : vgui::Panel //----------------------------------------------------------------------------- vgui::Panel *CClientModeCommander::GetMinimapParent( void ) { return GetCommanderOverlayPanel(); } //----------------------------------------------------------------------------- // Inherited from IMinimapClient //----------------------------------------------------------------------------- void CClientModeCommander::MinimapClicked( const Vector& clickWorldPos ) { if ( GetCommanderViewport() ) { GetCommanderViewport()->MinimapClicked( clickWorldPos ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCommanderOverlayPanel *CClientModeCommander::GetCommanderOverlayPanel( void ) { if ( GetCommanderViewport() ) { return GetCommanderViewport()->GetCommanderOverlayPanel(); } return NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClientModeCommander::Enable() { // HACK: Find a better place for these m_pClear = (ConVar *)cvar->FindVar( "gl_clear" ); m_pSkyBox = (ConVar *)cvar->FindVar( "r_drawskybox" ); HudCommanderOverlayMgr()->Enable( true ); BaseClass::Enable(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClientModeCommander::Disable() { BaseClass::Disable(); ::input->ResetMouse(); HudCommanderOverlayMgr()->Enable( false ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClientModeCommander::Update() { if ( !engine->IsInGame() ) { // Disable commander view modemanager->SwitchMode( false, false ); return; } ClientModeTFBase::Update(); Vector mins, maxs; GetCommanderViewport()->GetCommanderOverlayPanel()->GetVisibleArea( mins, maxs ); MapData().SetVisibleArea( mins, maxs ); HudCommanderOverlayMgr()->Tick( ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClientModeCommander::Layout() { BaseClass::Layout(); // Force it to recompute it's boundaries GetCommanderViewport()->GetCommanderOverlayPanel()->Disable(); GetCommanderViewport()->GetCommanderOverlayPanel()->Enable(); } //----------------------------------------------------------------------------- // Purpose: The mode can choose to not draw fog //----------------------------------------------------------------------------- bool CClientModeCommander::ShouldDrawFog( void ) { return false; } //----------------------------------------------------------------------------- // Purpose: Checks map bounds and determines ideal height for tactical view // Input : fov - // zoom - // Output : float //----------------------------------------------------------------------------- float CClientModeCommander::GetHeightForMap( float zoom ) { Vector mins, maxs; MapData().GetMapBounds( mins, maxs ); return maxs.z + TACTICAL_ZOFFSET; } bool CClientModeCommander::GetOrthoParameters(CViewSetup *pSetup) { Vector vCenter; float xSize, ySize; GetCommanderViewport()->GetCommanderOverlayPanel()->GetOrthoRenderBox(vCenter, xSize, ySize); pSetup->m_bOrtho = true; pSetup->m_OrthoLeft = -xSize; pSetup->m_OrthoTop = -ySize; pSetup->m_OrthoRight = xSize; pSetup->m_OrthoBottom = ySize; return true; } //----------------------------------------------------------------------------- // Purpose: // Input : *angles - //----------------------------------------------------------------------------- void CClientModeCommander::OverrideView( CViewSetup *pSetup ) { // Turn off vis when in commander mode view->DisableVis(); VectorCopy( GetCommanderViewport()->GetCommanderOverlayPanel()->TacticalAngles(), pSetup->angles ); VectorCopy( GetCommanderViewport()->GetCommanderOverlayPanel()->TacticalOrigin(), pSetup->origin ); } //----------------------------------------------------------------------------- // Purpose: Scale commander slue speed based on viewport zoom factor // Output : float //----------------------------------------------------------------------------- float CClientModeCommander::GetScaledSlueSpeed( void ) { return m_ScaledSlueSpeed; } //----------------------------------------------------------------------------- // Purpose: Convert move to scaled move // Input : in - // Output : float //----------------------------------------------------------------------------- float CClientModeCommander::Commander_ResampleMove( float in ) { float sign; float move; if ( !in ) return 0.0; sign = in > 0.0 ? 1.0 : -1.0; move = GetScaledSlueSpeed(); return move * sign; } //----------------------------------------------------------------------------- // Purpose: Zero out any movement in the command // Input : *cmd - //----------------------------------------------------------------------------- void CClientModeCommander::ResetCommand( CUserCmd *cmd ) { cmd->buttons = 0; cmd->forwardmove = 0; cmd->sidemove = 0; cmd->upmove = 0; cmd->viewangles.Init(); } //----------------------------------------------------------------------------- // Purpose: TF2 commander mode movement logic //----------------------------------------------------------------------------- void CClientModeCommander::IsometricMove( CUserCmd *cmd ) { int i; Vector wishvel; float fmove, smove; Vector forward, right, up; AngleVectors ( cmd->viewangles, &forward, &right, &up); // Determine movement angles // Copy movement amounts fmove = cmd->forwardmove; smove = cmd->sidemove; // No up / down movement forward.Init(1, 0, 0); right.Init(0, -1, 0); wishvel.Init(); // Determine x and y parts of velocity for (i=0; i < 3; i++) { wishvel[i] = forward[i]*fmove + right[i]*smove; } GetCommanderViewport()->GetCommanderOverlayPanel()->TacticalOrigin() += TICK_INTERVAL * wishvel; GetCommanderViewport()->GetCommanderOverlayPanel()->BoundOrigin( GetCommanderViewport()->GetCommanderOverlayPanel()->TacticalOrigin() ); } #define WINDOWED_KEEPMOVING_PIXELS 300 //----------------------------------------------------------------------------- // Purpose: // Input : frametime - // *cmd - //----------------------------------------------------------------------------- void CClientModeCommander::CreateMove( float flInputSampleTime, CUserCmd *cmd ) { int mx, my; int realx, realy; //int sidex, sidey; cmd->upmove = 0; // Figure out the speed scale so their perceptual movement speed stays the same. m_ScaledSlueSpeed = Commander_SlueSpeed.GetFloat() * GetCommanderViewport()->GetCommanderOverlayPanel()->WorldUnitsPerPixel(); m_ScaledMouseSpeed = Commander_MouseSpeed.GetFloat() * GetCommanderViewport()->GetCommanderOverlayPanel()->WorldUnitsPerPixel(); // Translate WASD while in commander mode... float temp = cmd->forwardmove; // Swap forward/right cmd->forwardmove = cmd->sidemove; // Invert right/left cmd->sidemove = -temp; // Normalize nonzero inputs to scaled speed if ( cmd->forwardmove ) { cmd->forwardmove = ( cmd->forwardmove > 0 ) ? GetScaledSlueSpeed() : -GetScaledSlueSpeed(); } if ( cmd->sidemove ) { cmd->sidemove = ( cmd->sidemove > 0 ) ? GetScaledSlueSpeed() : -GetScaledSlueSpeed(); } // Sample mouse ::input->GetFullscreenMousePos( &mx, &my, &realx, &realy ); if( GetCommanderViewport()->GetCommanderOverlayPanel()->IsRightMouseMapMoving() || ( in_commandermousemove.state & 1 ) ) { cmd->forwardmove = m_ScaledMouseSpeed * (mx - m_LastMouseX); cmd->sidemove = m_ScaledMouseSpeed * (my - m_LastMouseY); if ( Commander_InvertMouse.GetInt() ) { cmd->forwardmove *= -1.0f; cmd->sidemove *= -1.0f; } //input->SetFullscreenMousePos( m_LastMouseX, m_LastMouseY ); mx = m_LastMouseX; my = m_LastMouseY; } /* else if ( input->IsFullscreenMouse() ) { if ( abs( realx - mx ) < WINDOWED_KEEPMOVING_PIXELS && abs( realy - my ) < WINDOWED_KEEPMOVING_PIXELS ) { sidex = 2; sidey = 2; // Check Size of viewport if ( mx < sidex ) { cmd->forwardmove = -GetScaledSlueSpeed(); } else if ( mx > ( ScreenWidth() - sidex )) { cmd->forwardmove = GetScaledSlueSpeed(); } if ( my < sidey ) { cmd->sidemove = -GetScaledSlueSpeed(); } else if ( my > ( ScreenHeight() - sidey ) ) { cmd->sidemove = GetScaledSlueSpeed(); } } } */ m_LastMouseX = mx; m_LastMouseY = my; // Look straight down cmd->viewangles.x = 90; // 45; cmd->viewangles.y = 90; //45fmod( 3.0* 360 * (gpGlobals->curtime * 0.01), 360 ); cmd->viewangles.z = 0; GetCommanderViewport()->GetCommanderOverlayPanel()->TacticalAngles() = cmd->viewangles; IsometricMove( cmd ); // Reset command ResetCommand( cmd ); } //----------------------------------------------------------------------------- // Purpose: Makes sure the mouse is over the same world position as it started //----------------------------------------------------------------------------- void CClientModeCommander::MoveMouse( Vector& worldPos ) { Vector worldCenter; float wworld, hworld; GetCommanderViewport()->GetCommanderOverlayPanel()->GetOrthoRenderBox(worldCenter, wworld, hworld); wworld *= 2; hworld *= 2; Vector worldDelta; VectorSubtract( worldPos, worldCenter, worldDelta ); int w, h; GetCommanderViewport()->GetSize( w, h ); int mx, my; mx = (worldDelta.x / wworld + 0.5f) * w; my = (0.5f - worldDelta.y / hworld) * h; // Clamp if (mx < 0) mx = 0; else if (mx > w) mx = w; if (my < 0) my = 0; else if (my > h) my = h; //input->SetFullscreenMousePos( mx, my ); m_LastMouseX = mx; m_LastMouseY = my; } //----------------------------------------------------------------------------- // Purpose: // Input : *newmap - //----------------------------------------------------------------------------- void CClientModeCommander::LevelInit( const char *newmap ) { BaseClass::LevelInit( newmap ); HudCommanderOverlayMgr()->LevelShutdown(); MapData().LevelInit( newmap ); GetCommanderViewport()->GetCommanderOverlayPanel()->LevelInit( newmap ); HudCommanderOverlayMgr()->LevelInit( ); GetCommanderViewport()->Enable(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClientModeCommander::LevelShutdown( void ) { GetCommanderViewport()->Disable(); MapData().LevelShutdown(); HudCommanderOverlayMgr()->LevelShutdown(); GetCommanderViewport()->GetCommanderOverlayPanel()->LevelShutdown(); BaseClass::LevelShutdown(); } //----------------------------------------------------------------------------- // returns the viewport panel //----------------------------------------------------------------------------- vgui::Panel *CClientModeCommander::GetViewport() { return m_pViewport; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CClientModeCommander::ShouldDrawEntity(C_BaseEntity *pEnt) { return MapData().IsEntityVisibleToTactical(pEnt); } bool CClientModeCommander::ShouldDrawDetailObjects( ) { return false; } //----------------------------------------------------------------------------- // Purpose: Always draw the local player while in commander mode //----------------------------------------------------------------------------- bool CClientModeCommander::ShouldDrawLocalPlayer( C_BasePlayer *pPlayer ) { return true; } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CClientModeCommander::ShouldDrawViewModel( void ) { return false; } //----------------------------------------------------------------------------- // Purpose: Return false to disable crosshair // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CClientModeCommander::ShouldDrawCrosshair( void ) { return false; } //----------------------------------------------------------------------------- // Purpose: Adjust engine rendering viewport rectangle if needed // Input : x - // y - // width - // height - //----------------------------------------------------------------------------- void CClientModeCommander::AdjustEngineViewport( int& x, int& y, int& width, int& height ) { } //----------------------------------------------------------------------------- // Should I draw particles //----------------------------------------------------------------------------- bool CClientModeCommander::ShouldDrawParticles( ) { Vector vCenter; float xSize, ySize; GetCommanderViewport()->GetCommanderOverlayPanel()->GetOrthoRenderBox(vCenter, xSize, ySize); // Activate/deactivate particles rendering based on zoom level float maxSize = MAX( xSize, ySize ); return (maxSize < VISIBLE_STATIC_PROP_HEIGHT); } //----------------------------------------------------------------------------- // Purpose: When in commander mode, force gl_clear and don't draw the skybox //----------------------------------------------------------------------------- void CClientModeCommander::PreRender( CViewSetup *pSetup ) { if ( !m_pClear || !m_pSkyBox ) return; m_fOldClear = m_pClear->GetFloat(); m_pClear->SetValue( 1.0f ); pSetup->clearColor = !!m_pClear->GetInt(); m_fOldSkybox = m_pSkyBox->GetFloat(); m_pSkyBox->SetValue( 0.0f ); GetOrthoParameters(pSetup); render->DrawTopView( true ); Vector2D mins = pSetup->origin.AsVector2D(); Vector2D maxs = pSetup->origin.AsVector2D(); mins.x += pSetup->m_OrthoLeft; maxs.x += pSetup->m_OrthoRight; mins.y += pSetup->m_OrthoTop; maxs.y += pSetup->m_OrthoBottom; render->TopViewBounds( mins, maxs ); // Activate/deactivate static prop + particles rendering based on zoom level Vector2D size; Vector2DSubtract( maxs, mins, size ); float maxSize = MAX( size.x, size.y ); bool showStaticProps = (maxSize < VISIBLE_STATIC_PROP_HEIGHT); ClientLeafSystem()->DrawStaticProps(showStaticProps); ClientLeafSystem()->DrawSmallEntities(showStaticProps); BaseClass::PreRender(pSetup); } void CClientModeCommander::PostRenderWorld() { render->DrawTopView( false ); ClientLeafSystem()->DrawStaticProps(true); ClientLeafSystem()->DrawSmallEntities(true); } //----------------------------------------------------------------------------- // Purpose: Restore cvar values //----------------------------------------------------------------------------- void CClientModeCommander::PostRender( void ) { if ( !m_pClear || !m_pSkyBox ) return; m_pClear->SetValue( m_fOldClear ); m_pSkyBox->SetValue( m_fOldSkybox ); BaseClass::PostRender(); } //----------------------------------------------------------------------------- // Purpose: Swallow mouse wheel when in this view // Input : down - // keynum - // *pszCurrentBinding - // Output : int //----------------------------------------------------------------------------- int CClientModeCommander::KeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ) { switch ( keynum ) { case MOUSE_WHEEL_UP: case MOUSE_WHEEL_DOWN: // Swallow return 0; } // Allow engine to process return BaseClass::KeyInput( down, keynum, pszCurrentBinding ); }