1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 #include "g_local.h"
21 
22 
23 
24 /*
25 ======================================================================
26 
27 INTERMISSION
28 
29 ======================================================================
30 */
31 
MoveClientToIntermission(edict_t * ent)32 void MoveClientToIntermission (edict_t *ent)
33 {
34 	if (deathmatch->value || coop->value)
35 		ent->client->showscores = true;
36 	VectorCopy (level.intermission_origin, ent->s.origin);
37 	ent->client->ps.pmove.origin[0] = level.intermission_origin[0]*8;
38 	ent->client->ps.pmove.origin[1] = level.intermission_origin[1]*8;
39 	ent->client->ps.pmove.origin[2] = level.intermission_origin[2]*8;
40 	VectorCopy (level.intermission_angle, ent->client->ps.viewangles);
41 	ent->client->ps.pmove.pm_type = PM_FREEZE;
42 	ent->client->ps.gunindex = 0;
43 	ent->client->ps.blend[3] = 0;
44 	ent->client->ps.rdflags &= ~RDF_UNDERWATER;
45 
46 	// clean up powerup info
47 	ent->client->quad_framenum = 0;
48 	ent->client->invincible_framenum = 0;
49 	ent->client->breather_framenum = 0;
50 	ent->client->enviro_framenum = 0;
51 	ent->client->grenade_blew_up = false;
52 	ent->client->grenade_time = 0;
53 
54 	ent->viewheight = 0;
55 	ent->s.modelindex = 0;
56 	ent->s.modelindex2 = 0;
57 	ent->s.modelindex3 = 0;
58 	ent->s.modelindex = 0;
59 	ent->s.effects = 0;
60 	ent->s.sound = 0;
61 	ent->solid = SOLID_NOT;
62 
63 	// add the layout
64 
65 	if (deathmatch->value || coop->value)
66 	{
67 		DeathmatchScoreboardMessage (ent, NULL);
68 		gi.unicast (ent, true);
69 	}
70 
71 }
72 
BeginIntermission(edict_t * targ)73 void BeginIntermission (edict_t *targ)
74 {
75 	int		i, n;
76 	edict_t	*ent, *client;
77 
78 	if (level.intermissiontime)
79 		return;		// allready activated
80 
81 //ZOID
82 	if (deathmatch->value && ctf->value)
83 		CTFCalcScores();
84 //ZOID
85 
86 	game.autosaved = false;
87 
88 	// respawn any dead clients
89 	for (i=0 ; i<maxclients->value ; i++)
90 	{
91 		client = g_edicts + 1 + i;
92 		if (!client->inuse)
93 			continue;
94 		if (client->health <= 0)
95 			respawn(client);
96 	}
97 
98 	level.intermissiontime = level.time;
99 	level.changemap = targ->map;
100 
101 	if (strstr(level.changemap, "*"))
102 	{
103 		if (coop->value)
104 		{
105 			for (i=0 ; i<maxclients->value ; i++)
106 			{
107 				client = g_edicts + 1 + i;
108 				if (!client->inuse)
109 					continue;
110 				// strip players of all keys between units
111 				for (n = 0; n < MAX_ITEMS; n++)
112 				{
113 					if (itemlist[n].flags & IT_KEY)
114 						client->client->pers.inventory[n] = 0;
115 				}
116 			}
117 		}
118 	}
119 	else
120 	{
121 		if (!deathmatch->value)
122 		{
123 			level.exitintermission = 1;		// go immediately to the next level
124 			return;
125 		}
126 	}
127 
128 	level.exitintermission = 0;
129 
130 	// find an intermission spot
131 	ent = G_Find (NULL, FOFS(classname), "info_player_intermission");
132 	if (!ent)
133 	{	// the map creator forgot to put in an intermission point...
134 		ent = G_Find (NULL, FOFS(classname), "info_player_start");
135 		if (!ent)
136 			ent = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
137 	}
138 	else
139 	{	// chose one of four spots
140 		i = rand() & 3;
141 		while (i--)
142 		{
143 			ent = G_Find (ent, FOFS(classname), "info_player_intermission");
144 			if (!ent)	// wrap around the list
145 				ent = G_Find (ent, FOFS(classname), "info_player_intermission");
146 		}
147 	}
148 
149 	VectorCopy (ent->s.origin, level.intermission_origin);
150 	VectorCopy (ent->s.angles, level.intermission_angle);
151 
152 	// move all clients to the intermission point
153 	for (i=0 ; i<maxclients->value ; i++)
154 	{
155 		client = g_edicts + 1 + i;
156 		if (!client->inuse)
157 			continue;
158 		MoveClientToIntermission (client);
159 	}
160 }
161 
162 
163 /*
164 ==================
165 DeathmatchScoreboardMessage
166 
167 ==================
168 */
DeathmatchScoreboardMessage(edict_t * ent,edict_t * killer)169 void DeathmatchScoreboardMessage (edict_t *ent, edict_t *killer)
170 {
171 	char	entry[1024];
172 	char	string[1400];
173 	int		stringlength;
174 	int		i, j, k;
175 	int		sorted[MAX_CLIENTS];
176 	int		sortedscores[MAX_CLIENTS];
177 	int		score, total;
178 	int		picnum;
179 	int		x, y;
180 	gclient_t	*cl;
181 	edict_t		*cl_ent;
182 	char	*tag;
183 
184 //ZOID
185 	if (ctf->value) {
186 		CTFScoreboardMessage (ent, killer);
187 		return;
188 	}
189 //ZOID
190 
191 	// sort the clients by score
192 	total = 0;
193 	for (i=0 ; i<game.maxclients ; i++)
194 	{
195 		cl_ent = g_edicts + 1 + i;
196 		if (!cl_ent->inuse)
197 			continue;
198 		score = game.clients[i].resp.score;
199 		for (j=0 ; j<total ; j++)
200 		{
201 			if (score > sortedscores[j])
202 				break;
203 		}
204 		for (k=total ; k>j ; k--)
205 		{
206 			sorted[k] = sorted[k-1];
207 			sortedscores[k] = sortedscores[k-1];
208 		}
209 		sorted[j] = i;
210 		sortedscores[j] = score;
211 		total++;
212 	}
213 
214 	// print level name and exit rules
215 	string[0] = 0;
216 
217 	stringlength = strlen(string);
218 
219 	// add the clients in sorted order
220 	if (total > 12)
221 		total = 12;
222 
223 	for (i=0 ; i<total ; i++)
224 	{
225 		cl = &game.clients[sorted[i]];
226 		cl_ent = g_edicts + 1 + sorted[i];
227 
228 		picnum = gi.imageindex ("i_fixme");
229 		x = (i>=6) ? 160 : 0;
230 		y = 32 + 32 * (i%6);
231 
232 		// add a dogtag
233 		if (cl_ent == ent)
234 			tag = "tag1";
235 		else if (cl_ent == killer)
236 			tag = "tag2";
237 		else
238 			tag = NULL;
239 		if (tag)
240 		{
241 			Com_sprintf (entry, sizeof(entry),
242 				"xv %i yv %i picn %s ",x+32, y, tag);
243 			j = strlen(entry);
244 			if (stringlength + j > 1024)
245 				break;
246 			strcpy (string + stringlength, entry);
247 			stringlength += j;
248 		}
249 
250 		// send the layout
251 		Com_sprintf (entry, sizeof(entry),
252 			"client %i %i %i %i %i %i ",
253 			x, y, sorted[i], cl->resp.score, cl->ping, (level.framenum - cl->resp.enterframe)/600);
254 		j = strlen(entry);
255 		if (stringlength + j > 1024)
256 			break;
257 		strcpy (string + stringlength, entry);
258 		stringlength += j;
259 	}
260 
261 	gi.WriteByte (svc_layout);
262 	gi.WriteString (string);
263 }
264 
265 
266 /*
267 ==================
268 DeathmatchScoreboard
269 
270 Draw instead of help message.
271 Note that it isn't that hard to overflow the 1400 byte message limit!
272 ==================
273 */
DeathmatchScoreboard(edict_t * ent)274 void DeathmatchScoreboard (edict_t *ent)
275 {
276 	DeathmatchScoreboardMessage (ent, ent->enemy);
277 	gi.unicast (ent, true);
278 }
279 
280 
281 /*
282 ==================
283 Cmd_Score_f
284 
285 Display the scoreboard
286 ==================
287 */
Cmd_Score_f(edict_t * ent)288 void Cmd_Score_f (edict_t *ent)
289 {
290 	ent->client->showinventory = false;
291 	ent->client->showhelp = false;
292 //ZOID
293 	if (ent->client->menu)
294 		PMenu_Close(ent);
295 //ZOID
296 
297 	if (!deathmatch->value && !coop->value)
298 		return;
299 
300 	if (ent->client->showscores)
301 	{
302 		ent->client->showscores = false;
303 		ent->client->update_chase = true;
304 		return;
305 	}
306 
307 	ent->client->showscores = true;
308 
309 	DeathmatchScoreboard (ent);
310 }
311 
312 
313 /*
314 ==================
315 HelpComputer
316 
317 Draw help computer.
318 ==================
319 */
HelpComputer(edict_t * ent)320 void HelpComputer (edict_t *ent)
321 {
322 	char	string[1024];
323 	char	*sk;
324 
325 	if (skill->value == 0)
326 		sk = "easy";
327 	else if (skill->value == 1)
328 		sk = "medium";
329 	else if (skill->value == 2)
330 		sk = "hard";
331 	else
332 		sk = "hard+";
333 
334 	// send the layout
335 	Com_sprintf (string, sizeof(string),
336 		"xv 32 yv 8 picn help "			// background
337 		"xv 202 yv 12 string2 \"%s\" "		// skill
338 		"xv 0 yv 24 cstring2 \"%s\" "		// level name
339 		"xv 0 yv 54 cstring2 \"%s\" "		// help 1
340 		"xv 0 yv 110 cstring2 \"%s\" "		// help 2
341 		"xv 50 yv 164 string2 \" kills     goals    secrets\" "
342 		"xv 50 yv 172 string2 \"%3i/%3i     %i/%i       %i/%i\" ",
343 		sk,
344 		level.level_name,
345 		game.helpmessage1,
346 		game.helpmessage2,
347 		level.killed_monsters, level.total_monsters,
348 		level.found_goals, level.total_goals,
349 		level.found_secrets, level.total_secrets);
350 
351 	gi.WriteByte (svc_layout);
352 	gi.WriteString (string);
353 	gi.unicast (ent, true);
354 }
355 
356 
357 /*
358 ==================
359 Cmd_Help_f
360 
361 Display the current help message
362 ==================
363 */
Cmd_Help_f(edict_t * ent)364 void Cmd_Help_f (edict_t *ent)
365 {
366 	// this is for backwards compatability
367 	if (deathmatch->value)
368 	{
369 		Cmd_Score_f (ent);
370 		return;
371 	}
372 
373 	ent->client->showinventory = false;
374 	ent->client->showscores = false;
375 
376 	if (ent->client->showhelp && (ent->client->resp.game_helpchanged == game.helpchanged))
377 	{
378 		ent->client->showhelp = false;
379 		return;
380 	}
381 
382 	ent->client->showhelp = true;
383 	ent->client->resp.helpchanged = 0;
384 	HelpComputer (ent);
385 }
386 
387 
388 //=======================================================================
389 
390 /*
391 ===============
392 G_SetStats
393 ===============
394 */
G_SetStats(edict_t * ent)395 void G_SetStats (edict_t *ent)
396 {
397 	gitem_t		*item;
398 	int			index, cells;
399 	int			power_armor_type;
400 
401 	//
402 	// health
403 	//
404 	ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health;
405 	ent->client->ps.stats[STAT_HEALTH] = ent->health;
406 
407 	//
408 	// ammo
409 	//
410 	if (!ent->client->ammo_index /* || !ent->client->pers.inventory[ent->client->ammo_index] */)
411 	{
412 		ent->client->ps.stats[STAT_AMMO_ICON] = 0;
413 		ent->client->ps.stats[STAT_AMMO] = 0;
414 	}
415 	else
416 	{
417 		item = &itemlist[ent->client->ammo_index];
418 		ent->client->ps.stats[STAT_AMMO_ICON] = gi.imageindex (item->icon);
419 		ent->client->ps.stats[STAT_AMMO] = ent->client->pers.inventory[ent->client->ammo_index];
420 	}
421 
422 	cells = 0;
423 
424 	//
425 	// armor
426 	//
427 	power_armor_type = PowerArmorType (ent);
428 	if (power_armor_type)
429 	{
430 		cells = ent->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))];
431 		if (cells == 0)
432 		{	// ran out of cells for power armor
433 			ent->flags &= ~FL_POWER_ARMOR;
434 			gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0);
435 			power_armor_type = 0;;
436 		}
437 	}
438 
439 	index = ArmorIndex (ent);
440 	if (power_armor_type && (!index || (level.framenum & 8) ) )
441 	{	// flash between power armor and other armor icon
442 		ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex ("i_powershield");
443 		ent->client->ps.stats[STAT_ARMOR] = cells;
444 	}
445 	else if (index)
446 	{
447 		item = GetItemByIndex (index);
448 		ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex (item->icon);
449 		ent->client->ps.stats[STAT_ARMOR] = ent->client->pers.inventory[index];
450 	}
451 	else
452 	{
453 		ent->client->ps.stats[STAT_ARMOR_ICON] = 0;
454 		ent->client->ps.stats[STAT_ARMOR] = 0;
455 	}
456 
457 	//
458 	// pickup message
459 	//
460 	if (level.time > ent->client->pickup_msg_time)
461 	{
462 		ent->client->ps.stats[STAT_PICKUP_ICON] = 0;
463 		ent->client->ps.stats[STAT_PICKUP_STRING] = 0;
464 	}
465 
466 	//
467 	// timers
468 	//
469 	if (ent->client->quad_framenum > level.framenum)
470 	{
471 		ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_quad");
472 		ent->client->ps.stats[STAT_TIMER] = (ent->client->quad_framenum - level.framenum)/10;
473 	}
474 	else if (ent->client->invincible_framenum > level.framenum)
475 	{
476 		ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_invulnerability");
477 		ent->client->ps.stats[STAT_TIMER] = (ent->client->invincible_framenum - level.framenum)/10;
478 	}
479 	else if (ent->client->enviro_framenum > level.framenum)
480 	{
481 		ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_envirosuit");
482 		ent->client->ps.stats[STAT_TIMER] = (ent->client->enviro_framenum - level.framenum)/10;
483 	}
484 	else if (ent->client->breather_framenum > level.framenum)
485 	{
486 		ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_rebreather");
487 		ent->client->ps.stats[STAT_TIMER] = (ent->client->breather_framenum - level.framenum)/10;
488 	}
489 	else
490 	{
491 		ent->client->ps.stats[STAT_TIMER_ICON] = 0;
492 		ent->client->ps.stats[STAT_TIMER] = 0;
493 	}
494 
495 	//
496 	// selected item
497 	//
498 	if (ent->client->pers.selected_item == -1)
499 		ent->client->ps.stats[STAT_SELECTED_ICON] = 0;
500 	else
501 		ent->client->ps.stats[STAT_SELECTED_ICON] = gi.imageindex (itemlist[ent->client->pers.selected_item].icon);
502 
503 	ent->client->ps.stats[STAT_SELECTED_ITEM] = ent->client->pers.selected_item;
504 
505 	//
506 	// layouts
507 	//
508 	ent->client->ps.stats[STAT_LAYOUTS] = 0;
509 
510 	if (deathmatch->value)
511 	{
512 		if (ent->client->pers.health <= 0 || level.intermissiontime
513 			|| ent->client->showscores)
514 			ent->client->ps.stats[STAT_LAYOUTS] |= 1;
515 		if (ent->client->showinventory && ent->client->pers.health > 0)
516 			ent->client->ps.stats[STAT_LAYOUTS] |= 2;
517 	}
518 	else
519 	{
520 		if (ent->client->showscores || ent->client->showhelp)
521 			ent->client->ps.stats[STAT_LAYOUTS] |= 1;
522 		if (ent->client->showinventory && ent->client->pers.health > 0)
523 			ent->client->ps.stats[STAT_LAYOUTS] |= 2;
524 	}
525 
526 	//
527 	// frags
528 	//
529 	ent->client->ps.stats[STAT_FRAGS] = ent->client->resp.score;
530 
531 	//
532 	// help icon / current weapon if not shown
533 	//
534 	if (ent->client->resp.helpchanged && (level.framenum&8) )
535 		ent->client->ps.stats[STAT_HELPICON] = gi.imageindex ("i_help");
536 	else if ( (ent->client->pers.hand == CENTER_HANDED || ent->client->ps.fov > 91)
537 		&& ent->client->pers.weapon)
538 		ent->client->ps.stats[STAT_HELPICON] = gi.imageindex (ent->client->pers.weapon->icon);
539 	else
540 		ent->client->ps.stats[STAT_HELPICON] = 0;
541 
542 //ZOID
543 	SetCTFStats(ent);
544 //ZOID
545 }
546 
547