1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6 
7 This file is part of the OpenJK source code.
8 
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22 
23 #include "g_local.h"
24 
25 #define LOGGING_WEAPONS
26 
27 // Weapon statistic logging.
28 // Nothing super-fancy here, I just want to keep track of, per player:
29 //		--hom many times a weapon/item is picked up
30 //		--how many times a weapon/item is used/fired
31 //		--the total damage done by that weapon
32 //		--the number of kills by that weapon
33 //		--the number of deaths while holding that weapon
34 //		--the time spent with each weapon
35 //
36 // Additionally,
37 //		--how many times each powerup or item is picked up
38 
39 
40 #ifdef LOGGING_WEAPONS
41 int G_WeaponLogPickups[MAX_CLIENTS][WP_NUM_WEAPONS];
42 int G_WeaponLogFired[MAX_CLIENTS][WP_NUM_WEAPONS];
43 int G_WeaponLogDamage[MAX_CLIENTS][MOD_MAX];
44 int G_WeaponLogKills[MAX_CLIENTS][MOD_MAX];
45 int G_WeaponLogDeaths[MAX_CLIENTS][WP_NUM_WEAPONS];
46 int G_WeaponLogFrags[MAX_CLIENTS][MAX_CLIENTS];
47 int G_WeaponLogTime[MAX_CLIENTS][WP_NUM_WEAPONS];
48 int G_WeaponLogLastTime[MAX_CLIENTS];
49 qboolean G_WeaponLogClientTouch[MAX_CLIENTS];
50 int G_WeaponLogPowerups[MAX_CLIENTS][HI_NUM_HOLDABLE];
51 int	G_WeaponLogItems[MAX_CLIENTS][PW_NUM_POWERUPS];
52 
53 // MOD-weapon mapping array.
54 int weaponFromMOD[MOD_MAX] =
55 {
56 	WP_NONE,				//MOD_UNKNOWN,
57 	WP_STUN_BATON,			//MOD_STUN_BATON,
58 	WP_MELEE,				//MOD_MELEE,
59 	WP_SABER,				//MOD_SABER,
60 	WP_BRYAR_PISTOL,		//MOD_BRYAR_PISTOL,
61 	WP_BRYAR_PISTOL,		//MOD_BRYAR_PISTOL_ALT,
62 	WP_BLASTER,				//MOD_BLASTER,
63 	WP_TURRET,				//MOD_TURBLAST
64 	WP_DISRUPTOR,			//MOD_DISRUPTOR,
65 	WP_DISRUPTOR,			//MOD_DISRUPTOR_SPLASH,
66 	WP_DISRUPTOR,			//MOD_DISRUPTOR_SNIPER,
67 	WP_BOWCASTER,			//MOD_BOWCASTER,
68 	WP_REPEATER,			//MOD_REPEATER,
69 	WP_REPEATER,			//MOD_REPEATER_ALT,
70 	WP_REPEATER,			//MOD_REPEATER_ALT_SPLASH,
71 	WP_DEMP2,				//MOD_DEMP2,
72 	WP_DEMP2,				//MOD_DEMP2_ALT,
73 	WP_FLECHETTE,			//MOD_FLECHETTE,
74 	WP_FLECHETTE,			//MOD_FLECHETTE_ALT_SPLASH,
75 	WP_ROCKET_LAUNCHER,		//MOD_ROCKET,
76 	WP_ROCKET_LAUNCHER,		//MOD_ROCKET_SPLASH,
77 	WP_ROCKET_LAUNCHER,		//MOD_ROCKET_HOMING,
78 	WP_ROCKET_LAUNCHER,		//MOD_ROCKET_HOMING_SPLASH,
79 	WP_THERMAL,				//MOD_THERMAL,
80 	WP_THERMAL,				//MOD_THERMAL_SPLASH,
81 	WP_TRIP_MINE,			//MOD_TRIP_MINE_SPLASH,
82 	WP_TRIP_MINE,			//MOD_TIMED_MINE_SPLASH,
83 	WP_DET_PACK,			//MOD_DET_PACK_SPLASH,
84 	WP_NONE,				//MOD_FORCE_DARK,
85 	WP_NONE,				//MOD_SENTRY,
86 	WP_NONE,				//MOD_WATER,
87 	WP_NONE,				//MOD_SLIME,
88 	WP_NONE,				//MOD_LAVA,
89 	WP_NONE,				//MOD_CRUSH,
90 	WP_NONE,				//MOD_TELEFRAG,
91 	WP_NONE,				//MOD_FALLING,
92 	WP_NONE,				//MOD_SUICIDE,
93 	WP_NONE,				//MOD_TARGET_LASER,
94 	WP_NONE,				//MOD_TRIGGER_HURT,
95 };
96 
97 char *weaponNameFromIndex[WP_NUM_WEAPONS] =
98 {
99 	"No Weapon",
100 	"Stun Baton",
101 	"Saber",
102 	"Bryar Pistol",
103 	"Blaster",
104 	"Disruptor",
105 	"Bowcaster",
106 	"Repeater",
107 	"Demp2",
108 	"Flechette",
109 	"Rocket Launcher",
110 	"Thermal",
111 	"Tripmine",
112 	"Detpack",
113 	"Emplaced gun",
114 	"Turret"
115 };
116 
117 extern char	*modNames[];
118 
119 #endif //LOGGING_WEAPONS
120 
121 /*
122 =================
123 G_LogWeaponInit
124 =================
125 */
G_LogWeaponInit(void)126 void G_LogWeaponInit(void) {
127 #ifdef LOGGING_WEAPONS
128 	memset(G_WeaponLogPickups, 0, sizeof(G_WeaponLogPickups));
129 	memset(G_WeaponLogFired, 0, sizeof(G_WeaponLogFired));
130 	memset(G_WeaponLogDamage, 0, sizeof(G_WeaponLogDamage));
131 	memset(G_WeaponLogKills, 0, sizeof(G_WeaponLogKills));
132 	memset(G_WeaponLogDeaths, 0, sizeof(G_WeaponLogDeaths));
133 	memset(G_WeaponLogFrags, 0, sizeof(G_WeaponLogFrags));
134 	memset(G_WeaponLogTime, 0, sizeof(G_WeaponLogTime));
135 	memset(G_WeaponLogLastTime, 0, sizeof(G_WeaponLogLastTime));
136 	memset(G_WeaponLogPowerups, 0, sizeof(G_WeaponLogPowerups));
137 	memset(G_WeaponLogItems, 0, sizeof(G_WeaponLogItems));
138 #endif //LOGGING_WEAPONS
139 }
140 
G_LogWeaponPickup(int client,int weaponid)141 void QDECL G_LogWeaponPickup(int client, int weaponid)
142 {
143 #ifdef LOGGING_WEAPONS
144 	if (client>=MAX_CLIENTS)
145 		return;
146 
147 	G_WeaponLogPickups[client][weaponid]++;
148 	G_WeaponLogClientTouch[client] = qtrue;
149 #endif //_LOGGING_WEAPONS
150 }
151 
G_LogWeaponFire(int client,int weaponid)152 void QDECL G_LogWeaponFire(int client, int weaponid)
153 {
154 #ifdef LOGGING_WEAPONS
155 	int dur;
156 
157 	if (client>=MAX_CLIENTS)
158 		return;
159 
160 	G_WeaponLogFired[client][weaponid]++;
161 	dur = level.time - G_WeaponLogLastTime[client];
162 	if (dur > 5000)		// 5 second max.
163 		G_WeaponLogTime[client][weaponid] += 5000;
164 	else
165 		G_WeaponLogTime[client][weaponid] += dur;
166 	G_WeaponLogLastTime[client] = level.time;
167 	G_WeaponLogClientTouch[client] = qtrue;
168 #endif //_LOGGING_WEAPONS
169 }
170 
G_LogWeaponDamage(int client,int mod,int amount)171 void QDECL G_LogWeaponDamage(int client, int mod, int amount)
172 {
173 #ifdef LOGGING_WEAPONS
174 	if (client>=MAX_CLIENTS)
175 		return;
176 	G_WeaponLogDamage[client][mod] += amount;
177 	G_WeaponLogClientTouch[client] = qtrue;
178 #endif //_LOGGING_WEAPONS
179 }
180 
G_LogWeaponKill(int client,int mod)181 void QDECL G_LogWeaponKill(int client, int mod)
182 {
183 #ifdef LOGGING_WEAPONS
184 	if (client>=MAX_CLIENTS)
185 		return;
186 	G_WeaponLogKills[client][mod]++;
187 	G_WeaponLogClientTouch[client] = qtrue;
188 #endif //_LOGGING_WEAPONS
189 }
190 
G_LogWeaponFrag(int attacker,int deadguy)191 void QDECL G_LogWeaponFrag(int attacker, int deadguy)
192 {
193 #ifdef LOGGING_WEAPONS
194 	if ( (attacker>=MAX_CLIENTS) || (deadguy>=MAX_CLIENTS) )
195 		return;
196 	G_WeaponLogFrags[attacker][deadguy]++;
197 	G_WeaponLogClientTouch[attacker] = qtrue;
198 #endif //_LOGGING_WEAPONS
199 }
200 
G_LogWeaponDeath(int client,int weaponid)201 void QDECL G_LogWeaponDeath(int client, int weaponid)
202 {
203 #ifdef LOGGING_WEAPONS
204 	if (client>=MAX_CLIENTS)
205 		return;
206 	G_WeaponLogDeaths[client][weaponid]++;
207 	G_WeaponLogClientTouch[client] = qtrue;
208 #endif //_LOGGING_WEAPONS
209 }
210 
G_LogWeaponPowerup(int client,int powerupid)211 void QDECL G_LogWeaponPowerup(int client, int powerupid)
212 {
213 #ifdef LOGGING_WEAPONS
214 	if (client>=MAX_CLIENTS)
215 		return;
216 	G_WeaponLogPowerups[client][powerupid]++;
217 	G_WeaponLogClientTouch[client] = qtrue;
218 #endif //_LOGGING_WEAPONS
219 }
220 
G_LogWeaponItem(int client,int itemid)221 void QDECL G_LogWeaponItem(int client, int itemid)
222 {
223 #ifdef LOGGING_WEAPONS
224 	if (client>=MAX_CLIENTS)
225 		return;
226 	G_WeaponLogItems[client][itemid]++;
227 	G_WeaponLogClientTouch[client] = qtrue;
228 #endif //_LOGGING_WEAPONS
229 }
230 
231 
232 // Run through each player.  Print out:
233 //	-- Most commonly picked up weapon.
234 //  -- Weapon with which the most time was spent.
235 //  -- Weapon that was most often died with.
236 //  -- Damage type with which the most damage was done.
237 //  -- Damage type with the most kills.
238 //  -- Weapon with which the most damage was done.
239 //	-- Weapon with which the most damage was done per shot.
240 //
241 // For the whole game, print out:
242 //  -- Total pickups of each weapon.
243 //  -- Total time spent with each weapon.
244 //  -- Total damage done with each weapon.
245 //  -- Total damage done for each damage type.
246 //  -- Number of kills with each weapon.
247 //  -- Number of kills for each damage type.
248 //  -- Damage per shot with each weapon.
249 //  -- Number of deaths with each weapon.
250 
G_LogWeaponOutput(void)251 void G_LogWeaponOutput(void)
252 {
253 #ifdef LOGGING_WEAPONS
254 	int i,j,curwp;
255 	float pershot;
256 	fileHandle_t weaponfile;
257 	char string[1024];
258 
259 	int totalpickups[WP_NUM_WEAPONS];
260 	int totaltime[WP_NUM_WEAPONS];
261 	int totaldeaths[WP_NUM_WEAPONS];
262 	int totaldamageMOD[MOD_MAX];
263 	int totalkillsMOD[MOD_MAX];
264 	int totaldamage[WP_NUM_WEAPONS];
265 	int totalkills[WP_NUM_WEAPONS];
266 	int totalshots[WP_NUM_WEAPONS];
267 	int percharacter[WP_NUM_WEAPONS];
268 	char info[1024];
269 	char mapname[128];
270 	char *nameptr, *unknownname="<Unknown>";
271 
272 	if (!g_statLog.integer)
273 	{
274 		return;
275 	}
276 
277 	G_LogPrintf("*****************************Weapon Log:\n" );
278 
279 	memset(totalpickups, 0, sizeof(totalpickups));
280 	memset(totaltime, 0, sizeof(totaltime));
281 	memset(totaldeaths, 0, sizeof(totaldeaths));
282 	memset(totaldamageMOD, 0, sizeof(totaldamageMOD));
283 	memset(totalkillsMOD, 0, sizeof(totalkillsMOD));
284 	memset(totaldamage, 0, sizeof(totaldamage));
285 	memset(totalkills, 0, sizeof(totalkills));
286 	memset(totalshots, 0, sizeof(totalshots));
287 
288 	for (i=0; i<MAX_CLIENTS; i++)
289 	{
290 		if (G_WeaponLogClientTouch[i])
291 		{	// Ignore any entity/clients we don't care about!
292 			for (j=0;j<WP_NUM_WEAPONS;j++)
293 			{
294 				totalpickups[j] += G_WeaponLogPickups[i][j];
295 				totaltime[j] += G_WeaponLogTime[i][j];
296 				totaldeaths[j] += G_WeaponLogDeaths[i][j];
297 				totalshots[j] += G_WeaponLogFired[i][j];
298 			}
299 
300 			for (j=0;j<MOD_MAX;j++)
301 			{
302 				totaldamageMOD[j] += G_WeaponLogDamage[i][j];
303 				totalkillsMOD[j] += G_WeaponLogKills[i][j];
304 			}
305 		}
306 	}
307 
308 	// Now total the weapon data from the MOD data.
309 	for (j=0; j<MOD_MAX; j++)
310 	{
311 		if (j <= MOD_SENTRY)
312 		{
313 			curwp = weaponFromMOD[j];
314 			totaldamage[curwp] += totaldamageMOD[j];
315 			totalkills[curwp] += totalkillsMOD[j];
316 		}
317 	}
318 
319 	G_LogPrintf(  "\n****Data by Weapon:\n" );
320 	for (j=0; j<WP_NUM_WEAPONS; j++)
321 	{
322 		G_LogPrintf("%15s:  Pickups: %4d,  Time:  %5d,  Deaths: %5d\n",
323 				weaponNameFromIndex[j], totalpickups[j], (int)(totaltime[j]/1000), totaldeaths[j]);
324 	}
325 
326 	G_LogPrintf(  "\n****Combat Data by Weapon:\n" );
327 	for (j=0; j<WP_NUM_WEAPONS; j++)
328 	{
329 		if (totalshots[j] > 0)
330 		{
331 			pershot = (float)(totaldamage[j])/(float)(totalshots[j]);
332 		}
333 		else
334 		{
335 			pershot = 0;
336 		}
337 		G_LogPrintf("%15s:  Damage: %6d,  Kills: %5d,  Dmg per Shot: %f\n",
338 				weaponNameFromIndex[j], totaldamage[j], totalkills[j], pershot);
339 	}
340 
341 	G_LogPrintf(  "\n****Combat Data By Damage Type:\n" );
342 	for (j=0; j<MOD_MAX; j++)
343 	{
344 		G_LogPrintf("%25s:  Damage: %6d,  Kills: %5d\n",
345 				modNames[j], totaldamageMOD[j], totalkillsMOD[j]);
346 	}
347 
348 	G_LogPrintf("\n");
349 
350 	// Write the whole weapon statistic log out to a file.
351 	trap->FS_Open( g_statLogFile.string, &weaponfile, FS_APPEND );
352 	if (!weaponfile) {	//failed to open file, let's not crash, shall we?
353 		return;
354 	}
355 
356 	// Write out the level name
357 	trap->GetServerinfo(info, sizeof(info));
358 	Q_strncpyz(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname));
359 
360 	Com_sprintf(string, sizeof(string), "\n\n\nLevel:\t%s\n\n\n", mapname);
361 	trap->FS_Write( string, strlen( string ), weaponfile);
362 
363 
364 	// Combat data per character
365 
366 	// Start with Pickups per character
367 	Com_sprintf(string, sizeof(string), "Weapon Pickups per Player:\n\n");
368 	trap->FS_Write( string, strlen( string ), weaponfile);
369 
370 	Com_sprintf(string, sizeof(string), "Player");
371 	trap->FS_Write(string, strlen(string), weaponfile);
372 
373 	for (j=0; j<WP_NUM_WEAPONS; j++)
374 	{
375 		Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]);
376 		trap->FS_Write(string, strlen(string), weaponfile);
377 	}
378 	Com_sprintf(string, sizeof(string), "\n");
379 	trap->FS_Write(string, strlen(string), weaponfile);
380 
381 	// Cycle through each player, give their name and the number of times they picked up each weapon.
382 	for (i=0; i<MAX_CLIENTS; i++)
383 	{
384 		if (G_WeaponLogClientTouch[i])
385 		{	// Ignore any entity/clients we don't care about!
386 			if ( g_entities[i].client )
387 			{
388 				nameptr = g_entities[i].client->pers.netname;
389 			}
390 			else
391 			{
392 				nameptr = unknownname;
393 			}
394 			trap->FS_Write(nameptr, strlen(nameptr), weaponfile);
395 
396 			for (j=0;j<WP_NUM_WEAPONS;j++)
397 			{
398 				Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogPickups[i][j]);
399 				trap->FS_Write(string, strlen(string), weaponfile);
400 			}
401 
402 			Com_sprintf(string, sizeof(string), "\n");
403 			trap->FS_Write(string, strlen(string), weaponfile);
404 		}
405 	}
406 
407 	// Sum up the totals.
408 	Com_sprintf(string, sizeof(string), "\n***TOTAL:");
409 	trap->FS_Write(string, strlen(string), weaponfile);
410 
411 	for (j=0;j<WP_NUM_WEAPONS;j++)
412 	{
413 		Com_sprintf(string, sizeof(string), "\t%d", totalpickups[j]);
414 		trap->FS_Write(string, strlen(string), weaponfile);
415 	}
416 
417 	Com_sprintf(string, sizeof(string), "\n\n\n");
418 	trap->FS_Write(string, strlen(string), weaponfile);
419 
420 
421 	// Weapon fires per character
422 	Com_sprintf(string, sizeof(string), "Weapon Shots per Player:\n\n");
423 	trap->FS_Write( string, strlen( string ), weaponfile);
424 
425 	Com_sprintf(string, sizeof(string), "Player");
426 	trap->FS_Write(string, strlen(string), weaponfile);
427 
428 	for (j=0; j<WP_NUM_WEAPONS; j++)
429 	{
430 		Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]);
431 		trap->FS_Write(string, strlen(string), weaponfile);
432 	}
433 	Com_sprintf(string, sizeof(string), "\n");
434 	trap->FS_Write(string, strlen(string), weaponfile);
435 
436 	// Cycle through each player, give their name and the number of times they picked up each weapon.
437 	for (i=0; i<MAX_CLIENTS; i++)
438 	{
439 		if (G_WeaponLogClientTouch[i])
440 		{	// Ignore any entity/clients we don't care about!
441 			if ( g_entities[i].client )
442 			{
443 				nameptr = g_entities[i].client->pers.netname;
444 			}
445 			else
446 			{
447 				nameptr = unknownname;
448 			}
449 			trap->FS_Write(nameptr, strlen(nameptr), weaponfile);
450 
451 			for (j=0;j<WP_NUM_WEAPONS;j++)
452 			{
453 				Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogFired[i][j]);
454 				trap->FS_Write(string, strlen(string), weaponfile);
455 			}
456 
457 			Com_sprintf(string, sizeof(string), "\n");
458 			trap->FS_Write(string, strlen(string), weaponfile);
459 		}
460 	}
461 
462 	// Sum up the totals.
463 	Com_sprintf(string, sizeof(string), "\n***TOTAL:");
464 	trap->FS_Write(string, strlen(string), weaponfile);
465 
466 	for (j=0;j<WP_NUM_WEAPONS;j++)
467 	{
468 		Com_sprintf(string, sizeof(string), "\t%d", totalshots[j]);
469 		trap->FS_Write(string, strlen(string), weaponfile);
470 	}
471 
472 	Com_sprintf(string, sizeof(string), "\n\n\n");
473 	trap->FS_Write(string, strlen(string), weaponfile);
474 
475 
476 	// Weapon time per character
477 	Com_sprintf(string, sizeof(string), "Weapon Use Time per Player:\n\n");
478 	trap->FS_Write( string, strlen( string ), weaponfile);
479 
480 	Com_sprintf(string, sizeof(string), "Player");
481 	trap->FS_Write(string, strlen(string), weaponfile);
482 
483 	for (j=0; j<WP_NUM_WEAPONS; j++)
484 	{
485 		Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]);
486 		trap->FS_Write(string, strlen(string), weaponfile);
487 	}
488 	Com_sprintf(string, sizeof(string), "\n");
489 	trap->FS_Write(string, strlen(string), weaponfile);
490 
491 	// Cycle through each player, give their name and the number of times they picked up each weapon.
492 	for (i=0; i<MAX_CLIENTS; i++)
493 	{
494 		if (G_WeaponLogClientTouch[i])
495 		{	// Ignore any entity/clients we don't care about!
496 			if ( g_entities[i].client )
497 			{
498 				nameptr = g_entities[i].client->pers.netname;
499 			}
500 			else
501 			{
502 				nameptr = unknownname;
503 			}
504 			trap->FS_Write(nameptr, strlen(nameptr), weaponfile);
505 
506 			for (j=0;j<WP_NUM_WEAPONS;j++)
507 			{
508 				Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogTime[i][j]);
509 				trap->FS_Write(string, strlen(string), weaponfile);
510 			}
511 
512 			Com_sprintf(string, sizeof(string), "\n");
513 			trap->FS_Write(string, strlen(string), weaponfile);
514 		}
515 	}
516 
517 	// Sum up the totals.
518 	Com_sprintf(string, sizeof(string), "\n***TOTAL:");
519 	trap->FS_Write(string, strlen(string), weaponfile);
520 
521 	for (j=0;j<WP_NUM_WEAPONS;j++)
522 	{
523 		Com_sprintf(string, sizeof(string), "\t%d", totaltime[j]);
524 		trap->FS_Write(string, strlen(string), weaponfile);
525 	}
526 
527 	Com_sprintf(string, sizeof(string), "\n\n\n");
528 	trap->FS_Write(string, strlen(string), weaponfile);
529 
530 
531 
532 	// Weapon deaths per character
533 	Com_sprintf(string, sizeof(string), "Weapon Deaths per Player:\n\n");
534 	trap->FS_Write( string, strlen( string ), weaponfile);
535 
536 	Com_sprintf(string, sizeof(string), "Player");
537 	trap->FS_Write(string, strlen(string), weaponfile);
538 
539 	for (j=0; j<WP_NUM_WEAPONS; j++)
540 	{
541 		Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]);
542 		trap->FS_Write(string, strlen(string), weaponfile);
543 	}
544 	Com_sprintf(string, sizeof(string), "\n");
545 	trap->FS_Write(string, strlen(string), weaponfile);
546 
547 	// Cycle through each player, give their name and the number of times they picked up each weapon.
548 	for (i=0; i<MAX_CLIENTS; i++)
549 	{
550 		if (G_WeaponLogClientTouch[i])
551 		{	// Ignore any entity/clients we don't care about!
552 			if ( g_entities[i].client )
553 			{
554 				nameptr = g_entities[i].client->pers.netname;
555 			}
556 			else
557 			{
558 				nameptr = unknownname;
559 			}
560 			trap->FS_Write(nameptr, strlen(nameptr), weaponfile);
561 
562 			for (j=0;j<WP_NUM_WEAPONS;j++)
563 			{
564 				Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogDeaths[i][j]);
565 				trap->FS_Write(string, strlen(string), weaponfile);
566 			}
567 
568 			Com_sprintf(string, sizeof(string), "\n");
569 			trap->FS_Write(string, strlen(string), weaponfile);
570 		}
571 	}
572 
573 	// Sum up the totals.
574 	Com_sprintf(string, sizeof(string), "\n***TOTAL:");
575 	trap->FS_Write(string, strlen(string), weaponfile);
576 
577 	for (j=0;j<WP_NUM_WEAPONS;j++)
578 	{
579 		Com_sprintf(string, sizeof(string), "\t%d", totaldeaths[j]);
580 		trap->FS_Write(string, strlen(string), weaponfile);
581 	}
582 
583 	Com_sprintf(string, sizeof(string), "\n\n\n");
584 	trap->FS_Write(string, strlen(string), weaponfile);
585 
586 
587 
588 
589 	// Weapon damage per character
590 
591 	Com_sprintf(string, sizeof(string), "Weapon Damage per Player:\n\n");
592 	trap->FS_Write( string, strlen( string ), weaponfile);
593 
594 	Com_sprintf(string, sizeof(string), "Player");
595 	trap->FS_Write(string, strlen(string), weaponfile);
596 
597 	for (j=0; j<WP_NUM_WEAPONS; j++)
598 	{
599 		Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]);
600 		trap->FS_Write(string, strlen(string), weaponfile);
601 	}
602 	Com_sprintf(string, sizeof(string), "\n");
603 	trap->FS_Write(string, strlen(string), weaponfile);
604 
605 	// Cycle through each player, give their name and the number of times they picked up each weapon.
606 	for (i=0; i<MAX_CLIENTS; i++)
607 	{
608 		if (G_WeaponLogClientTouch[i])
609 		{	// Ignore any entity/clients we don't care about!
610 
611 			// We must grab the totals from the damage types for the player and map them to the weapons.
612 			memset(percharacter, 0, sizeof(percharacter));
613 			for (j=0; j<MOD_MAX; j++)
614 			{
615 				if (j <= MOD_SENTRY)
616 				{
617 					curwp = weaponFromMOD[j];
618 					percharacter[curwp] += G_WeaponLogDamage[i][j];
619 				}
620 			}
621 
622 			if ( g_entities[i].client )
623 			{
624 				nameptr = g_entities[i].client->pers.netname;
625 			}
626 			else
627 			{
628 				nameptr = unknownname;
629 			}
630 			trap->FS_Write(nameptr, strlen(nameptr), weaponfile);
631 
632 			for (j=0;j<WP_NUM_WEAPONS;j++)
633 			{
634 				Com_sprintf(string, sizeof(string), "\t%d", percharacter[j]);
635 				trap->FS_Write(string, strlen(string), weaponfile);
636 			}
637 
638 			Com_sprintf(string, sizeof(string), "\n");
639 			trap->FS_Write(string, strlen(string), weaponfile);
640 		}
641 	}
642 
643 	// Sum up the totals.
644 	Com_sprintf(string, sizeof(string), "\n***TOTAL:");
645 	trap->FS_Write(string, strlen(string), weaponfile);
646 
647 	for (j=0;j<WP_NUM_WEAPONS;j++)
648 	{
649 		Com_sprintf(string, sizeof(string), "\t%d", totaldamage[j]);
650 		trap->FS_Write(string, strlen(string), weaponfile);
651 	}
652 
653 	Com_sprintf(string, sizeof(string), "\n\n\n");
654 	trap->FS_Write(string, strlen(string), weaponfile);
655 
656 
657 
658 	// Weapon kills per character
659 
660 	Com_sprintf(string, sizeof(string), "Weapon Kills per Player:\n\n");
661 	trap->FS_Write( string, strlen( string ), weaponfile);
662 
663 	Com_sprintf(string, sizeof(string), "Player");
664 	trap->FS_Write(string, strlen(string), weaponfile);
665 
666 	for (j=0; j<WP_NUM_WEAPONS; j++)
667 	{
668 		Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]);
669 		trap->FS_Write(string, strlen(string), weaponfile);
670 	}
671 	Com_sprintf(string, sizeof(string), "\n");
672 	trap->FS_Write(string, strlen(string), weaponfile);
673 
674 	// Cycle through each player, give their name and the number of times they picked up each weapon.
675 	for (i=0; i<MAX_CLIENTS; i++)
676 	{
677 		if (G_WeaponLogClientTouch[i])
678 		{	// Ignore any entity/clients we don't care about!
679 
680 			// We must grab the totals from the damage types for the player and map them to the weapons.
681 			memset(percharacter, 0, sizeof(percharacter));
682 			for (j=0; j<MOD_MAX; j++)
683 			{
684 				if (j <= MOD_SENTRY)
685 				{
686 					curwp = weaponFromMOD[j];
687 					percharacter[curwp] += G_WeaponLogKills[i][j];
688 				}
689 			}
690 
691 			if ( g_entities[i].client )
692 			{
693 				nameptr = g_entities[i].client->pers.netname;
694 			}
695 			else
696 			{
697 				nameptr = unknownname;
698 			}
699 			trap->FS_Write(nameptr, strlen(nameptr), weaponfile);
700 
701 			for (j=0;j<WP_NUM_WEAPONS;j++)
702 			{
703 				Com_sprintf(string, sizeof(string), "\t%d", percharacter[j]);
704 				trap->FS_Write(string, strlen(string), weaponfile);
705 			}
706 
707 			Com_sprintf(string, sizeof(string), "\n");
708 			trap->FS_Write(string, strlen(string), weaponfile);
709 		}
710 	}
711 
712 	// Sum up the totals.
713 	Com_sprintf(string, sizeof(string), "\n***TOTAL:");
714 	trap->FS_Write(string, strlen(string), weaponfile);
715 
716 	for (j=0;j<WP_NUM_WEAPONS;j++)
717 	{
718 		Com_sprintf(string, sizeof(string), "\t%d", totalkills[j]);
719 		trap->FS_Write(string, strlen(string), weaponfile);
720 	}
721 
722 	Com_sprintf(string, sizeof(string), "\n\n\n");
723 	trap->FS_Write(string, strlen(string), weaponfile);
724 
725 
726 
727 	// Damage type damage per character
728 	Com_sprintf(string, sizeof(string), "Typed Damage per Player:\n\n");
729 	trap->FS_Write( string, strlen( string ), weaponfile);
730 
731 	Com_sprintf(string, sizeof(string), "Player");
732 	trap->FS_Write(string, strlen(string), weaponfile);
733 
734 	for (j=0; j<MOD_MAX; j++)
735 	{
736 		Com_sprintf(string, sizeof(string), "\t%s", modNames[j]);
737 		trap->FS_Write(string, strlen(string), weaponfile);
738 	}
739 	Com_sprintf(string, sizeof(string), "\n");
740 	trap->FS_Write(string, strlen(string), weaponfile);
741 
742 	// Cycle through each player, give their name and the number of times they picked up each weapon.
743 	for (i=0; i<MAX_CLIENTS; i++)
744 	{
745 		if (G_WeaponLogClientTouch[i])
746 		{	// Ignore any entity/clients we don't care about!
747 			if ( g_entities[i].client )
748 			{
749 				nameptr = g_entities[i].client->pers.netname;
750 			}
751 			else
752 			{
753 				nameptr = unknownname;
754 			}
755 			trap->FS_Write(nameptr, strlen(nameptr), weaponfile);
756 
757 			for (j=0;j<MOD_MAX;j++)
758 			{
759 				Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogDamage[i][j]);
760 				trap->FS_Write(string, strlen(string), weaponfile);
761 			}
762 
763 			Com_sprintf(string, sizeof(string), "\n");
764 			trap->FS_Write(string, strlen(string), weaponfile);
765 		}
766 	}
767 
768 	// Sum up the totals.
769 	Com_sprintf(string, sizeof(string), "\n***TOTAL:");
770 	trap->FS_Write(string, strlen(string), weaponfile);
771 
772 	for (j=0;j<MOD_MAX;j++)
773 	{
774 		Com_sprintf(string, sizeof(string), "\t%d", totaldamageMOD[j]);
775 		trap->FS_Write(string, strlen(string), weaponfile);
776 	}
777 
778 	Com_sprintf(string, sizeof(string), "\n\n\n");
779 	trap->FS_Write(string, strlen(string), weaponfile);
780 
781 
782 
783 	// Damage type kills per character
784 	Com_sprintf(string, sizeof(string), "Damage-Typed Kills per Player:\n\n");
785 	trap->FS_Write( string, strlen( string ), weaponfile);
786 
787 	Com_sprintf(string, sizeof(string), "Player");
788 	trap->FS_Write(string, strlen(string), weaponfile);
789 
790 	for (j=0; j<MOD_MAX; j++)
791 	{
792 		Com_sprintf(string, sizeof(string), "\t%s", modNames[j]);
793 		trap->FS_Write(string, strlen(string), weaponfile);
794 	}
795 	Com_sprintf(string, sizeof(string), "\n");
796 	trap->FS_Write(string, strlen(string), weaponfile);
797 
798 	// Cycle through each player, give their name and the number of times they picked up each weapon.
799 	for (i=0; i<MAX_CLIENTS; i++)
800 	{
801 		if (G_WeaponLogClientTouch[i])
802 		{	// Ignore any entity/clients we don't care about!
803 			if ( g_entities[i].client )
804 			{
805 				nameptr = g_entities[i].client->pers.netname;
806 			}
807 			else
808 			{
809 				nameptr = unknownname;
810 			}
811 			trap->FS_Write(nameptr, strlen(nameptr), weaponfile);
812 
813 			for (j=0;j<MOD_MAX;j++)
814 			{
815 				Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogKills[i][j]);
816 				trap->FS_Write(string, strlen(string), weaponfile);
817 			}
818 
819 			Com_sprintf(string, sizeof(string), "\n");
820 			trap->FS_Write(string, strlen(string), weaponfile);
821 		}
822 	}
823 
824 	// Sum up the totals.
825 	Com_sprintf(string, sizeof(string), "\n***TOTAL:");
826 	trap->FS_Write(string, strlen(string), weaponfile);
827 
828 	for (j=0;j<MOD_MAX;j++)
829 	{
830 		Com_sprintf(string, sizeof(string), "\t%d", totalkillsMOD[j]);
831 		trap->FS_Write(string, strlen(string), weaponfile);
832 	}
833 
834 	Com_sprintf(string, sizeof(string), "\n\n\n");
835 	trap->FS_Write(string, strlen(string), weaponfile);
836 
837 
838 	trap->FS_Close(weaponfile);
839 
840 
841 #endif //LOGGING_WEAPONS
842 }
843 
844 // did this player earn the efficiency award?
CalculateEfficiency(gentity_t * ent,int * efficiency)845 qboolean CalculateEfficiency(gentity_t *ent, int *efficiency)
846 {
847 #ifdef LOGGING_WEAPONS
848 	float		fAccuracyRatio = 0, fBestRatio = 0;
849 	int			i = 0, nShotsFired = 0, nShotsHit = 0, nBestPlayer = -1, tempEff = 0;
850 	gentity_t	*player = NULL;
851 
852 
853 	for (i = 0; i < sv_maxclients.integer; i++)
854 	{
855 		player = g_entities + i;
856 		if (!player->inuse)
857 			continue;
858 		nShotsFired = player->client->accuracy_shots; //player->client->ps.persistant[PERS_ACCURACY_SHOTS];
859 		nShotsHit = player->client->accuracy_hits; //player->client->ps.persistant[PERS_ACCURACY_HITS];
860 		fAccuracyRatio = ( ((float)nShotsHit)/((float)nShotsFired) );
861 		if (fAccuracyRatio > fBestRatio)
862 		{
863 			fBestRatio = fAccuracyRatio;
864 			nBestPlayer = i;
865 		}
866 	}
867 	if (-1 == nBestPlayer)
868 	{
869 		// huh?
870 		return qfalse;
871 	}
872 	if (nBestPlayer == ent->s.number)
873 	{
874 		tempEff = (int)(100*fBestRatio);
875 		if (tempEff > 50)
876 		{
877 			*efficiency = tempEff;
878 			return qtrue;
879 		}
880 		return qfalse;
881 	}
882 #endif // LOGGING_WEAPONS
883 	return qfalse;
884 }
885 
886 // did this player earn the sharpshooter award?
CalculateSharpshooter(gentity_t * ent,int * frags)887 qboolean CalculateSharpshooter(gentity_t *ent, int *frags)
888 {
889 #ifdef LOGGING_WEAPONS
890 	int			i = 0, nBestPlayer = -1, nKills = 0, nMostKills = 0,
891 				playTime = (level.time - ent->client->pers.enterTime)/60000;
892 	gentity_t	*player = NULL;
893 
894 	// if this guy didn't get one kill per minute, reject him right now
895 	if ( ((float)(G_WeaponLogKills[ent-g_entities][MOD_DISRUPTOR_SNIPER]))/((float)(playTime)) < 1.0 )
896 	{
897 		return qfalse;
898 	}
899 
900 	for (i = 0; i < sv_maxclients.integer; i++)
901 	{
902 		nKills = 0;
903 		player = g_entities + i;
904 		if (!player->inuse)
905 			continue;
906 		nKills = G_WeaponLogKills[i][MOD_DISRUPTOR_SNIPER];
907 		if (nKills > nMostKills)
908 		{
909 			nMostKills = nKills;
910 			nBestPlayer = i;
911 		}
912 	}
913 	if (-1 == nBestPlayer)
914 	{
915 		return qfalse;
916 	}
917 	if (nBestPlayer == ent->s.number)
918 	{
919 		*frags = nMostKills;
920 		return qtrue;
921 	}
922 #endif // LOGGING_WEAPONS
923 	return qfalse;
924 }
925 
926 // did this player earn the untouchable award?
CalculateUntouchable(gentity_t * ent)927 qboolean CalculateUntouchable(gentity_t *ent)
928 {
929 #ifdef LOGGING_WEAPONS
930 	int			playTime;
931 	playTime = (level.time - ent->client->pers.enterTime)/60000;
932 
933 	if ( level.gametype == GT_JEDIMASTER && ent->client->ps.isJediMaster )
934 	{//Jedi Master can only be killed once anyway
935 		return qfalse;
936 	}
937 	//------------------------------------------------------ MUST HAVE ACHIEVED 2 KILLS PER MINUTE
938 	if ( ((float)ent->client->ps.persistant[PERS_SCORE])/((float)(playTime)) < 2.0  || playTime==0)
939 		return qfalse;
940 	//------------------------------------------------------ MUST HAVE ACHIEVED 2 KILLS PER MINUTE
941 
942 
943 	// if this guy was never killed...  Award Away!!!
944 	if (ent->client->ps.persistant[PERS_KILLED]==0)
945 		return qtrue;
946 
947 #endif // LOGGING_WEAPONS
948 	return qfalse;
949 }
950 
951 // did this player earn the logistics award?
CalculateLogistics(gentity_t * ent,int * stuffUsed)952 qboolean CalculateLogistics(gentity_t *ent, int *stuffUsed)
953 {
954 #ifdef LOGGING_WEAPONS
955 	int			i = 0, j = 0, nBestPlayer = -1, nStuffUsed = 0, nMostStuffUsed = 0,
956 				nDifferent = 0, nMostDifferent = 0;
957 	gentity_t	*player = NULL;
958 
959 	for (i = 0; i < sv_maxclients.integer; i++)
960 	{
961 		nStuffUsed = 0;
962 		nDifferent = 0;
963 		player = g_entities + i;
964 		if (!player->inuse)
965 			continue;
966 		for (j = HI_NONE+1; j < HI_NUM_HOLDABLE; j++)
967 		{
968 			if (G_WeaponLogPowerups[i][j])
969 			{
970 				nDifferent++;
971 			}
972 			nStuffUsed += G_WeaponLogPowerups[i][j];
973 		}
974 		for (j = PW_NONE+1; j < PW_NUM_POWERUPS; j++)
975 		{
976 			if (G_WeaponLogItems[i][j])
977 			{
978 				nDifferent++;
979 			}
980 			nStuffUsed += G_WeaponLogItems[i][j];
981 		}
982 		if ( (nDifferent >= 4) && (nDifferent >= nMostDifferent) )
983 		{
984 			if (nStuffUsed > nMostStuffUsed)
985 			{
986 				nMostDifferent = nDifferent;
987 				nMostStuffUsed = nStuffUsed;
988 				nBestPlayer = i;
989 			}
990 		}
991 	}
992 	if (-1 == nBestPlayer)
993 	{
994 		return qfalse;
995 	}
996 	if (nBestPlayer == ent->s.number)
997 	{
998 		*stuffUsed = nMostDifferent;
999 		return qtrue;
1000 	}
1001 #endif // LOGGING_WEAPONS
1002 	return qfalse;
1003 }
1004 
1005 
1006 
1007 
1008 // did this player earn the tactician award?
CalculateTactician(gentity_t * ent,int * kills)1009 qboolean CalculateTactician(gentity_t *ent, int *kills)
1010 {
1011 #ifdef LOGGING_WEAPONS
1012 	int			i = 0, nBestPlayer = -1, nKills = 0, nMostKills = 0;
1013 	int			person = 0, weapon = 0;
1014 	gentity_t	*player = NULL;
1015 	int			wasPickedUpBySomeone[WP_NUM_WEAPONS];
1016 	int			killsWithWeapon[WP_NUM_WEAPONS];
1017 	int			playTime = (level.time - ent->client->pers.enterTime)/60000;
1018 
1019 	if ( HasSetSaberOnly() )
1020 	{//duh, only 1 weapon
1021 		return qfalse;
1022 	}
1023 	if ( level.gametype == GT_JEDIMASTER && ent->client->ps.isJediMaster )
1024 	{//Jedi Master has only 1 weapon
1025 		return qfalse;
1026 	}
1027 	//------------------------------------------------------ MUST HAVE ACHIEVED 2 KILLS PER MINUTE
1028 	if (playTime<0.3)
1029 		return qfalse;
1030 
1031 	if ( ((float)ent->client->ps.persistant[PERS_SCORE])/((float)(playTime)) < 2.0 )
1032 		return qfalse;
1033 	//------------------------------------------------------ MUST HAVE ACHIEVED 2 KILLS PER MINUTE
1034 
1035 
1036 
1037 
1038 	//------------------------------------------------------ FOR EVERY WEAPON, ADD UP TOTAL PICKUPS
1039 	for (weapon = 0; weapon<WP_NUM_WEAPONS; weapon++)
1040 			wasPickedUpBySomeone[weapon] = 0;				// CLEAR
1041 
1042 	for (person=0; person<sv_maxclients.integer; person++)
1043 	{
1044 		for (weapon = 0; weapon<WP_NUM_WEAPONS; weapon++)
1045 		{
1046 			if (G_WeaponLogPickups[person][weapon]>0)
1047 				wasPickedUpBySomeone[weapon]++;
1048 		}
1049 	}
1050 	//------------------------------------------------------ FOR EVERY WEAPON, ADD UP TOTAL PICKUPS
1051 
1052 
1053 
1054 
1055 	//------------------------------------------------------ FOR EVERY PERSON, CHECK FOR CANDIDATE
1056 	for (person=0; person<sv_maxclients.integer; person++)
1057 	{
1058 		player = g_entities + person;
1059 		if (!player->inuse)			continue;
1060 
1061 		nKills = 0;											// This Persons's Kills
1062 		for (weapon=0; weapon<WP_NUM_WEAPONS; weapon++)
1063 			killsWithWeapon[weapon] = 0;					// CLEAR
1064 
1065 		for (i=0; i<MOD_MAX; i++)
1066 		{
1067 			weapon = weaponFromMOD[i];									// Select Weapon
1068 			killsWithWeapon[weapon] += G_WeaponLogKills[person][i];		// Store Num Kills With Weapon
1069 		}
1070 
1071 		weapon=WP_STUN_BATON;		// Start At Stun Baton
1072 		//   keep looking through weapons if weapon is not on map, or if it is and we used it
1073 		while( weapon<WP_NUM_WEAPONS && (!wasPickedUpBySomeone[weapon] || killsWithWeapon[weapon]>0) )
1074 		{
1075 			weapon++;
1076 			nKills+=killsWithWeapon[weapon];							//  Update the number of kills
1077 		}
1078 		//
1079 		// At this point we have either successfully gone through every weapon on the map and saw it had
1080 		// been used, or we found one that WAS on the map and was NOT used
1081 		//
1082 		// so we look to see if the weapon==Max (i.e. we used every one) and then we check to see
1083 		// if we got the most kills out of anyone else who did this.
1084 		//
1085 		if (weapon>=WP_NUM_WEAPONS && nKills>nMostKills)
1086 		{
1087 			// WE ARE A TACTICION CANDIDATE
1088 			nMostKills  = nKills;
1089 			nBestPlayer = person;
1090 		}
1091 	}
1092 	//------------------------------------------------------ FOR EVERY PERSON, CHECK FOR CANDIDATE
1093 
1094 	//Now, if we are the best player, return true and the number of kills we got
1095 	if (nBestPlayer == ent->s.number)
1096 	{
1097 		*kills = nMostKills;
1098 		return qtrue;
1099 	}
1100 #endif // LOGGING_WEAPONS
1101 	return qfalse;
1102 }
1103 
1104 
1105 
1106 
1107 // did this player earn the demolitionist award?
CalculateDemolitionist(gentity_t * ent,int * kills)1108 qboolean CalculateDemolitionist(gentity_t *ent, int *kills)
1109 {
1110 #ifdef LOGGING_WEAPONS
1111 	int			i = 0, nBestPlayer = -1, nKills = 0, nMostKills = 0,
1112 				playTime = (level.time - ent->client->pers.enterTime)/60000;
1113 	gentity_t	*player = NULL;
1114 
1115 	for (i = 0; i < sv_maxclients.integer; i++)
1116 	{
1117 		nKills = 0;
1118 		player = g_entities + i;
1119 		if (!player->inuse)
1120 			continue;
1121 
1122 		nKills = G_WeaponLogKills[i][MOD_THERMAL];
1123 		nKills += G_WeaponLogKills[i][MOD_THERMAL_SPLASH];
1124 		nKills += G_WeaponLogKills[i][MOD_ROCKET];
1125 		nKills += G_WeaponLogKills[i][MOD_ROCKET_SPLASH];
1126 		nKills += G_WeaponLogKills[i][MOD_ROCKET_HOMING];
1127 		nKills += G_WeaponLogKills[i][MOD_ROCKET_HOMING_SPLASH];
1128 		nKills += G_WeaponLogKills[i][MOD_TRIP_MINE_SPLASH];
1129 		nKills += G_WeaponLogKills[i][MOD_TIMED_MINE_SPLASH];
1130 		nKills += G_WeaponLogKills[i][MOD_DET_PACK_SPLASH];
1131 
1132 		// if this guy didn't get two explosive kills per minute, reject him right now
1133 		if ( ((float)nKills)/((float)(playTime)) < 2.0 )
1134 		{
1135 			continue;
1136 		}
1137 
1138 		if (nKills > nMostKills)
1139 		{
1140 			nMostKills = nKills;
1141 			nBestPlayer = i;
1142 		}
1143 	}
1144 	if (-1 == nBestPlayer)
1145 	{
1146 		return qfalse;
1147 	}
1148 	if (nBestPlayer == ent->s.number)
1149 	{
1150 		*kills = nMostKills;
1151 		return qtrue;
1152 	}
1153 #endif // LOGGING_WEAPONS
1154 	return qfalse;
1155 }
1156 
CalculateStreak(gentity_t * ent)1157 int CalculateStreak(gentity_t *ent)
1158 {
1159 #if 0
1160 	if (ent->client->ps.persistant[PERS_STREAK_COUNT] >= STREAK_CHAMPION)
1161 	{
1162 		return STREAK_CHAMPION;
1163 	}
1164 	if (ent->client->ps.persistant[PERS_STREAK_COUNT] >= STREAK_MASTER)
1165 	{
1166 		return STREAK_MASTER;
1167 	}
1168 	if (ent->client->ps.persistant[PERS_STREAK_COUNT] >= STREAK_EXPERT)
1169 	{
1170 		return STREAK_EXPERT;
1171 	}
1172 	if (ent->client->ps.persistant[PERS_STREAK_COUNT] >= STREAK_ACE)
1173 	{
1174 		return STREAK_ACE;
1175 	}
1176 #endif
1177 	//No streak calculation, at least for now.
1178 	return 0;
1179 }
1180 
CalculateTeamMVP(gentity_t * ent)1181 qboolean CalculateTeamMVP(gentity_t *ent)
1182 {
1183 	int			i = 0, nBestPlayer = -1, nScore = 0, nHighestScore = 0,
1184 				team = ent->client->ps.persistant[PERS_TEAM];
1185 	gentity_t	*player = NULL;
1186 
1187 	for (i = 0; i < sv_maxclients.integer; i++)
1188 	{
1189 		nScore = 0;
1190 		player = g_entities + i;
1191 		if (!player->inuse || (player->client->ps.persistant[PERS_TEAM] != team))
1192 			continue;
1193 		nScore = player->client->ps.persistant[PERS_SCORE];
1194 		if (nScore > nHighestScore)
1195 		{
1196 			nHighestScore = nScore;
1197 			nBestPlayer = i;
1198 		}
1199 	}
1200 	if (-1 == nBestPlayer)
1201 	{
1202 		return qfalse;
1203 	}
1204 	if (nBestPlayer == ent->s.number)
1205 	{
1206 		return qtrue;
1207 	}
1208 	return qfalse;
1209 }
1210 
CalculateTeamDefender(gentity_t * ent)1211 qboolean CalculateTeamDefender(gentity_t *ent)
1212 {
1213 	int			i = 0, nBestPlayer = -1, nScore = 0, nHighestScore = 0,
1214 				team = ent->client->ps.persistant[PERS_TEAM];
1215 	gentity_t	*player = NULL;
1216 
1217 	/*
1218 	if (CalculateTeamMVP(ent))
1219 	{
1220 		return qfalse;
1221 	}
1222 	*/
1223 	for (i = 0; i < sv_maxclients.integer; i++)
1224 	{
1225 		nScore = 0;
1226 		player = g_entities + i;
1227 		if (!player->inuse || (player->client->ps.persistant[PERS_TEAM] != team))
1228 			continue;
1229 		nScore = player->client->pers.teamState.basedefense;
1230 		if (nScore > nHighestScore)
1231 		{
1232 			nHighestScore = nScore;
1233 			nBestPlayer = i;
1234 		}
1235 	}
1236 	if (-1 == nBestPlayer)
1237 	{
1238 		return qfalse;
1239 	}
1240 	if (nBestPlayer == ent->s.number)
1241 	{
1242 		return qtrue;
1243 	}
1244 	return qfalse;
1245 }
1246 
CalculateTeamWarrior(gentity_t * ent)1247 qboolean CalculateTeamWarrior(gentity_t *ent)
1248 {
1249 	int			i = 0, nBestPlayer = -1, nScore = 0, nHighestScore = 0,
1250 				team = ent->client->ps.persistant[PERS_TEAM];
1251 	gentity_t	*player = NULL;
1252 
1253 	/*
1254 	if (CalculateTeamMVP(ent) || CalculateTeamDefender(ent))
1255 	{
1256 		return qfalse;
1257 	}
1258 	*/
1259 	for (i = 0; i < sv_maxclients.integer; i++)
1260 	{
1261 		nScore = 0;
1262 		player = g_entities + i;
1263 		if (!player->inuse || (player->client->ps.persistant[PERS_TEAM] != team))
1264 			continue;
1265 		nScore = player->client->ps.persistant[PERS_SCORE];
1266 		if (nScore > nHighestScore)
1267 		{
1268 			nHighestScore = nScore;
1269 			nBestPlayer = i;
1270 		}
1271 	}
1272 	if (-1 == nBestPlayer)
1273 	{
1274 		return qfalse;
1275 	}
1276 	if (nBestPlayer == ent->s.number)
1277 	{
1278 		return qtrue;
1279 	}
1280 	return qfalse;
1281 }
1282 
CalculateTeamCarrier(gentity_t * ent)1283 qboolean CalculateTeamCarrier(gentity_t *ent)
1284 {
1285 	int			i = 0, nBestPlayer = -1, nScore = 0, nHighestScore = 0,
1286 				team = ent->client->ps.persistant[PERS_TEAM];
1287 	gentity_t	*player = NULL;
1288 
1289 	/*
1290 	if (CalculateTeamMVP(ent) || CalculateTeamDefender(ent) || CalculateTeamWarrior(ent))
1291 	{
1292 		return qfalse;
1293 	}
1294 	*/
1295 	for (i = 0; i < sv_maxclients.integer; i++)
1296 	{
1297 		nScore = 0;
1298 		player = g_entities + i;
1299 		if (!player->inuse || (player->client->ps.persistant[PERS_TEAM] != team))
1300 			continue;
1301 		nScore = player->client->pers.teamState.captures;
1302 		if (nScore > nHighestScore)
1303 		{
1304 			nHighestScore = nScore;
1305 			nBestPlayer = i;
1306 		}
1307 	}
1308 	if (-1 == nBestPlayer)
1309 	{
1310 		return qfalse;
1311 	}
1312 	if (nBestPlayer == ent->s.number)
1313 	{
1314 		return qtrue;
1315 	}
1316 	return qfalse;
1317 }
1318 
CalculateTeamInterceptor(gentity_t * ent)1319 qboolean CalculateTeamInterceptor(gentity_t *ent)
1320 {
1321 	int			i = 0, nBestPlayer = -1, nScore = 0, nHighestScore = 0,
1322 				team = ent->client->ps.persistant[PERS_TEAM];
1323 	gentity_t	*player = NULL;
1324 
1325 	/*
1326 	if (CalculateTeamMVP(ent) || CalculateTeamDefender(ent) || CalculateTeamWarrior(ent) ||
1327 		CalculateTeamCarrier(ent))
1328 	{
1329 		return qfalse;
1330 	}
1331 	*/
1332 	for (i = 0; i < sv_maxclients.integer; i++)
1333 	{
1334 		nScore = 0;
1335 		player = g_entities + i;
1336 		if (!player->inuse || (player->client->ps.persistant[PERS_TEAM] != team))
1337 			continue;
1338 		nScore = player->client->pers.teamState.flagrecovery;
1339 		nScore += player->client->pers.teamState.fragcarrier;
1340 		if (nScore > nHighestScore)
1341 		{
1342 			nHighestScore = nScore;
1343 			nBestPlayer = i;
1344 		}
1345 	}
1346 	if (-1 == nBestPlayer)
1347 	{
1348 		return qfalse;
1349 	}
1350 	if (nBestPlayer == ent->s.number)
1351 	{
1352 		return qtrue;
1353 	}
1354 	return qfalse;
1355 }
1356 
CalculateTeamRedShirt(gentity_t * ent)1357 qboolean CalculateTeamRedShirt(gentity_t *ent)
1358 {
1359 	int			i = 0, nBestPlayer = -1, nScore = 0, nHighestScore = 0,
1360 				team = ent->client->ps.persistant[PERS_TEAM];
1361 	gentity_t	*player = NULL;
1362 
1363 	/*
1364 	if (CalculateTeamMVP(ent) || CalculateTeamDefender(ent) || CalculateTeamWarrior(ent) ||
1365 		CalculateTeamCarrier(ent) || CalculateTeamInterceptor(ent))
1366 	{
1367 		return qfalse;
1368 	}
1369 	*/
1370 	for (i = 0; i < sv_maxclients.integer; i++)
1371 	{
1372 		nScore = 0;
1373 		player = g_entities + i;
1374 		if (!player->inuse || (player->client->ps.persistant[PERS_TEAM] != team))
1375 			continue;
1376 		nScore = player->client->ps.persistant[PERS_KILLED];
1377 		nScore -= player->client->ps.fd.suicides; // suicides don't count, you big cheater.
1378 		if (nScore > nHighestScore)
1379 		{
1380 			nHighestScore = nScore;
1381 			nBestPlayer = i;
1382 		}
1383 	}
1384 	if (-1 == nBestPlayer)
1385 	{
1386 		return qfalse;
1387 	}
1388 	if (nBestPlayer == ent->s.number)
1389 	{
1390 		return qtrue;
1391 	}
1392 	return qfalse;
1393 }
1394 
1395 typedef enum {
1396 	AWARD_EFFICIENCY,		// Accuracy
1397 	AWARD_SHARPSHOOTER,		// Most compression rifle frags
1398 	AWARD_UNTOUCHABLE,		// Perfect (no deaths)
1399 	AWARD_LOGISTICS,		// Most pickups
1400 	AWARD_TACTICIAN,		// Kills with all weapons
1401 	AWARD_DEMOLITIONIST,	// Most explosive damage kills
1402 	AWARD_STREAK,			// Ace/Expert/Master/Champion
1403 	AWARD_TEAM,				// MVP/Defender/Warrior/Carrier/Interceptor/Bravery
1404 	AWARD_SECTION31,		// All-around god
1405 	AWARD_MAX
1406 } awardType_t;
1407 
1408 typedef enum
1409 {
1410 	TEAM_NONE = 0,			// ha ha! you suck!
1411 	TEAM_MVP,				// most overall points
1412 	TEAM_DEFENDER,			// killed the most baddies near your flag
1413 	TEAM_WARRIOR,			// most frags
1414 	TEAM_CARRIER,			// infected the most people with plague
1415 	TEAM_INTERCEPTOR,		// returned your own flag the most
1416 	TEAM_BRAVERY,			// Red Shirt Award (tm). you died more than anybody.
1417 	TEAM_MAX
1418 } teamAward_e;
1419 
CalculateTeamAward(gentity_t * ent)1420 int CalculateTeamAward(gentity_t *ent)
1421 {
1422 	int teamAwards = 0;
1423 
1424 	if (CalculateTeamMVP(ent))
1425 	{
1426 		teamAwards |= (1<<TEAM_MVP);
1427 	}
1428 	if (GT_CTF == level.gametype ||
1429 		GT_CTY == level.gametype)
1430 	{
1431 		if (CalculateTeamDefender(ent))
1432 		{
1433 			teamAwards |= (1<<TEAM_DEFENDER);
1434 		}
1435 		if (CalculateTeamWarrior(ent))
1436 		{
1437 			teamAwards |= (1<<TEAM_WARRIOR);
1438 		}
1439 		if (CalculateTeamCarrier(ent))
1440 		{
1441 			teamAwards |= (1<<TEAM_CARRIER);
1442 		}
1443 		if (CalculateTeamInterceptor(ent))
1444 		{
1445 			teamAwards |= (1<<TEAM_INTERCEPTOR);
1446 		}
1447 	}
1448 	if ( !teamAwards && CalculateTeamRedShirt(ent) )
1449 	{//if you got nothing else and died a lot, at least get bravery
1450 		teamAwards |= (1<<TEAM_BRAVERY);
1451 	}
1452 	return teamAwards;
1453 }
1454 
CalculateSection31Award(gentity_t * ent)1455 qboolean CalculateSection31Award(gentity_t *ent)
1456 {
1457 	int			i = 0, frags = 0, efficiency = 0;
1458 	gentity_t	*player = NULL;
1459 
1460 	for (i = 0; i < sv_maxclients.integer; i++)
1461 	{
1462 		player = g_entities + i;
1463 		if (!player->inuse)
1464 			continue;
1465 //
1466 //	kef -- heh.
1467 //
1468 //		if (strcmp("JaxxonPhred", ent->client->pers.netname))
1469 //		{
1470 //			continue;
1471 //		}
1472 		CalculateEfficiency(ent, &efficiency);
1473 		if (!CalculateSharpshooter(ent, &frags) ||
1474 			!CalculateUntouchable(ent) ||
1475 			/*(CalculateStreak(ent) < STREAK_CHAMPION) ||*/
1476 			(efficiency < 75))
1477 		{
1478 			continue;
1479 		}
1480 		return qtrue;
1481 	}
1482 	return qfalse;
1483 }
1484 
1485 #if 0
1486 
1487 #define AWARDS_MSG_LENGTH		256
1488 
1489 void CalculateAwards(gentity_t *ent, char *msg)
1490 {
1491 #ifdef LOGGING_WEAPONS
1492 	char		buf1[AWARDS_MSG_LENGTH], buf2[AWARDS_MSG_LENGTH];
1493 	int			awardFlags = 0, efficiency = 0, stuffUsed = 0, kills = 0, streak = 0, teamAwards = 0;
1494 
1495 	memset(buf1, 0, AWARDS_MSG_LENGTH);
1496 	memset(buf2, 0, AWARDS_MSG_LENGTH);
1497 	if (CalculateEfficiency(ent, &efficiency))
1498 	{
1499 		awardFlags |= (1<<AWARD_EFFICIENCY);
1500 		Com_sprintf(buf1, AWARDS_MSG_LENGTH, " %d", efficiency);
1501 	}
1502 	if (CalculateSharpshooter(ent, &kills))
1503 	{
1504 		awardFlags |= (1<<AWARD_SHARPSHOOTER);
1505 		strcpy(buf2, buf1);
1506 		Com_sprintf(buf1, AWARDS_MSG_LENGTH, "%s %d", buf2, kills);
1507 	}
1508 	if (CalculateUntouchable(ent))
1509 	{
1510 		awardFlags |= (1<<AWARD_UNTOUCHABLE);
1511 		strcpy(buf2, buf1);
1512 		Com_sprintf(buf1, AWARDS_MSG_LENGTH, "%s %d", buf2, 0);
1513 	}
1514 	if (CalculateLogistics(ent, &stuffUsed))
1515 	{
1516 		awardFlags |= (1<<AWARD_LOGISTICS);
1517 		strcpy(buf2, buf1);
1518 		Com_sprintf(buf1, AWARDS_MSG_LENGTH, "%s %d", buf2, stuffUsed);
1519 	}
1520 	if (CalculateTactician(ent, &kills))
1521 	{
1522 		awardFlags |= (1<<AWARD_TACTICIAN);
1523 		strcpy(buf2, buf1);
1524 		Com_sprintf(buf1, AWARDS_MSG_LENGTH, "%s %d", buf2, kills);
1525 	}
1526 	if (CalculateDemolitionist(ent, &kills))
1527 	{
1528 		awardFlags |= (1<<AWARD_DEMOLITIONIST);
1529 		strcpy(buf2, buf1);
1530 		Com_sprintf(buf1, AWARDS_MSG_LENGTH, "%s %d", buf2, kills);
1531 	}
1532 	streak = CalculateStreak(ent);
1533 	if (streak)
1534 	{
1535 		awardFlags |= (1<<AWARD_STREAK);
1536 		strcpy(buf2, buf1);
1537 		Com_sprintf(buf1, AWARDS_MSG_LENGTH, "%s %d", buf2, streak);
1538 	}
1539 	if (level.gametype >= GT_TEAM)
1540 	{
1541 		teamAwards = CalculateTeamAward(ent);
1542 		if (teamAwards)
1543 		{
1544 			awardFlags |= (1<<AWARD_TEAM);
1545 			strcpy(buf2, buf1);
1546 			Com_sprintf(buf1, AWARDS_MSG_LENGTH, "%s %d", buf2, teamAwards);
1547 		}
1548 	}
1549 	if (CalculateSection31Award(ent))
1550 	{
1551 		awardFlags |= (1<<AWARD_SECTION31);
1552 		strcpy(buf2, buf1);
1553 		Com_sprintf(buf1, AWARDS_MSG_LENGTH, "%s %d", buf2, 0);
1554 	}
1555 	Q_strncpyz( buf2, msg, sizeof( buf2 ) );
1556 	Com_sprintf( msg, AWARDS_MSG_LENGTH, "%s %d%s", buf2, awardFlags, buf1);
1557 #endif // LOGGING_WEAPONS
1558 }
1559 
1560 int GetMaxDeathsForClient(int nClient)
1561 {
1562 	int i = 0, nMostDeaths = 0;
1563 
1564 	if ((nClient < 0) || (nClient >= MAX_CLIENTS))
1565 	{
1566 		return 0;
1567 	}
1568 	for (i = 0; i < MAX_CLIENTS; i++)
1569 	{
1570 		if (G_WeaponLogFrags[i][nClient] > nMostDeaths)
1571 		{
1572 			nMostDeaths = G_WeaponLogFrags[i][nClient];
1573 		}
1574 	}
1575 	return nMostDeaths;
1576 }
1577 
1578 int GetMaxKillsForClient(int nClient)
1579 {
1580 	int i = 0, nMostKills = 0;
1581 
1582 	if ((nClient < 0) || (nClient >= MAX_CLIENTS))
1583 	{
1584 		return 0;
1585 	}
1586 	for (i = 0; i < MAX_CLIENTS; i++)
1587 	{
1588 		if (G_WeaponLogFrags[nClient][i] > nMostKills)
1589 		{
1590 			nMostKills = G_WeaponLogFrags[nClient][i];
1591 		}
1592 	}
1593 	return nMostKills;
1594 }
1595 
1596 int GetFavoriteTargetForClient(int nClient)
1597 {
1598 	int i = 0, nMostKills = 0, nFavoriteTarget = -1;
1599 
1600 	if ((nClient < 0) || (nClient >= MAX_CLIENTS))
1601 	{
1602 		return 0;
1603 	}
1604 	for (i = 0; i < MAX_CLIENTS; i++)
1605 	{
1606 		if (G_WeaponLogFrags[nClient][i] > nMostKills)
1607 		{
1608 			nMostKills = G_WeaponLogFrags[nClient][i];
1609 			nFavoriteTarget = i;
1610 		}
1611 	}
1612 	return nFavoriteTarget;
1613 }
1614 
1615 int GetWorstEnemyForClient(int nClient)
1616 {
1617 	int i = 0, nMostDeaths = 0, nWorstEnemy = -1;
1618 
1619 	if ((nClient < 0) || (nClient >= MAX_CLIENTS))
1620 	{
1621 		return 0;
1622 	}
1623 	for (i = 0; i < MAX_CLIENTS; i++)
1624 	{
1625 		// If there is a tie for most deaths, we want to choose anybody else
1626 		// over the client...  I.E. Most deaths should not tie with yourself and
1627 		// have yourself show up...
1628 
1629 		if ( G_WeaponLogFrags[i][nClient] > nMostDeaths ||
1630 			(G_WeaponLogFrags[i][nClient]== nMostDeaths && i!=nClient && nMostDeaths!=0) )
1631 		{
1632 			nMostDeaths = G_WeaponLogFrags[i][nClient];
1633 			nWorstEnemy = i;
1634 		}
1635 	}
1636 	return nWorstEnemy;
1637 }
1638 
1639 int GetFavoriteWeaponForClient(int nClient)
1640 {
1641 	int i = 0, nMostKills = 0, fav=0, weapon=WP_STUN_BATON;
1642 	int	killsWithWeapon[WP_NUM_WEAPONS];
1643 
1644 
1645 	// First thing we need to do is cycle through all the MOD types and convert
1646 	// number of kills to a single weapon.
1647 	//----------------------------------------------------------------
1648 	for (weapon=0; weapon<WP_NUM_WEAPONS; weapon++)
1649 		killsWithWeapon[weapon] = 0;					// CLEAR
1650 
1651 	for (i=MOD_STUN_BATON; i<=MOD_FORCE_DARK; i++)
1652 	{
1653 		weapon = weaponFromMOD[i];									// Select Weapon
1654 
1655 		if (weapon != WP_NONE)
1656 		{
1657 			killsWithWeapon[weapon] += G_WeaponLogKills[nClient][i];	// Store Num Kills With Weapon
1658 		}
1659 	}
1660 
1661 	// now look through our list of kills per weapon and pick the biggest
1662 	//----------------------------------------------------------------
1663 	nMostKills=0;
1664 	for (weapon=WP_STUN_BATON; weapon<WP_NUM_WEAPONS; weapon++)
1665 	{
1666 		if (killsWithWeapon[weapon]>nMostKills)
1667 		{
1668 			nMostKills = killsWithWeapon[weapon];
1669 			fav = weapon;
1670 		}
1671 	}
1672 	return fav;
1673 }
1674 #endif
1675 
1676 // kef -- if a client leaves the game, clear out all counters he may have set
G_ClearClientLog(int client)1677 void QDECL G_ClearClientLog(int client)
1678 {
1679 	int i = 0;
1680 
1681 	for (i = 0; i < WP_NUM_WEAPONS; i++)
1682 	{
1683 		G_WeaponLogPickups[client][i] = 0;
1684 	}
1685 	for (i = 0; i < WP_NUM_WEAPONS; i++)
1686 	{
1687 		G_WeaponLogFired[client][i] = 0;
1688 	}
1689 	for (i = 0; i < MOD_MAX; i++)
1690 	{
1691 		G_WeaponLogDamage[client][i] = 0;
1692 	}
1693 	for (i = 0; i < MOD_MAX; i++)
1694 	{
1695 		G_WeaponLogKills[client][i] = 0;
1696 	}
1697 	for (i = 0; i < WP_NUM_WEAPONS; i++)
1698 	{
1699 		G_WeaponLogDeaths[client][i] = 0;
1700 	}
1701 	for (i = 0; i < MAX_CLIENTS; i++)
1702 	{
1703 		G_WeaponLogFrags[client][i] = 0;
1704 	}
1705 	for (i = 0; i < MAX_CLIENTS; i++)
1706 	{
1707 		G_WeaponLogFrags[i][client] = 0;
1708 	}
1709 	for (i = 0; i < WP_NUM_WEAPONS; i++)
1710 	{
1711 		G_WeaponLogTime[client][i] = 0;
1712 	}
1713 	G_WeaponLogLastTime[client] = 0;
1714 	G_WeaponLogClientTouch[client] = qfalse;
1715 	for (i = 0; i < HI_NUM_HOLDABLE; i++)
1716 	{
1717 		G_WeaponLogPowerups[client][i] = 0;
1718 	}
1719 	for (i = 0; i < PW_NUM_POWERUPS; i++)
1720 	{
1721 		G_WeaponLogItems[client][i] = 0;
1722 	}
1723 }
1724 
1725