Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

g_arenas.c (11344B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 //
     24 // g_arenas.c
     25 //
     26 
     27 #include "g_local.h"
     28 
     29 
     30 gentity_t	*podium1;
     31 gentity_t	*podium2;
     32 gentity_t	*podium3;
     33 
     34 
     35 /*
     36 ==================
     37 UpdateTournamentInfo
     38 ==================
     39 */
     40 void UpdateTournamentInfo( void ) {
     41 	int			i;
     42 	gentity_t	*player;
     43 	int			playerClientNum;
     44 	int			n, accuracy, perfect,	msglen;
     45 	int			buflen;
     46 #ifdef MISSIONPACK // bk001205
     47   int score1, score2;
     48 	qboolean won;
     49 #endif
     50 	char		buf[32];
     51 	char		msg[MAX_STRING_CHARS];
     52 
     53 	// find the real player
     54 	player = NULL;
     55 	for (i = 0; i < level.maxclients; i++ ) {
     56 		player = &g_entities[i];
     57 		if ( !player->inuse ) {
     58 			continue;
     59 		}
     60 		if ( !( player->r.svFlags & SVF_BOT ) ) {
     61 			break;
     62 		}
     63 	}
     64 	// this should never happen!
     65 	if ( !player || i == level.maxclients ) {
     66 		return;
     67 	}
     68 	playerClientNum = i;
     69 
     70 	CalculateRanks();
     71 
     72 	if ( level.clients[playerClientNum].sess.sessionTeam == TEAM_SPECTATOR ) {
     73 #ifdef MISSIONPACK
     74 		Com_sprintf( msg, sizeof(msg), "postgame %i %i 0 0 0 0 0 0 0 0 0 0 0", level.numNonSpectatorClients, playerClientNum );
     75 #else
     76 		Com_sprintf( msg, sizeof(msg), "postgame %i %i 0 0 0 0 0 0", level.numNonSpectatorClients, playerClientNum );
     77 #endif
     78 	}
     79 	else {
     80 		if( player->client->accuracy_shots ) {
     81 			accuracy = player->client->accuracy_hits * 100 / player->client->accuracy_shots;
     82 		}
     83 		else {
     84 			accuracy = 0;
     85 		}
     86 #ifdef MISSIONPACK
     87 		won = qfalse;
     88 		if (g_gametype.integer >= GT_CTF) {
     89 			score1 = level.teamScores[TEAM_RED];
     90 			score2 = level.teamScores[TEAM_BLUE];
     91 			if (level.clients[playerClientNum].sess.sessionTeam	== TEAM_RED) {
     92 				won = (level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE]);
     93 			} else {
     94 				won = (level.teamScores[TEAM_BLUE] > level.teamScores[TEAM_RED]);
     95 			}
     96 		} else {
     97 			if (&level.clients[playerClientNum] == &level.clients[ level.sortedClients[0] ]) {
     98 				won = qtrue;
     99 				score1 = level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE];
    100 				score2 = level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE];
    101 			} else {
    102 				score2 = level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE];
    103 				score1 = level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE];
    104 			}
    105 		}
    106 		if (won && player->client->ps.persistant[PERS_KILLED] == 0) {
    107 			perfect = 1;
    108 		} else {
    109 			perfect = 0;
    110 		}
    111 		Com_sprintf( msg, sizeof(msg), "postgame %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.numNonSpectatorClients, playerClientNum, accuracy,
    112 			player->client->ps.persistant[PERS_IMPRESSIVE_COUNT], player->client->ps.persistant[PERS_EXCELLENT_COUNT],player->client->ps.persistant[PERS_DEFEND_COUNT],
    113 			player->client->ps.persistant[PERS_ASSIST_COUNT], player->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], player->client->ps.persistant[PERS_SCORE],
    114 			perfect, score1, score2, level.time, player->client->ps.persistant[PERS_CAPTURES] );
    115 
    116 #else
    117 		perfect = ( level.clients[playerClientNum].ps.persistant[PERS_RANK] == 0 && player->client->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0;
    118 		Com_sprintf( msg, sizeof(msg), "postgame %i %i %i %i %i %i %i %i", level.numNonSpectatorClients, playerClientNum, accuracy,
    119 			player->client->ps.persistant[PERS_IMPRESSIVE_COUNT], player->client->ps.persistant[PERS_EXCELLENT_COUNT],
    120 			player->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], player->client->ps.persistant[PERS_SCORE],
    121 			perfect );
    122 #endif
    123 	}
    124 
    125 	msglen = strlen( msg );
    126 	for( i = 0; i < level.numNonSpectatorClients; i++ ) {
    127 		n = level.sortedClients[i];
    128 		Com_sprintf( buf, sizeof(buf), " %i %i %i", n, level.clients[n].ps.persistant[PERS_RANK], level.clients[n].ps.persistant[PERS_SCORE] );
    129 		buflen = strlen( buf );
    130 		if( msglen + buflen + 1 >= sizeof(msg) ) {
    131 			break;
    132 		}
    133 		strcat( msg, buf );
    134 	}
    135 	trap_SendConsoleCommand( EXEC_APPEND, msg );
    136 }
    137 
    138 
    139 static gentity_t *SpawnModelOnVictoryPad( gentity_t *pad, vec3_t offset, gentity_t *ent, int place ) {
    140 	gentity_t	*body;
    141 	vec3_t		vec;
    142 	vec3_t		f, r, u;
    143 
    144 	body = G_Spawn();
    145 	if ( !body ) {
    146 		G_Printf( S_COLOR_RED "ERROR: out of gentities\n" );
    147 		return NULL;
    148 	}
    149 
    150 	body->classname = ent->client->pers.netname;
    151 	body->client = ent->client;
    152 	body->s = ent->s;
    153 	body->s.eType = ET_PLAYER;		// could be ET_INVISIBLE
    154 	body->s.eFlags = 0;				// clear EF_TALK, etc
    155 	body->s.powerups = 0;			// clear powerups
    156 	body->s.loopSound = 0;			// clear lava burning
    157 	body->s.number = body - g_entities;
    158 	body->timestamp = level.time;
    159 	body->physicsObject = qtrue;
    160 	body->physicsBounce = 0;		// don't bounce
    161 	body->s.event = 0;
    162 	body->s.pos.trType = TR_STATIONARY;
    163 	body->s.groundEntityNum = ENTITYNUM_WORLD;
    164 	body->s.legsAnim = LEGS_IDLE;
    165 	body->s.torsoAnim = TORSO_STAND;
    166 	if( body->s.weapon == WP_NONE ) {
    167 		body->s.weapon = WP_MACHINEGUN;
    168 	}
    169 	if( body->s.weapon == WP_GAUNTLET) {
    170 		body->s.torsoAnim = TORSO_STAND2;
    171 	}
    172 	body->s.event = 0;
    173 	body->r.svFlags = ent->r.svFlags;
    174 	VectorCopy (ent->r.mins, body->r.mins);
    175 	VectorCopy (ent->r.maxs, body->r.maxs);
    176 	VectorCopy (ent->r.absmin, body->r.absmin);
    177 	VectorCopy (ent->r.absmax, body->r.absmax);
    178 	body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
    179 	body->r.contents = CONTENTS_BODY;
    180 	body->r.ownerNum = ent->r.ownerNum;
    181 	body->takedamage = qfalse;
    182 
    183 	VectorSubtract( level.intermission_origin, pad->r.currentOrigin, vec );
    184 	vectoangles( vec, body->s.apos.trBase );
    185 	body->s.apos.trBase[PITCH] = 0;
    186 	body->s.apos.trBase[ROLL] = 0;
    187 
    188 	AngleVectors( body->s.apos.trBase, f, r, u );
    189 	VectorMA( pad->r.currentOrigin, offset[0], f, vec );
    190 	VectorMA( vec, offset[1], r, vec );
    191 	VectorMA( vec, offset[2], u, vec );
    192 
    193 	G_SetOrigin( body, vec );
    194 
    195 	trap_LinkEntity (body);
    196 
    197 	body->count = place;
    198 
    199 	return body;
    200 }
    201 
    202 
    203 static void CelebrateStop( gentity_t *player ) {
    204 	int		anim;
    205 
    206 	if( player->s.weapon == WP_GAUNTLET) {
    207 		anim = TORSO_STAND2;
    208 	}
    209 	else {
    210 		anim = TORSO_STAND;
    211 	}
    212 	player->s.torsoAnim = ( ( player->s.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
    213 }
    214 
    215 
    216 #define	TIMER_GESTURE	(34*66+50)
    217 static void CelebrateStart( gentity_t *player ) {
    218 	player->s.torsoAnim = ( ( player->s.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | TORSO_GESTURE;
    219 	player->nextthink = level.time + TIMER_GESTURE;
    220 	player->think = CelebrateStop;
    221 
    222 	/*
    223 	player->client->ps.events[player->client->ps.eventSequence & (MAX_PS_EVENTS-1)] = EV_TAUNT;
    224 	player->client->ps.eventParms[player->client->ps.eventSequence & (MAX_PS_EVENTS-1)] = 0;
    225 	player->client->ps.eventSequence++;
    226 	*/
    227 	G_AddEvent(player, EV_TAUNT, 0);
    228 }
    229 
    230 
    231 static vec3_t	offsetFirst  = {0, 0, 74};
    232 static vec3_t	offsetSecond = {-10, 60, 54};
    233 static vec3_t	offsetThird  = {-19, -60, 45};
    234 
    235 static void PodiumPlacementThink( gentity_t *podium ) {
    236 	vec3_t		vec;
    237 	vec3_t		origin;
    238 	vec3_t		f, r, u;
    239 
    240 	podium->nextthink = level.time + 100;
    241 
    242 	AngleVectors( level.intermission_angle, vec, NULL, NULL );
    243 	VectorMA( level.intermission_origin, trap_Cvar_VariableIntegerValue( "g_podiumDist" ), vec, origin );
    244 	origin[2] -= trap_Cvar_VariableIntegerValue( "g_podiumDrop" );
    245 	G_SetOrigin( podium, origin );
    246 
    247 	if( podium1 ) {
    248 		VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec );
    249 		vectoangles( vec, podium1->s.apos.trBase );
    250 		podium1->s.apos.trBase[PITCH] = 0;
    251 		podium1->s.apos.trBase[ROLL] = 0;
    252 
    253 		AngleVectors( podium1->s.apos.trBase, f, r, u );
    254 		VectorMA( podium->r.currentOrigin, offsetFirst[0], f, vec );
    255 		VectorMA( vec, offsetFirst[1], r, vec );
    256 		VectorMA( vec, offsetFirst[2], u, vec );
    257 
    258 		G_SetOrigin( podium1, vec );
    259 	}
    260 
    261 	if( podium2 ) {
    262 		VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec );
    263 		vectoangles( vec, podium2->s.apos.trBase );
    264 		podium2->s.apos.trBase[PITCH] = 0;
    265 		podium2->s.apos.trBase[ROLL] = 0;
    266 
    267 		AngleVectors( podium2->s.apos.trBase, f, r, u );
    268 		VectorMA( podium->r.currentOrigin, offsetSecond[0], f, vec );
    269 		VectorMA( vec, offsetSecond[1], r, vec );
    270 		VectorMA( vec, offsetSecond[2], u, vec );
    271 
    272 		G_SetOrigin( podium2, vec );
    273 	}
    274 
    275 	if( podium3 ) {
    276 		VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec );
    277 		vectoangles( vec, podium3->s.apos.trBase );
    278 		podium3->s.apos.trBase[PITCH] = 0;
    279 		podium3->s.apos.trBase[ROLL] = 0;
    280 
    281 		AngleVectors( podium3->s.apos.trBase, f, r, u );
    282 		VectorMA( podium->r.currentOrigin, offsetThird[0], f, vec );
    283 		VectorMA( vec, offsetThird[1], r, vec );
    284 		VectorMA( vec, offsetThird[2], u, vec );
    285 
    286 		G_SetOrigin( podium3, vec );
    287 	}
    288 }
    289 
    290 
    291 static gentity_t *SpawnPodium( void ) {
    292 	gentity_t	*podium;
    293 	vec3_t		vec;
    294 	vec3_t		origin;
    295 
    296 	podium = G_Spawn();
    297 	if ( !podium ) {
    298 		return NULL;
    299 	}
    300 
    301 	podium->classname = "podium";
    302 	podium->s.eType = ET_GENERAL;
    303 	podium->s.number = podium - g_entities;
    304 	podium->clipmask = CONTENTS_SOLID;
    305 	podium->r.contents = CONTENTS_SOLID;
    306 	podium->s.modelindex = G_ModelIndex( SP_PODIUM_MODEL );
    307 
    308 	AngleVectors( level.intermission_angle, vec, NULL, NULL );
    309 	VectorMA( level.intermission_origin, trap_Cvar_VariableIntegerValue( "g_podiumDist" ), vec, origin );
    310 	origin[2] -= trap_Cvar_VariableIntegerValue( "g_podiumDrop" );
    311 	G_SetOrigin( podium, origin );
    312 
    313 	VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec );
    314 	podium->s.apos.trBase[YAW] = vectoyaw( vec );
    315 	trap_LinkEntity (podium);
    316 
    317 	podium->think = PodiumPlacementThink;
    318 	podium->nextthink = level.time + 100;
    319 	return podium;
    320 }
    321 
    322 
    323 /*
    324 ==================
    325 SpawnModelsOnVictoryPads
    326 ==================
    327 */
    328 void SpawnModelsOnVictoryPads( void ) {
    329 	gentity_t	*player;
    330 	gentity_t	*podium;
    331 
    332 	podium1 = NULL;
    333 	podium2 = NULL;
    334 	podium3 = NULL;
    335 
    336 	podium = SpawnPodium();
    337 
    338 	player = SpawnModelOnVictoryPad( podium, offsetFirst, &g_entities[level.sortedClients[0]],
    339 				level.clients[ level.sortedClients[0] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG );
    340 	if ( player ) {
    341 		player->nextthink = level.time + 2000;
    342 		player->think = CelebrateStart;
    343 		podium1 = player;
    344 	}
    345 
    346 	player = SpawnModelOnVictoryPad( podium, offsetSecond, &g_entities[level.sortedClients[1]],
    347 				level.clients[ level.sortedClients[1] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG );
    348 	if ( player ) {
    349 		podium2 = player;
    350 	}
    351 
    352 	if ( level.numNonSpectatorClients > 2 ) {
    353 		player = SpawnModelOnVictoryPad( podium, offsetThird, &g_entities[level.sortedClients[2]],
    354 				level.clients[ level.sortedClients[2] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG );
    355 		if ( player ) {
    356 			podium3 = player;
    357 		}
    358 	}
    359 }
    360 
    361 
    362 /*
    363 ===============
    364 Svcmd_AbortPodium_f
    365 ===============
    366 */
    367 void Svcmd_AbortPodium_f( void ) {
    368 	if( g_gametype.integer != GT_SINGLE_PLAYER ) {
    369 		return;
    370 	}
    371 
    372 	if( podium1 ) {
    373 		podium1->nextthink = level.time;
    374 		podium1->think = CelebrateStop;
    375 	}
    376 }