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; // already activated
80
81 game.autosaved = false;
82
83 // respawn any dead clients
84 for (i=0 ; i<maxclients->value ; i++)
85 {
86 client = g_edicts + 1 + i;
87 if (!client->inuse)
88 continue;
89 if (client->health <= 0)
90 respawn(client);
91 }
92
93 level.intermissiontime = level.time;
94 level.changemap = targ->map;
95
96 if (strstr(level.changemap, "*"))
97 {
98 if (coop->value)
99 {
100 for (i=0 ; i<maxclients->value ; i++)
101 {
102 client = g_edicts + 1 + i;
103 if (!client->inuse)
104 continue;
105 // strip players of all keys between units
106 for (n = 0; n < MAX_ITEMS; n++)
107 {
108 if (itemlist[n].flags & IT_KEY)
109 client->client->pers.inventory[n] = 0;
110 }
111 }
112 }
113 }
114 else
115 {
116 if (!deathmatch->value)
117 {
118 level.exitintermission = 1; // go immediately to the next level
119 return;
120 }
121 }
122
123 level.exitintermission = 0;
124
125 // find an intermission spot
126 ent = G_Find (NULL, FOFS(classname), "info_player_intermission");
127 if (!ent)
128 { // the map creator forgot to put in an intermission point...
129 ent = G_Find (NULL, FOFS(classname), "info_player_start");
130 if (!ent)
131 ent = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
132 }
133 else
134 { // chose one of four spots
135 i = rand() & 3;
136 while (i--)
137 {
138 ent = G_Find (ent, FOFS(classname), "info_player_intermission");
139 if (!ent) // wrap around the list
140 ent = G_Find (ent, FOFS(classname), "info_player_intermission");
141 }
142 }
143
144 VectorCopy (ent->s.origin, level.intermission_origin);
145 VectorCopy (ent->s.angles, level.intermission_angle);
146
147 // move all clients to the intermission point
148 for (i=0 ; i<maxclients->value ; i++)
149 {
150 client = g_edicts + 1 + i;
151 if (!client->inuse)
152 continue;
153 MoveClientToIntermission (client);
154 }
155 }
156
157
158 /*
159 ==================
160 DeathmatchScoreboardMessage
161
162 ==================
163 */
DeathmatchScoreboardMessage(edict_t * ent,edict_t * killer)164 void DeathmatchScoreboardMessage (edict_t *ent, edict_t *killer)
165 {
166 char entry[1024];
167 char string[1400];
168 int stringlength;
169 int i, j, k;
170 int sorted[MAX_CLIENTS];
171 int sortedscores[MAX_CLIENTS];
172 int score, total;
173 int picnum;
174 int x, y;
175 gclient_t *cl;
176 edict_t *cl_ent;
177 char *tag;
178
179 // sort the clients by score
180 total = 0;
181 for (i=0 ; i<game.maxclients ; i++)
182 {
183 cl_ent = g_edicts + 1 + i;
184 if (!cl_ent->inuse || game.clients[i].resp.spectator)
185 continue;
186 score = game.clients[i].resp.score;
187 for (j=0 ; j<total ; j++)
188 {
189 if (score > sortedscores[j])
190 break;
191 }
192 for (k=total ; k>j ; k--)
193 {
194 sorted[k] = sorted[k-1];
195 sortedscores[k] = sortedscores[k-1];
196 }
197 sorted[j] = i;
198 sortedscores[j] = score;
199 total++;
200 }
201
202 // print level name and exit rules
203 string[0] = 0;
204
205 stringlength = strlen(string);
206
207 // add the clients in sorted order
208 if (total > 12)
209 total = 12;
210
211 for (i=0 ; i<total ; i++)
212 {
213 cl = &game.clients[sorted[i]];
214 cl_ent = g_edicts + 1 + sorted[i];
215
216 picnum = gi.imageindex ("i_fixme");
217 x = (i>=6) ? 160 : 0;
218 y = 32 + 32 * (i%6);
219
220 // add a dogtag
221 if (cl_ent == ent)
222 tag = "tag1";
223 else if (cl_ent == killer)
224 tag = "tag2";
225 else
226 tag = NULL;
227 if (tag)
228 {
229 Com_sprintf (entry, sizeof(entry),
230 "xv %i yv %i picn %s ",x+32, y, tag);
231 j = strlen(entry);
232 if (stringlength + j > 1024)
233 break;
234 strcpy (string + stringlength, entry);
235 stringlength += j;
236 }
237
238 // send the layout
239 Com_sprintf (entry, sizeof(entry),
240 "client %i %i %i %i %i %i ",
241 x, y, sorted[i], cl->resp.score, cl->ping, (level.framenum - cl->resp.enterframe)/600);
242 j = strlen(entry);
243 if (stringlength + j > 1024)
244 break;
245 strcpy (string + stringlength, entry);
246 stringlength += j;
247 }
248
249 gi.WriteByte (svc_layout);
250 gi.WriteString (string);
251 }
252
253
254 /*
255 ==================
256 DeathmatchScoreboard
257
258 Draw instead of help message.
259 Note that it isn't that hard to overflow the 1400 byte message limit!
260 ==================
261 */
DeathmatchScoreboard(edict_t * ent)262 void DeathmatchScoreboard (edict_t *ent)
263 {
264 DeathmatchScoreboardMessage (ent, ent->enemy);
265 gi.unicast (ent, true);
266 }
267
268
269 /*
270 ==================
271 Cmd_Score_f
272
273 Display the scoreboard
274 ==================
275 */
Cmd_Score_f(edict_t * ent)276 void Cmd_Score_f (edict_t *ent)
277 {
278 ent->client->showinventory = false;
279 ent->client->showhelp = false;
280
281 if (!deathmatch->value && !coop->value)
282 return;
283
284 if (ent->client->showscores)
285 {
286 ent->client->showscores = false;
287 return;
288 }
289
290 ent->client->showscores = true;
291 DeathmatchScoreboard (ent);
292 }
293
294
295 /*
296 ==================
297 HelpComputer
298
299 Draw help computer.
300 ==================
301 */
HelpComputer(edict_t * ent)302 void HelpComputer (edict_t *ent)
303 {
304 char string[1024];
305 char *sk;
306
307 if (skill->value == 0)
308 sk = "easy";
309 else if (skill->value == 1)
310 sk = "medium";
311 else if (skill->value == 2)
312 sk = "hard";
313 else
314 sk = "hard+";
315
316 // send the layout
317 Com_sprintf (string, sizeof(string),
318 "xv 32 yv 8 picn help " // background
319 "xv 202 yv 12 string2 \"%s\" " // skill
320 "xv 0 yv 24 cstring2 \"%s\" " // level name
321 "xv 0 yv 54 cstring2 \"%s\" " // help 1
322 "xv 0 yv 110 cstring2 \"%s\" " // help 2
323 "xv 50 yv 164 string2 \" kills goals secrets\" "
324 "xv 50 yv 172 string2 \"%3i/%3i %i/%i %i/%i\" ",
325 sk,
326 level.level_name,
327 game.helpmessage1,
328 game.helpmessage2,
329 level.killed_monsters, level.total_monsters,
330 level.found_goals, level.total_goals,
331 level.found_secrets, level.total_secrets);
332
333 gi.WriteByte (svc_layout);
334 gi.WriteString (string);
335 gi.unicast (ent, true);
336 }
337
338
339 /*
340 ==================
341 Cmd_Help_f
342
343 Display the current help message
344 ==================
345 */
Cmd_Help_f(edict_t * ent)346 void Cmd_Help_f (edict_t *ent)
347 {
348 // this is for backwards compatability
349 if (deathmatch->value)
350 {
351 Cmd_Score_f (ent);
352 return;
353 }
354
355 ent->client->showinventory = false;
356 ent->client->showscores = false;
357
358 if (ent->client->showhelp && (ent->client->pers.game_helpchanged == game.helpchanged))
359 {
360 ent->client->showhelp = false;
361 return;
362 }
363
364 ent->client->showhelp = true;
365 ent->client->pers.helpchanged = 0;
366 HelpComputer (ent);
367 }
368
369
370 //=======================================================================
371
372 /*
373 ===============
374 G_SetStats
375 ===============
376 */
G_SetStats(edict_t * ent)377 void G_SetStats (edict_t *ent)
378 {
379 gitem_t *item;
380 int index, cells;
381 int power_armor_type;
382
383 //
384 // health
385 //
386 ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health;
387 ent->client->ps.stats[STAT_HEALTH] = ent->health;
388
389 //
390 // ammo
391 //
392 if (!ent->client->ammo_index /* || !ent->client->pers.inventory[ent->client->ammo_index] */)
393 {
394 ent->client->ps.stats[STAT_AMMO_ICON] = 0;
395 ent->client->ps.stats[STAT_AMMO] = 0;
396 }
397 else
398 {
399 item = &itemlist[ent->client->ammo_index];
400 ent->client->ps.stats[STAT_AMMO_ICON] = gi.imageindex (item->icon);
401 ent->client->ps.stats[STAT_AMMO] = ent->client->pers.inventory[ent->client->ammo_index];
402 }
403
404 cells = 0;
405
406 //
407 // armor
408 //
409 power_armor_type = PowerArmorType (ent);
410 if (power_armor_type)
411 {
412 cells = ent->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))];
413 if (cells == 0)
414 { // ran out of cells for power armor
415 ent->flags &= ~FL_POWER_ARMOR;
416 gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0);
417 power_armor_type = 0;;
418 }
419 }
420
421 index = ArmorIndex (ent);
422 if (power_armor_type && (!index || (level.framenum & 8) ) )
423 { // flash between power armor and other armor icon
424 ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex ("i_powershield");
425 ent->client->ps.stats[STAT_ARMOR] = cells;
426 }
427 else if (index)
428 {
429 item = GetItemByIndex (index);
430 ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex (item->icon);
431 ent->client->ps.stats[STAT_ARMOR] = ent->client->pers.inventory[index];
432 }
433 else
434 {
435 ent->client->ps.stats[STAT_ARMOR_ICON] = 0;
436 ent->client->ps.stats[STAT_ARMOR] = 0;
437 }
438
439 //
440 // pickup message
441 //
442 if (level.time > ent->client->pickup_msg_time)
443 {
444 ent->client->ps.stats[STAT_PICKUP_ICON] = 0;
445 ent->client->ps.stats[STAT_PICKUP_STRING] = 0;
446 }
447
448 //
449 // timers
450 //
451 if (ent->client->quad_framenum > level.framenum)
452 {
453 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_quad");
454 ent->client->ps.stats[STAT_TIMER] = (ent->client->quad_framenum - level.framenum)/10;
455 }
456 else if (ent->client->invincible_framenum > level.framenum)
457 {
458 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_invulnerability");
459 ent->client->ps.stats[STAT_TIMER] = (ent->client->invincible_framenum - level.framenum)/10;
460 }
461 else if (ent->client->enviro_framenum > level.framenum)
462 {
463 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_envirosuit");
464 ent->client->ps.stats[STAT_TIMER] = (ent->client->enviro_framenum - level.framenum)/10;
465 }
466 else if (ent->client->breather_framenum > level.framenum)
467 {
468 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_rebreather");
469 ent->client->ps.stats[STAT_TIMER] = (ent->client->breather_framenum - level.framenum)/10;
470 }
471 else
472 {
473 ent->client->ps.stats[STAT_TIMER_ICON] = 0;
474 ent->client->ps.stats[STAT_TIMER] = 0;
475 }
476
477 //
478 // selected item
479 //
480 if (ent->client->pers.selected_item == -1)
481 ent->client->ps.stats[STAT_SELECTED_ICON] = 0;
482 else
483 ent->client->ps.stats[STAT_SELECTED_ICON] = gi.imageindex (itemlist[ent->client->pers.selected_item].icon);
484
485 ent->client->ps.stats[STAT_SELECTED_ITEM] = ent->client->pers.selected_item;
486
487 //
488 // layouts
489 //
490 ent->client->ps.stats[STAT_LAYOUTS] = 0;
491
492 if (deathmatch->value)
493 {
494 if (ent->client->pers.health <= 0 || level.intermissiontime
495 || ent->client->showscores)
496 ent->client->ps.stats[STAT_LAYOUTS] |= 1;
497 if (ent->client->showinventory && ent->client->pers.health > 0)
498 ent->client->ps.stats[STAT_LAYOUTS] |= 2;
499 }
500 else
501 {
502 if (ent->client->showscores || ent->client->showhelp)
503 ent->client->ps.stats[STAT_LAYOUTS] |= 1;
504 if (ent->client->showinventory && ent->client->pers.health > 0)
505 ent->client->ps.stats[STAT_LAYOUTS] |= 2;
506 }
507
508 //
509 // frags
510 //
511 ent->client->ps.stats[STAT_FRAGS] = ent->client->resp.score;
512
513 //
514 // help icon / current weapon if not shown
515 //
516 if (ent->client->pers.helpchanged && (level.framenum&8) )
517 ent->client->ps.stats[STAT_HELPICON] = gi.imageindex ("i_help");
518 else if ( (ent->client->pers.hand == CENTER_HANDED || ent->client->ps.fov > 91)
519 && ent->client->pers.weapon)
520 ent->client->ps.stats[STAT_HELPICON] = gi.imageindex (ent->client->pers.weapon->icon);
521 else
522 ent->client->ps.stats[STAT_HELPICON] = 0;
523
524 ent->client->ps.stats[STAT_SPECTATOR] = 0;
525 }
526
527 /*
528 ===============
529 G_CheckChaseStats
530 ===============
531 */
G_CheckChaseStats(edict_t * ent)532 void G_CheckChaseStats (edict_t *ent)
533 {
534 int i;
535 gclient_t *cl;
536
537 for (i = 1; i <= maxclients->value; i++) {
538 cl = g_edicts[i].client;
539 if (!g_edicts[i].inuse || cl->chase_target != ent)
540 continue;
541 memcpy(cl->ps.stats, ent->client->ps.stats, sizeof(cl->ps.stats));
542 G_SetSpectatorStats(g_edicts + i);
543 }
544 }
545
546 /*
547 ===============
548 G_SetSpectatorStats
549 ===============
550 */
G_SetSpectatorStats(edict_t * ent)551 void G_SetSpectatorStats (edict_t *ent)
552 {
553 gclient_t *cl = ent->client;
554
555 if (!cl->chase_target)
556 G_SetStats (ent);
557
558 cl->ps.stats[STAT_SPECTATOR] = 1;
559
560 // layouts are independant in spectator
561 cl->ps.stats[STAT_LAYOUTS] = 0;
562 if (cl->pers.health <= 0 || level.intermissiontime || cl->showscores)
563 cl->ps.stats[STAT_LAYOUTS] |= 1;
564 if (cl->showinventory && cl->pers.health > 0)
565 cl->ps.stats[STAT_LAYOUTS] |= 2;
566
567 if (cl->chase_target && cl->chase_target->inuse)
568 cl->ps.stats[STAT_CHASE] = CS_PLAYERSKINS +
569 (cl->chase_target - g_edicts) - 1;
570 else
571 cl->ps.stats[STAT_CHASE] = 0;
572 }
573
574