/* * G_RunGametype */ void G_RunGametype( void ) { G_Teams_ExecuteChallengersQueue(); G_Teams_UpdateMembersList(); G_Match_CheckStateAbort(); G_UpdateScoreBoardMessages(); //check gametype specific rules if( game.asEngine != NULL ) GT_asCallThinkRules(); else G_Gametype_GENERIC_ThinkRules(); if( G_EachNewSecond() ) { G_CheckNumBots(); G_TickOutPowerUps(); } if( G_EachNewMinute() ) G_CheckEvenTeam(); G_Match_ScoreAnnouncement(); G_Match_ReadyAnnouncement(); if( GS_TeamBasedGametype() ) G_Teams_UpdateTeamInfoMessages(); G_asGarbageCollect( false ); }
/* * G_Teams_AdvanceChallengersQueue */ void G_Teams_AdvanceChallengersQueue( void ) { int i, team, loserscount, winnerscount, playerscount = 0; int maxscore = 999999; edict_t *won, *e; int START_TEAM = TEAM_PLAYERS, END_TEAM = TEAM_PLAYERS+1; if( !GS_HasChallengers() ) return; G_Teams_UpdateMembersList(); if( GS_TeamBasedGametype() ) { START_TEAM = TEAM_ALPHA; END_TEAM = GS_MAX_TEAMS; } // assign new timestamps to all the players inside teams for( team = START_TEAM; team < END_TEAM; team++ ) { playerscount += teamlist[team].numplayers; } if( !playerscount ) return; loserscount = 0; if( playerscount > 1 ) { loserscount = (int)( playerscount / 2 ); } winnerscount = playerscount - loserscount; // put everyone who just played out of the challengers queue for( team = START_TEAM; team < END_TEAM; team++ ) { for( i = 0; i < teamlist[team].numplayers; i++ ) { e = game.edicts + teamlist[team].playerIndices[i]; e->r.client->queueTimeStamp = 0; } } // put (back) the best scoring players in first positions of challengers queue for( i = 0; i < winnerscount; i++ ) { won = G_Teams_BestScoreBelow( maxscore ); if( won ) { maxscore = won->r.client->level.stats.score; won->r.client->queueTimeStamp = 1 + ( winnerscount-i ); // never have 2 players with the same timestamp } } }
/* * G_Teams_JoinTeam - checks that client can join the given team and then joins it */ bool G_Teams_JoinTeam( edict_t *ent, int team ) { int error; G_Teams_UpdateMembersList(); // make sure we have up-to-date data if( !ent->r.client ) return false; if( ( error = G_GameTypes_DenyJoinTeam( ent, team ) ) ) { if( error == ER_TEAM_INVALID ) { G_PrintMsg( ent, "Can't join %s in %s\n", GS_TeamName( team ), gs.gametypeName ); } else if( error == ER_TEAM_CHALLENGERS ) { G_Teams_JoinChallengersQueue( ent ); } else if( error == ER_TEAM_FULL ) { G_PrintMsg( ent, "Team %s is FULL\n", GS_TeamName( team ) ); G_Teams_JoinChallengersQueue( ent ); } else if( error == ER_TEAM_LOCKED ) { G_PrintMsg( ent, "Team %s is LOCKED\n", GS_TeamName( team ) ); G_Teams_JoinChallengersQueue( ent ); } else if( error == ER_TEAM_MATCHSTATE ) { G_PrintMsg( ent, "Can't join %s at this moment\n", GS_TeamName( team ) ); } else if( error == ER_TEAM_UNEVEN ) { G_PrintMsg( ent, "Can't join %s because of uneven teams\n", GS_TeamName( team ) ); // FIXME: need more suitable message :P G_Teams_JoinChallengersQueue( ent ); } return false; } //ok, can join, proceed G_Teams_SetTeam( ent, team ); return true; }
/* * G_Teams_JoinAnyTeam - find us a team since we are too lazy to do ourselves */ bool G_Teams_JoinAnyTeam( edict_t *ent, bool silent ) { int best_numplayers = gs.maxclients + 1, best_score = 999999; int i, team = -1; bool wasinqueue = ( ent->r.client->queueTimeStamp != 0 ); G_Teams_UpdateMembersList(); // make sure we have up-to-date data //depending on the gametype, of course if( !GS_TeamBasedGametype() ) { if( ent->s.team == TEAM_PLAYERS ) { if( !silent ) { G_PrintMsg( ent, "You are already in %s team\n", GS_TeamName( TEAM_PLAYERS ) ); } return false; } if( G_Teams_JoinTeam( ent, TEAM_PLAYERS ) ) { if( !silent ) { G_PrintMsg( NULL, "%s%s joined the %s team.\n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); } } return true; } else { //team based //find the available team with smaller player count or worse score for( i = TEAM_ALPHA; i < GS_MAX_TEAMS; i++ ) { if( G_GameTypes_DenyJoinTeam( ent, i ) ) { continue; } if( team == -1 || teamlist[i].numplayers < best_numplayers || ( teamlist[i].numplayers == best_numplayers && teamlist[i].stats.score < best_score ) ) { best_numplayers = teamlist[i].numplayers; best_score = teamlist[i].stats.score; team = i; } } if( team == ent->s.team ) { // he is at the right team if( !silent ) { G_PrintMsg( ent, "%sCouldn't find a better team than team %s.\n", S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); } return false; } if( team != -1 ) { if( G_Teams_JoinTeam( ent, team ) ) { if( !silent ) { G_PrintMsg( NULL, "%s%s joined the %s team.\n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); } return true; } } if( GS_MatchState() <= MATCH_STATE_PLAYTIME && !silent ) G_Teams_JoinChallengersQueue( ent ); } // don't print message if we joined the queue if( !silent && ( !GS_HasChallengers() || wasinqueue || !ent->r.client->queueTimeStamp ) ) G_PrintMsg( ent, "You can't join the game now\n" ); return false; }
/* * G_ClientRespawn */ void G_ClientRespawn( edict_t *self, bool ghost ) { int i; edict_t *spawnpoint; vec3_t hull_mins, hull_maxs; vec3_t spawn_origin, spawn_angles; gclient_t *client; int old_team; G_DeathAwards( self ); G_SpawnQueue_RemoveClient( self ); self->r.svflags &= ~SVF_NOCLIENT; //if invalid be spectator if( self->r.client->team < 0 || self->r.client->team >= GS_MAX_TEAMS ) self->r.client->team = TEAM_SPECTATOR; // force ghost always to true when in spectator team if( self->r.client->team == TEAM_SPECTATOR ) ghost = true; old_team = self->s.team; if( self->r.client->teamstate.is_coach ) ghost = true; GClip_UnlinkEntity( self ); client = self->r.client; memset( &client->resp, 0, sizeof( client->resp ) ); memset( &client->ps, 0, sizeof( client->ps ) ); client->resp.timeStamp = level.time; client->ps.playerNum = PLAYERNUM( self ); // clear entity values memset( &self->snap, 0, sizeof( self->snap ) ); memset( &self->s, 0, sizeof( self->s ) ); memset( &self->olds, 0, sizeof( self->olds ) ); memset( &self->invpak, 0, sizeof( self->invpak ) ); self->s.number = self->olds.number = ENTNUM( self ); // relink client struct self->r.client = &game.clients[PLAYERNUM( self )]; // update team self->s.team = client->team; self->deadflag = DEAD_NO; self->s.type = ET_PLAYER; self->groundentity = NULL; self->takedamage = DAMAGE_AIM; self->think = player_think; self->pain = player_pain; self->die = player_die; self->viewheight = playerbox_stand_viewheight; self->r.inuse = true; self->mass = PLAYER_MASS; self->air_finished = level.time + ( 12 * 1000 ); self->r.clipmask = MASK_PLAYERSOLID; self->waterlevel = 0; self->watertype = 0; self->flags &= ~FL_NO_KNOCKBACK; self->r.svflags &= ~SVF_CORPSE; self->enemy = NULL; self->r.owner = NULL; self->max_health = 100; self->health = self->max_health; if( AI_GetType( self->ai ) == AI_ISBOT ) { self->think = NULL; self->classname = "bot"; } else if( self->r.svflags & SVF_FAKECLIENT ) self->classname = "fakeclient"; else self->classname = "player"; VectorCopy( playerbox_stand_mins, self->r.mins ); VectorCopy( playerbox_stand_maxs, self->r.maxs ); VectorClear( self->velocity ); VectorClear( self->avelocity ); VectorCopy( self->r.mins, hull_mins ); VectorCopy( self->r.maxs, hull_maxs ); trap_CM_RoundUpToHullSize( hull_mins, hull_maxs, NULL ); if( self->r.maxs[2] > hull_maxs[2] ) self->viewheight -= (self->r.maxs[2] - hull_maxs[2]); client->ps.POVnum = ENTNUM( self ); // set movement info client->ps.pmove.stats[PM_STAT_MAXSPEED] = (short)DEFAULT_PLAYERSPEED; client->ps.pmove.stats[PM_STAT_JUMPSPEED] = (short)DEFAULT_JUMPSPEED; client->ps.pmove.stats[PM_STAT_DASHSPEED] = (short)DEFAULT_DASHSPEED; if( ghost ) { self->r.solid = SOLID_NOT; self->movetype = MOVETYPE_NOCLIP; if( self->s.team == TEAM_SPECTATOR ) self->r.svflags |= SVF_NOCLIENT; } else { self->r.client->resp.takeStun = true; self->r.solid = SOLID_YES; self->movetype = MOVETYPE_PLAYER; client->ps.pmove.stats[PM_STAT_FEATURES] = static_cast<unsigned short>(PMFEAT_DEFAULT); if( !g_allow_bunny->integer ) client->ps.pmove.stats[PM_STAT_FEATURES] &= ~( PMFEAT_AIRCONTROL|PMFEAT_FWDBUNNY ); } ClientUserinfoChanged( self, client->userinfo ); if( old_team != self->s.team ) G_Teams_UpdateMembersList(); SelectSpawnPoint( self, &spawnpoint, spawn_origin, spawn_angles ); VectorCopy( spawn_origin, client->ps.pmove.origin ); VectorCopy( spawn_origin, self->s.origin ); VectorCopy( self->s.origin, self->s.old_origin ); // set angles self->s.angles[PITCH] = 0; self->s.angles[YAW] = anglemod( spawn_angles[YAW] ); self->s.angles[ROLL] = 0; VectorCopy( self->s.angles, client->ps.viewangles ); // set the delta angle for( i = 0; i < 3; i++ ) client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i]; // don't put spectators in the game if( !ghost ) { if( KillBox( self ) ) { } } self->s.attenuation = ATTN_NORM; self->s.teleported = true; // hold in place briefly client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; client->ps.pmove.pm_time = 14; client->ps.pmove.stats[PM_STAT_NOUSERCONTROL] = CLIENT_RESPAWN_FREEZE_DELAY; client->ps.pmove.stats[PM_STAT_NOAUTOATTACK] = 1000; // set race stats to invisible client->ps.stats[STAT_TIME_SELF] = STAT_NOTSET; client->ps.stats[STAT_TIME_BEST] = STAT_NOTSET; client->ps.stats[STAT_TIME_RECORD] = STAT_NOTSET; client->ps.stats[STAT_TIME_ALPHA] = STAT_NOTSET; client->ps.stats[STAT_TIME_BETA] = STAT_NOTSET; BOT_Respawn( self ); self->r.client->level.respawnCount++; G_UseTargets( spawnpoint, self ); GClip_LinkEntity( self ); // let the gametypes perform their changes if( game.asEngine != NULL ) GT_asCallPlayerRespawn( self, old_team, self->s.team ); else G_Gametype_GENERIC_ClientRespawn( self, old_team, self->s.team ); }