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 Quake III Arena source code; 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 */
UpdateTournamentInfo(void)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
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
SpawnModelOnVictoryPad(gentity_t * pad,vec3_t offset,gentity_t * ent,int place)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
CelebrateStop(gentity_t * player)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)
CelebrateStart(gentity_t * player)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
PodiumPlacementThink(gentity_t * podium)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
SpawnPodium(void)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 */
SpawnModelsOnVictoryPads(void)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 */
Svcmd_AbortPodium_f(void)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 }
377