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