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 }