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