diff --git a/dlls/client.cpp b/dlls/client.cpp index d6804daf..0801dd7c 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -161,6 +161,12 @@ void ClientKill( edict_t *pEntity ) { entvars_t *pev = &pEntity->v; + if( pev->flags & FL_SPECTATOR ) + { + pev->health = 1; + return; + } + CBasePlayer *pl = (CBasePlayer*) CBasePlayer::Instance( pev ); if ( pl->m_fNextSuicideTime > gpGlobals->time ) @@ -471,7 +477,7 @@ void ClientCommand( edict_t *pEntity ) { GetClassPtr((CBasePlayer *)pev)->SelectLastItem(); } - else if ( FStrEq( pcmd, "spectate" ) && (pev->flags & FL_PROXY) ) // added for proxy support + else if ( FStrEq( pcmd, "spectate" ) ) // added for proxy support { CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev); diff --git a/dlls/multiplay_gamerules.cpp b/dlls/multiplay_gamerules.cpp index 6b4bfa93..20203d15 100644 --- a/dlls/multiplay_gamerules.cpp +++ b/dlls/multiplay_gamerules.cpp @@ -95,6 +95,88 @@ void CoopClearWeaponList( void ) g_WeaponList.Clear(); } + +void BecomeSpectator( CBasePlayer *pPlayer ) +{ + //pPlayer->m_bDoneFirstSpawn = true; + pPlayer->pev->takedamage = DAMAGE_NO; + pPlayer->pev->flags |= FL_SPECTATOR; + pPlayer->pev->flags |= FL_NOTARGET; + pPlayer->pev->effects |= EF_NODRAW; + pPlayer->pev->solid = SOLID_NOT; + pPlayer->pev->movetype = MOVETYPE_NOCLIP; + pPlayer->pev->modelindex = 0; + pPlayer->pev->health = 1; + pPlayer->m_pGoalEnt = NULL; + return; +} + +void SpawnPlayer( CBasePlayer *pPlayer ) +{ + pPlayer->m_state = STATE_SPAWNED; + pPlayer->m_iRespawnFrames = 0; + pPlayer->pev->effects &= ~EF_NODRAW; + + pPlayer->pev->takedamage = DAMAGE_YES; + pPlayer->pev->flags &= ~FL_SPECTATOR; + pPlayer->pev->movetype = MOVETYPE_WALK; + pPlayer->Spawn(); + +} + +extern int gmsgShowMenu; + +void ShowMenu( CBasePlayer *pPlayer, const char *title, int count, const char **slot ) +{ + char buf[128], *pbuf = buf; + short int flags = 1<<9; + pbuf += sprintf( pbuf, "^2%s:\n", title ); + for( int i = 0; i < count; i++ ) + { + pbuf += sprintf( pbuf, "^3%d.^7 %s\n", i+1, slot[i]); + flags |= 1<pev); + WRITE_SHORT( flags ); // slots + WRITE_CHAR( -1 ); // show time + WRITE_BYTE( 0 ); // need more + WRITE_STRING( buf ); + MESSAGE_END(); + CLIENT_COMMAND( pPlayer->edict(), "exec touch_default/numbers.cfg\n"); +} + +void CoopMenu( CBasePlayer *pPlayer ) +{ + if( pPlayer->m_state == STATE_SPAWNED ) + { + + if( mp_coop.value ) + { + const char *menu[] = { + "Force respawn", + "Unblock", + "Become spectator", + //"Vote changelevel" + }; + ShowMenu( pPlayer, "Coop menu", ARRAYSIZE( menu ), menu ); + } + } + else if ( pPlayer->m_state == STATE_SPECTATOR ) + { + if( mp_coop.value ) + { + const char *menu[] = { + "Spawn", + "Close menu" + }; + ShowMenu( pPlayer, "Spectator menu", ARRAYSIZE( menu ), menu ); + } + } +} + +int g_iMenu; + + //********************************************************* // Rules for the half-life multiplayer game. //********************************************************* @@ -152,6 +234,59 @@ BOOL CHalfLifeMultiplay::ClientCommand( CBasePlayer *pPlayer, const char *pcmd ) if( g_VoiceGameMgr.ClientCommand( pPlayer, pcmd ) ) return TRUE; #endif + if( FStrEq( pcmd, "joincoop" ) ) + { + if( pPlayer->m_state == STATE_SPECTATOR_BEGIN ) + SpawnPlayer( pPlayer ); + else + ClientPrint( pPlayer->pev, HUD_PRINTCONSOLE, "You cannot use joincoop now!" ); + + return TRUE; + } + if( FStrEq( pcmd, "menuselect" ) ) + { + int imenu = atoi( CMD_ARGV( 1 ) ); + switch( pPlayer->m_state ) + { + case STATE_SPECTATOR_BEGIN: + case STATE_SPECTATOR: + if( imenu == 1 ) + { + SpawnPlayer( pPlayer ); + } + if( imenu == 2 ) + { + pPlayer->m_state = STATE_SPECTATOR; + } + case STATE_SPAWNED: + if( imenu == 1 ) + { + SpawnPlayer( pPlayer ); + } + if( imenu == 2 ) + { + UTIL_CleanSpawnPoint( pPlayer->pev->origin, 150 ); + } + if( imenu == 3 ) + { + pPlayer->RemoveAllItems( TRUE ); + BecomeSpectator( pPlayer ); + } + default: + break; + } + return TRUE; + } + if( FStrEq( pcmd, "coopmenu" ) ) + { + if( !g_iMenu ) + CoopMenu( pPlayer ); + else + ClientPrint( pPlayer->pev, HUD_PRINTCONSOLE, "You cannot use coopmenu now!" ); + + return TRUE; + } + return CGameRules::ClientCommand( pPlayer, pcmd ); } @@ -505,6 +640,20 @@ void CHalfLifeMultiplay::InitHUD( CBasePlayer *pl ) MESSAGE_BEGIN( MSG_ONE, SVC_INTERMISSION, NULL, pl->edict() ); MESSAGE_END(); } + + if( pl->m_state == STATE_SPECTATOR_BEGIN ) + { + + if( mp_coop.value ) + { + const char *menu[] = { + "Join coop", + "Join spectators" + }; + ShowMenu( pl, "COOP SERVER", ARRAYSIZE( menu ), menu ); + } + } + } //========================================================= @@ -576,6 +725,10 @@ BOOL CHalfLifeMultiplay::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity //========================================================= void CHalfLifeMultiplay::PlayerThink( CBasePlayer *pPlayer ) { + if( !mp_coop.value && pPlayer->m_state == STATE_SPECTATOR_BEGIN ) + if( pPlayer->m_afButtonPressed & ( IN_DUCK | IN_ATTACK | IN_ATTACK2 | IN_USE | IN_JUMP ) ) + SpawnPlayer( pPlayer ); + if( g_fGameOver ) { // check for button presses @@ -596,6 +749,13 @@ void CHalfLifeMultiplay::PlayerSpawn( CBasePlayer *pPlayer ) BOOL addDefault; CBaseEntity *pWeaponEntity = NULL; + if( pPlayer->m_state == STATE_CONNECTED ) + { + pPlayer->m_state = STATE_SPECTATOR_BEGIN; + BecomeSpectator( pPlayer ); + return; + } + pPlayer->pev->weapons |= ( 1 << WEAPON_SUIT ); addDefault = TRUE; diff --git a/dlls/player.cpp b/dlls/player.cpp index 6bed4bf7..cbf207cb 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -833,6 +833,9 @@ void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib ) { CSound *pSound; + if( pev->flags & FL_SPECTATOR ) + return; + // Holster weapon immediately, to allow it to cleanup if( m_pActiveItem ) m_pActiveItem->Holster(); @@ -3523,6 +3526,8 @@ int CBasePlayer::AddPlayerItem( CBasePlayerItem *pItem ) { CBasePlayerItem *pInsert; + if( pev->flags & FL_SPECTATOR ) + return FALSE; pInsert = m_rgpPlayerItems[pItem->iItemSlot()]; while( pInsert ) diff --git a/dlls/player.h b/dlls/player.h index 036f9d02..5aad4a41 100644 --- a/dlls/player.h +++ b/dlls/player.h @@ -83,6 +83,15 @@ enum sbar_data #define CHAT_INTERVAL 1.0f +enum PlayerState +{ + STATE_CONNECTED = 0, + STATE_SPECTATOR_BEGIN, + STATE_SPAWNED, + STATE_SPECTATOR, + STATE_POINT_SELECT +}; + class CBasePlayer : public CBaseMonster { public: @@ -201,9 +210,9 @@ public: virtual void StartSneaking( void ) { m_tSneaking = gpGlobals->time - 1; } virtual void StopSneaking( void ) { m_tSneaking = gpGlobals->time + 30; } virtual BOOL IsSneaking( void ) { return m_tSneaking <= gpGlobals->time; } - virtual BOOL IsAlive( void ) { return (pev->deadflag == DEAD_NO) && pev->health > 0; } + virtual BOOL IsAlive( void ) { return (pev->deadflag == DEAD_NO) && pev->health > 0 || ( pev->flags & FL_SPECTATOR ); } virtual BOOL ShouldFadeOnDeath( void ) { return FALSE; } - virtual BOOL IsPlayer( void ) { return TRUE; } // Spectators should return FALSE for this, they aren't "players" as far as game logic is concerned + virtual BOOL IsPlayer( void ) { return !( pev->flags & FL_SPECTATOR ); } // Spectators should return FALSE for this, they aren't "players" as far as game logic is concerned virtual BOOL IsNetClient( void ) { return TRUE; } // Bots should return FALSE for this, they can't receive NET messages // Spectators should return TRUE for this @@ -311,6 +320,7 @@ public: virtual float TouchGravGun( CBaseEntity *attacker, int stage ); float m_flSpawnTime; + PlayerState m_state; }; #define AUTOAIM_2DEGREES 0.0348994967025