/* * TVM_ClientBegin * * called when a client has finished connecting, and is ready * to be placed into the game. This will happen every level load. */ void TVM_ClientBegin( tvm_relay_t *relay, edict_t *ent ) { edict_t *spot, *other; int i, specs; char hostname[MAX_CONFIGSTRING_CHARS]; assert( ent && ent->local && ent->r.client ); //TVM_Printf( "Begin: %s\n", ent->r.client->pers.netname ); ent->r.client->pers.connecting = false; spot = TVM_SelectSpawnPoint( ent ); if( spot ) { VectorCopy( spot->s.origin, ent->s.origin ); VectorCopy( spot->s.origin, ent->s.old_origin ); VectorCopy( spot->s.angles, ent->s.angles ); VectorCopy( spot->s.origin, ent->r.client->ps.pmove.origin ); VectorCopy( spot->s.angles, ent->r.client->ps.viewangles ); } else { VectorClear( ent->s.origin ); VectorClear( ent->s.old_origin ); VectorClear( ent->s.angles ); VectorClear( ent->r.client->ps.pmove.origin ); VectorClear( ent->r.client->ps.viewangles ); } ent->s.teleported = true; // set the delta angle for( i = 0; i < 3; i++ ) ent->r.client->ps.pmove.delta_angles[i] = ANGLE2SHORT( ent->s.angles[i] ) - ent->r.client->pers.cmd_angles[i]; specs = 0; for( i = 0; i < relay->local_maxclients; i++ ) { other = relay->local_edicts + i; if( other == ent ) continue; if( !other->r.inuse || !other->r.client ) continue; if( trap_GetClientState( relay, PLAYERNUM( other ) ) != CS_SPAWNED ) continue; specs++; } Q_strncpyz( hostname, relay->configStrings[CS_HOSTNAME], sizeof( hostname ) ); TVM_PrintMsg( relay, ent, S_COLOR_ORANGE "Welcome to %s! There %s currently %i spectator%s on this channel.\n", COM_RemoveColorTokens( hostname ), (specs == 1 ? "is" : "are"), specs, (specs == 1 ? "" : "s") ); TVM_PrintMsg( relay, ent, S_COLOR_ORANGE "For more information about chase camera modes type 'chase help' at console.\n" ); if( ent->r.client->chase.active ) TVM_ChaseClientEndSnapFrame( ent ); else TVM_ClientEndSnapFrame( ent ); }
/* * TVM_Cmd_PlayersExt_f */ static void TVM_Cmd_PlayersExt_f( edict_t *ent, bool onlyspecs ) { int i; int count = 0; int start = 0; char line[64]; char msg[1024]; edict_t *e; tvm_relay_t *relay = ent->relay; if( trap_Cmd_Argc() > 1 ) { start = atoi( trap_Cmd_Argv( 1 ) ); } clamp( start, 0, relay->maxclients - 1 ); // print information msg[0] = 0; for( i = start + 1; PLAYERNUM( ( relay->edicts + i ) ) < relay->maxclients; i++ ) { e = relay->edicts + i; if( e->r.inuse && !e->local && e->r.client ) { char *userinfo; userinfo = relay->configStrings[CS_PLAYERINFOS + PLAYERNUM( e )]; Q_snprintfz( line, sizeof( line ), "%3i %s\n", i, Info_ValueForKey( userinfo, "name" ) ); if( strlen( line ) + strlen( msg ) > sizeof( msg ) - 100 ) { // can't print all of them in one packet Q_strncatz( msg, "...\n", sizeof( msg ) ); break; } if( count == 0 ) { Q_strncatz( msg, "num name\n", sizeof( msg ) ); Q_strncatz( msg, "--- ---------------\n", sizeof( msg ) ); } Q_strncatz( msg, line, sizeof( msg ) ); count++; } } if( count ) { Q_strncatz( msg, "--- ---------------\n", sizeof( msg ) ); } Q_strncatz( msg, va( "%3i %s\n", count, trap_Cmd_Argv( 0 ) ), sizeof( msg ) ); TVM_PrintMsg( relay, ent, "%s", msg ); if( i < relay->maxclients ) { TVM_PrintMsg( relay, ent, "Type '%s %i' for more %s\n", trap_Cmd_Argv( 0 ), i, trap_Cmd_Argv( 0 ) ); } }
/* * TVM_Cmd_ChaseCam */ void TVM_Cmd_ChaseCam( edict_t *ent ) { const char *arg1; assert( ent && ent->local && ent->r.client ); // & 1 = scorelead // & 2 = powerups // & 4 = objectives // & 8 = fragger arg1 = trap_Cmd_Argv( 1 ); if( trap_Cmd_Argc() < 2 ) { TVM_ChasePlayer( ent, NULL, 0 ); } else if( !Q_stricmp( arg1, "auto" ) ) { TVM_PrintMsg( ent->relay, ent, "Chasecam mode is 'auto'. It will follow the score leader when no powerup nor flag is carried.\n" ); TVM_ChasePlayer( ent, NULL, 7 ); } else if( !Q_stricmp( arg1, "carriers" ) ) { TVM_PrintMsg( ent->relay, ent, "Chasecam mode is 'carriers'. It will switch to flag or powerup carriers when any of these items is picked up.\n" ); TVM_ChasePlayer( ent, NULL, 6 ); } else if( !Q_stricmp( arg1, "powerups" ) ) { TVM_PrintMsg( ent->relay, ent, "Chasecam mode is 'powerups'. It will switch to powerup carriers when any of these items is picked up.\n" ); TVM_ChasePlayer( ent, NULL, 2 ); } else if( !Q_stricmp( arg1, "objectives" ) ) { TVM_PrintMsg( ent->relay, ent, "Chasecam mode is 'objectives'. It will switch to flag carriers when any of these items is picked up.\n" ); TVM_ChasePlayer( ent, NULL, 4 ); } else if( !Q_stricmp( arg1, "score" ) ) { TVM_PrintMsg( ent->relay, ent, "Chasecam mode is 'score'. It will always follow the highest fragger.\n" ); TVM_ChasePlayer( ent, NULL, 1 ); } else if( !Q_stricmp( arg1, "fragger" ) ) { TVM_PrintMsg( ent->relay, ent, "Chasecam mode is 'fragger'. The last fragging player will be followed.\n" ); TVM_ChasePlayer( ent, NULL, 8 ); } else if( !Q_stricmp( arg1, "help" ) ) { TVM_PrintMsg( ent->relay, ent, "Chasecam modes:\n" ); TVM_PrintMsg( ent->relay, ent, "- 'auto': Chase the score leader unless there's an objective carrier or a powerup carrier.\n" ); TVM_PrintMsg( ent->relay, ent, "- 'carriers': User has pov control unless there's an objective carrier or a powerup carrier.\n" ); TVM_PrintMsg( ent->relay, ent, "- 'objectives': User has pov control unless there's a objective carrier.\n" ); TVM_PrintMsg( ent->relay, ent, "- 'powerups': User has pov control unless there's a powerup carrier.\n" ); TVM_PrintMsg( ent->relay, ent, "- 'score': Always follow the score leader. User has no pov control.\n" ); TVM_PrintMsg( ent->relay, ent, "- 'none': Disable chasecam.\n" ); } else { TVM_ChasePlayer( ent, trap_Cmd_Argv( 1 ), 0 ); } }
void TVM_ChasePlayer( edict_t *ent, char *name, int followmode ) { int i; edict_t *e; gclient_t *client; int targetNum = -1; int oldTarget; bool can_follow = true; char colorlessname[MAX_NAME_BYTES]; client = ent->r.client; oldTarget = client->chase.target; if( oldTarget < 0 ) oldTarget = 0; if( !can_follow && followmode ) { TVM_PrintMsg( ent->relay, ent, "Chasecam follow mode unavailable\n" ); followmode = false; } if( ent->r.client->chase.followmode && !followmode ) TVM_PrintMsg( ent->relay, ent, "Disabling chasecam follow mode\n" ); // always disable chasing as a start memset( &client->chase, 0, sizeof( chasecam_t ) ); // locate the requested target if( name && name[0] ) { // find it by player names for( e = ent->relay->edicts + 1; PLAYERNUM( e ) < ent->relay->maxclients; e++ ) { if( !TVM_Chase_IsValidTarget( ent, e ) ) continue; Q_strncpyz( colorlessname, COM_RemoveColorTokens( e->r.client->pers.netname ), sizeof(colorlessname) ); if( !Q_stricmp( COM_RemoveColorTokens( name ), colorlessname ) ) { targetNum = PLAYERNUM( e ); break; } } // didn't find it by name, try by numbers if( targetNum == -1 ) { i = atoi( name ); if( i >= 0 && i < ent->relay->maxclients ) { e = ent->relay->edicts + 1 + i; if( TVM_Chase_IsValidTarget( ent, e ) ) targetNum = PLAYERNUM( e ); } } if( targetNum == -1 ) TVM_PrintMsg( ent->relay, ent, "Requested chasecam target is not available\n" ); } // try to reuse old target if we didn't find a valid one if( targetNum == -1 && oldTarget > 0 && oldTarget <= ent->relay->maxclients ) { e = ent->relay->edicts + 1 + oldTarget; if( TVM_Chase_IsValidTarget( ent, e ) ) targetNum = PLAYERNUM( e ); } // if we still don't have a target, just pick the first valid one if( targetNum == -1 ) { for( e = ent->relay->edicts + 1; PLAYERNUM( e ) < ent->relay->maxclients; e++ ) { if( !TVM_Chase_IsValidTarget( ent, e ) ) continue; targetNum = PLAYERNUM( e ); break; } } // make the client a ghost TVM_GhostClient( ent ); if( targetNum != -1 ) { // we found a target, set up the chasecam client->chase.target = targetNum + 1; client->chase.followmode = followmode; TVM_Chase_SetChaseActive( ent, true ); } else { // stay as observer ent->movetype = MOVETYPE_NOCLIP; TVM_SpectatorMode( ent ); TVM_CenterPrintMsg( ent->relay, ent, "No one to chase" ); } }