//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Entities that capture the player's UI and move it into game design // as outputs. // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "entitylist.h" #include "util.h" #include "physics.h" #include "entityoutput.h" #include "player.h" #include "in_buttons.h" #include "basecombatweapon.h" #include "baseviewmodel.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //---------------------------------------------------------------- // Spawn flags //---------------------------------------------------------------- #define SF_GAMEUI_FREEZE_PLAYER 32 #define SF_GAMEUI_HIDE_WEAPON 64 #define SF_GAMEUI_USE_DEACTIVATES 128 #define SF_GAMEUI_JUMP_DEACTIVATES 256 class CGameUI : public CBaseEntity { public: DECLARE_CLASS( CGameUI, CBaseEntity ); DECLARE_DATADESC(); // Input handlers void InputDeactivate( inputdata_t &inputdata ); void InputActivate( inputdata_t &inputdata ); void Think( void ); void Deactivate( CBaseEntity *pActivator ); float m_flFieldOfView; CHandle m_hSaveWeapon; COutputEvent m_playerOn; COutputEvent m_playerOff; COutputEvent m_pressedMoveLeft; COutputEvent m_pressedMoveRight; COutputEvent m_pressedForward; COutputEvent m_pressedBack; COutputEvent m_pressedAttack; COutputEvent m_pressedAttack2; COutputEvent m_unpressedMoveLeft; COutputEvent m_unpressedMoveRight; COutputEvent m_unpressedForward; COutputEvent m_unpressedBack; COutputEvent m_unpressedAttack; COutputEvent m_unpressedAttack2; COutputFloat m_xaxis; COutputFloat m_yaxis; COutputFloat m_attackaxis; COutputFloat m_attack2axis; bool m_bForceUpdate; int m_nLastButtonState; CHandle m_player; }; BEGIN_DATADESC( CGameUI ) DEFINE_KEYFIELD( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ), DEFINE_FIELD( m_hSaveWeapon, FIELD_EHANDLE ), DEFINE_FIELD( m_bForceUpdate, FIELD_BOOLEAN ), DEFINE_FIELD( m_player, FIELD_EHANDLE ), DEFINE_FIELD( m_nLastButtonState, FIELD_INTEGER ), DEFINE_INPUTFUNC( FIELD_VOID, "Deactivate", InputDeactivate ), DEFINE_INPUTFUNC( FIELD_STRING, "Activate", InputActivate ), DEFINE_OUTPUT( m_playerOn, "PlayerOn" ), DEFINE_OUTPUT( m_playerOff, "PlayerOff" ), DEFINE_OUTPUT( m_pressedMoveLeft, "PressedMoveLeft" ), DEFINE_OUTPUT( m_pressedMoveRight, "PressedMoveRight" ), DEFINE_OUTPUT( m_pressedForward, "PressedForward" ), DEFINE_OUTPUT( m_pressedBack, "PressedBack" ), DEFINE_OUTPUT( m_pressedAttack, "PressedAttack" ), DEFINE_OUTPUT( m_pressedAttack2, "PressedAttack2" ), DEFINE_OUTPUT( m_unpressedMoveLeft, "UnpressedMoveLeft" ), DEFINE_OUTPUT( m_unpressedMoveRight, "UnpressedMoveRight" ), DEFINE_OUTPUT( m_unpressedForward, "UnpressedForward" ), DEFINE_OUTPUT( m_unpressedBack, "UnpressedBack" ), DEFINE_OUTPUT( m_unpressedAttack, "UnpressedAttack" ), DEFINE_OUTPUT( m_unpressedAttack2, "UnpressedAttack2" ), DEFINE_OUTPUT( m_xaxis, "XAxis" ), DEFINE_OUTPUT( m_yaxis, "YAxis" ), DEFINE_OUTPUT( m_attackaxis, "AttackAxis" ), DEFINE_OUTPUT( m_attack2axis, "Attack2Axis" ), END_DATADESC() LINK_ENTITY_TO_CLASS( game_ui, CGameUI ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CGameUI::InputDeactivate( inputdata_t &inputdata ) { Deactivate( inputdata.pActivator ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CGameUI::Deactivate( CBaseEntity *pActivator ) { CBasePlayer *pPlayer = m_player; AssertMsg(pPlayer, "CGameUI deactivated without a player!"); if (pPlayer) { // Re-enable player motion if ( FBitSet( m_spawnflags, SF_GAMEUI_FREEZE_PLAYER ) ) { m_player->RemoveFlag( FL_ATCONTROLS ); } // Restore weapons if ( FBitSet( m_spawnflags, SF_GAMEUI_HIDE_WEAPON ) ) { // Turn the hud back on pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION; if ( m_hSaveWeapon.Get() ) { m_player->Weapon_Switch( m_hSaveWeapon.Get() ); m_hSaveWeapon = NULL; } if ( pPlayer->GetActiveWeapon() ) { pPlayer->GetActiveWeapon()->Deploy(); } } // Announce that the player is no longer controlling through us m_playerOff.FireOutput( pPlayer, this, 0 ); // Clear out the axis controls m_xaxis.Set( 0, pPlayer, this ); m_yaxis.Set( 0, pPlayer, this ); m_attackaxis.Set( 0, pPlayer, this ); m_attack2axis.Set( 0, pPlayer, this ); m_nLastButtonState = 0; m_player = NULL; } else { Warning("%s Deactivate(): I have no player when called by %s!\n", GetEntityName().ToCStr(), pActivator->GetEntityName().ToCStr()); } // Stop thinking SetNextThink( TICK_NEVER_THINK ); } //------------------------------------------------------------------------------ // Purpose : //------------------------------------------------------------------------------ void CGameUI::InputActivate( inputdata_t &inputdata ) { CBasePlayer *pPlayer; // Determine if we're specifying this as an override parameter if ( inputdata.value.StringID() != NULL_STRING ) { CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller ); if ( pEntity == NULL || pEntity->IsPlayer() == false ) { Warning( "%s InputActivate: entity %s not found or is not a player!\n", GetEntityName().ToCStr(), inputdata.value.String() ); return; } pPlayer = ToBasePlayer( pEntity ); } else { // Otherwise try to use the activator if ( inputdata.pActivator == NULL || inputdata.pActivator->IsPlayer() == false ) { Warning( "%s InputActivate: invalid or missing !activator!\n", GetEntityName().ToCStr() ); return; } pPlayer = ToBasePlayer( inputdata.pActivator ); } // If another player is already using these controls3, ignore this activation if ( m_player.Get() != NULL && pPlayer != m_player.Get() ) { // TODO: We could allow this by calling Deactivate() at this point and continuing on -- jdw return; } // Setup our internal data m_player = pPlayer; m_playerOn.FireOutput( pPlayer, this, 0 ); // Turn the hud off SetNextThink( gpGlobals->curtime ); // Disable player's motion if ( FBitSet( m_spawnflags, SF_GAMEUI_FREEZE_PLAYER ) ) { m_player->AddFlag( FL_ATCONTROLS ); } // Store off and hide the currently held weapon if ( FBitSet( m_spawnflags, SF_GAMEUI_HIDE_WEAPON ) ) { m_player->m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION; if ( m_player->GetActiveWeapon() ) { m_hSaveWeapon = m_player->GetActiveWeapon(); m_player->GetActiveWeapon()->Holster(); m_player->ClearActiveWeapon(); m_player->HideViewModels(); } } // We must update our state m_bForceUpdate = true; } //------------------------------------------------------------------------------ // Purpose: Samples the player's inputs and fires outputs based on what buttons // are currently held down. //------------------------------------------------------------------------------ void CGameUI::Think( void ) { CBasePlayer *pPlayer = m_player; // If player is gone, stop thinking if (pPlayer == NULL) { SetNextThink( TICK_NEVER_THINK ); return; } // If we're forcing an update, state with a clean button state if ( m_bForceUpdate ) { m_nLastButtonState = pPlayer->m_nButtons; } // ------------------------------------------------ // Check that toucher is facing the UI within // the field of view tolerance. If not disconnect // ------------------------------------------------ if (m_flFieldOfView > -1) { Vector vPlayerFacing; pPlayer->EyeVectors( &vPlayerFacing ); Vector vPlayerToUI = GetAbsOrigin() - pPlayer->WorldSpaceCenter(); VectorNormalize(vPlayerToUI); float flDotPr = DotProduct(vPlayerFacing,vPlayerToUI); if (flDotPr < m_flFieldOfView) { Deactivate( pPlayer ); return; } } pPlayer->AddFlag( FL_ONTRAIN ); SetNextThink( gpGlobals->curtime ); // Deactivate if they jump or press +use. // FIXME: prevent the use from going through in player.cpp if ((( pPlayer->m_afButtonPressed & IN_USE ) && ( m_spawnflags & SF_GAMEUI_USE_DEACTIVATES )) || (( pPlayer->m_afButtonPressed & IN_JUMP ) && ( m_spawnflags & SF_GAMEUI_JUMP_DEACTIVATES ))) { Deactivate( pPlayer ); return; } // Determine what's different int nButtonsChanged = ( pPlayer->m_nButtons ^ m_nLastButtonState ); // // Handle all our possible input triggers // if ( nButtonsChanged & IN_MOVERIGHT ) { if ( m_nLastButtonState & IN_MOVERIGHT ) { m_unpressedMoveRight.FireOutput( pPlayer, this, 0 ); } else { m_pressedMoveRight.FireOutput( pPlayer, this, 0 ); } } if ( nButtonsChanged & IN_MOVELEFT ) { if ( m_nLastButtonState & IN_MOVELEFT ) { m_unpressedMoveLeft.FireOutput( pPlayer, this, 0 ); } else { m_pressedMoveLeft.FireOutput( pPlayer, this, 0 ); } } if ( nButtonsChanged & IN_FORWARD ) { if ( m_nLastButtonState & IN_FORWARD ) { m_unpressedForward.FireOutput( pPlayer, this, 0 ); } else { m_pressedForward.FireOutput( pPlayer, this, 0 ); } } if ( nButtonsChanged & IN_BACK ) { if ( m_nLastButtonState & IN_BACK ) { m_unpressedBack.FireOutput( pPlayer, this, 0 ); } else { m_pressedBack.FireOutput( pPlayer, this, 0 ); } } if ( nButtonsChanged & IN_ATTACK ) { if ( m_nLastButtonState & IN_ATTACK ) { m_unpressedAttack.FireOutput( pPlayer, this, 0 ); } else { m_pressedAttack.FireOutput( pPlayer, this, 0 ); } } if ( nButtonsChanged & IN_ATTACK2 ) { if ( m_nLastButtonState & IN_ATTACK2 ) { m_unpressedAttack2.FireOutput( pPlayer, this, 0 ); } else { m_pressedAttack2.FireOutput( pPlayer, this, 0 ); } } // Setup for the next frame m_nLastButtonState = pPlayer->m_nButtons; float x = 0, y = 0, attack = 0, attack2 = 0; if ( pPlayer->m_nButtons & IN_MOVERIGHT ) { x = 1; } else if ( pPlayer->m_nButtons & IN_MOVELEFT ) { x = -1; } if ( pPlayer->m_nButtons & IN_FORWARD ) { y = 1; } else if ( pPlayer->m_nButtons & IN_BACK ) { y = -1; } if ( pPlayer->m_nButtons & IN_ATTACK ) { attack = 1; } if ( pPlayer->m_nButtons & IN_ATTACK2 ) { attack2 = 1; } // // Fire the analog outputs if they changed. // if ( m_bForceUpdate || ( m_xaxis.Get() != x ) ) { m_xaxis.Set( x, pPlayer, this ); } if ( m_bForceUpdate || ( m_yaxis.Get() != y ) ) { m_yaxis.Set( y, pPlayer, this ); } if ( m_bForceUpdate || ( m_attackaxis.Get() != attack ) ) { m_attackaxis.Set( attack, pPlayer, this ); } if ( m_bForceUpdate || ( m_attack2axis.Get() != attack2 ) ) { m_attack2axis.Set( attack2, pPlayer, this ); } m_bForceUpdate = false; }