1 #include "g_local.h"
2
3
4
5 /*
6 ======================================================================
7
8 INTERMISSION
9
10 ======================================================================
11 */
12
MoveClientToIntermission(edict_t * ent)13 void MoveClientToIntermission (edict_t *ent)
14 {
15 if (deathmatch->value || coop->value)
16 ent->client->showscores = true;
17 VectorCopy (level.intermission_origin, ent->s.origin);
18 ent->client->ps.pmove.origin[0] = level.intermission_origin[0]*8;
19 ent->client->ps.pmove.origin[1] = level.intermission_origin[1]*8;
20 ent->client->ps.pmove.origin[2] = level.intermission_origin[2]*8;
21 VectorCopy (level.intermission_angle, ent->client->ps.viewangles);
22 ent->client->ps.pmove.pm_type = PM_FREEZE;
23 ent->client->ps.gunindex = 0;
24 ent->client->ps.blend[3] = 0;
25 ent->client->ps.rdflags &= ~RDF_UNDERWATER;
26
27 // clean up powerup info
28 ent->client->quad_framenum = 0;
29 ent->client->invincible_framenum = 0;
30 ent->client->breather_framenum = 0;
31 ent->client->enviro_framenum = 0;
32 ent->client->grenade_blew_up = false;
33 ent->client->grenade_time = 0;
34 ent->client->speed_framenum = 0;
35 ent->pinned_nextthink = 0;
36 ent->client->ir_framenum =0;
37 ent->client->cloak_framenum = level.framenum +1;
38 ent->bullet_framenum = 0;
39 matrix.lightsout_framenum = level.framenum +1;
40
41
42 ent->viewheight = 0;
43 ent->s.modelindex = 0;
44 ent->s.modelindex2 = 0;
45 ent->s.modelindex3 = 0;
46 ent->s.modelindex = 0;
47 ent->s.effects = 0;
48 ent->s.sound = 0;
49 ent->solid = SOLID_NOT;
50
51 // add the layout
52 //gi.sound(ent, CHAN_AUTO, gi.soundindex ("endlevel.wav"), 1, ATTN_NORM, 0);
53 gi.positioned_sound (level.intermission_origin, g_edicts, CHAN_NO_PHS_ADD, gi.soundindex("endlevel.wav"), 1, ATTN_NORM, 0);
54
55 if (deathmatch->value || coop->value)
56 {
57 DeathmatchScoreboardMessage (ent, NULL);
58 gi.unicast (ent, true);
59 }
60
61 }
62
BeginIntermission(edict_t * targ)63 void BeginIntermission (edict_t *targ)
64 {
65 int i, n;
66 edict_t *ent, *client;
67
68 if (level.intermissiontime)
69 return; // already activated
70
71 game.autosaved = false;
72
73 // respawn any dead clients
74 for (i=0 ; i<maxclients->value ; i++)
75 {
76 client = g_edicts + 1 + i;
77 if (!client->inuse)
78 continue;
79 //SKULL
80 //Save it off over level changes
81 client->client->pers.chasetoggle = client->client->chasetoggle;
82 //END
83 if (client->health <= 0)
84 respawn(client);
85 }
86
87 level.intermissiontime = level.time;
88 level.changemap = targ->map;
89
90 if (strstr(level.changemap, "*"))
91 {
92 if (coop->value)
93 {
94 for (i=0 ; i<maxclients->value ; i++)
95 {
96 client = g_edicts + 1 + i;
97 if (!client->inuse)
98 continue;
99 // strip players of all keys between units
100 for (n = 0; n < MAX_ITEMS; n++)
101 {
102 if (itemlist[n].flags & IT_KEY)
103 client->client->pers.inventory[n] = 0;
104 }
105 }
106 }
107 }
108 else
109 {
110 if (!deathmatch->value)
111 {
112 level.exitintermission = 1; // go immediately to the next level
113 return;
114 }
115 }
116
117 level.exitintermission = 0;
118
119 // find an intermission spot
120 ent = G_Find (NULL, FOFS(classname), "info_player_intermission");
121 if (!ent)
122 { // the map creator forgot to put in an intermission point...
123 ent = G_Find (NULL, FOFS(classname), "info_player_start");
124 if (!ent)
125 ent = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
126 }
127 else
128 { // chose one of four spots
129 i = rand() & 3;
130 while (i--)
131 {
132 ent = G_Find (ent, FOFS(classname), "info_player_intermission");
133 if (!ent) // wrap around the list
134 ent = G_Find (ent, FOFS(classname), "info_player_intermission");
135 }
136 }
137
138 VectorCopy (ent->s.origin, level.intermission_origin);
139 VectorCopy (ent->s.angles, level.intermission_angle);
140
141 // move all clients to the intermission point
142 for (i=0 ; i<maxclients->value ; i++)
143 {
144 client = g_edicts + 1 + i;
145 if (!client->inuse)
146 continue;
147 MoveClientToIntermission (client);
148 }
149 }
150
151
152 /*
153 ==================
154 DeathmatchScoreboardMessage
155
156 ==================
157 */
DeathmatchScoreboardMessage(edict_t * ent,edict_t * killer)158 void DeathmatchScoreboardMessage (edict_t *ent, edict_t *killer)
159 {
160 char entry[1024];
161 char string[1400];
162 int stringlength;
163 int i, j, k;
164 int sorted[MAX_CLIENTS];
165 int sortedscores[MAX_CLIENTS];
166 int score, total;
167 int picnum;
168 int x, y;
169 gclient_t *cl;
170 edict_t *cl_ent;
171 char *tag;
172
173 // sort the clients by score
174 total = 0;
175 for (i=0 ; i<game.maxclients ; i++)
176 {
177 cl_ent = g_edicts + 1 + i;
178 if (!cl_ent->inuse || game.clients[i].resp.spectator)
179 continue;
180 score = game.clients[i].resp.score;
181 for (j=0 ; j<total ; j++)
182 {
183 if (score > sortedscores[j])
184 break;
185 }
186 for (k=total ; k>j ; k--)
187 {
188 sorted[k] = sorted[k-1];
189 sortedscores[k] = sortedscores[k-1];
190 }
191 sorted[j] = i;
192 sortedscores[j] = score;
193 total++;
194 }
195
196 // print level name and exit rules
197 string[0] = 0;
198
199 stringlength = strlen(string);
200
201 // add the clients in sorted order
202 if (total > 12)
203 total = 12;
204
205 for (i=0 ; i<total ; i++)
206 {
207 cl = &game.clients[sorted[i]];
208 cl_ent = g_edicts + 1 + sorted[i];
209
210 picnum = gi.imageindex ("i_fixme");
211 x = (i>=6) ? 160 : 0;
212 y = 32 + 32 * (i%6);
213
214 // add a dogtag
215 if (cl_ent == ent)
216 tag = "tag1";
217 else if (cl_ent == killer)
218 tag = "tag2";
219 else
220 tag = NULL;
221 if (tag)
222 {
223 Com_sprintf (entry, sizeof(entry),
224 "xv %i yv %i picn %s ",x+32, y, tag);
225 j = strlen(entry);
226 if (stringlength + j > 1024)
227 break;
228 strcpy (string + stringlength, entry);
229 stringlength += j;
230 }
231
232 // send the layout
233 Com_sprintf (entry, sizeof(entry),
234 "client %i %i %i %i %i %i ",
235 x, y, sorted[i], cl->resp.score, cl->ping, (level.framenum - cl->resp.enterframe)/600);
236 j = strlen(entry);
237 if (stringlength + j > 1024)
238 break;
239 strcpy (string + stringlength, entry);
240 stringlength += j;
241 }
242
243 gi.WriteByte (svc_layout);
244 gi.WriteString (string);
245 }
246
247
248 /*
249 ==================
250 DeathmatchScoreboard
251
252 Draw instead of help message.
253 Note that it isn't that hard to overflow the 1400 byte message limit!
254 ==================
255 */
DeathmatchScoreboard(edict_t * ent)256 void DeathmatchScoreboard (edict_t *ent)
257 {
258 if (!teamplay->value)
259 DeathmatchScoreboardMessage (ent, ent->enemy);
260 else
261 TeamplayScoreboardMessage (ent, ent->enemy);
262 gi.unicast (ent, true);
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 = false;
276 ent->client->showhelp = false;
277
278 if (!deathmatch->value && !coop->value)
279 return;
280
281 if (ent->client->showscores)
282 {
283 ent->client->showscores = false;
284 return;
285 }
286
287 ent->client->showscores = true;
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->value == 0)
305 sk = "easy";
306 else if (skill->value == 1)
307 sk = "medium";
308 else if (skill->value == 2)
309 sk = "hard";
310 else
311 sk = "hard+";
312
313 // send the layout
314 Com_sprintf (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, true);
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->value)
347 {
348 Cmd_Score_f (ent);
349 return;
350 }
351
352 ent->client->showinventory = false;
353 ent->client->showscores = false;
354
355 if (ent->client->showhelp && (ent->client->pers.game_helpchanged == game.helpchanged))
356 {
357 ent->client->showhelp = false;
358 return;
359 }
360
361 ent->client->showhelp = true;
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 int i;
380 //
381 // health
382 //
383 if(ent->health <= 0)
384 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h1");
385 if(ent->health > 0)
386 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h1");
387 if(ent->health > 11)
388 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h2");
389 if(ent->health > 16)
390 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h3");
391 if(ent->health > 22)
392 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h4");
393 if(ent->health > 27)
394 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h5");
395 if(ent->health > 33)
396 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h6");
397 if(ent->health > 38)
398 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h7");
399 if(ent->health > 44)
400 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h8");
401 if(ent->health > 49)
402 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h9");
403 if(ent->health > 55)
404 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h10");
405 if(ent->health > 60)
406 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h11");
407 if(ent->health > 66)
408 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h12");
409 if(ent->health > 71)
410 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h13");
411 if(ent->health > 77)
412 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h14");
413 if(ent->health > 82)
414 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h15");
415 if(ent->health > 88)
416 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h16");
417 if(ent->health > 93)
418 ent->client->ps.stats[STAT_HEALTH_ICON] = gi.imageindex ("h17");
419 if(ent->health > 99)
420 ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health;
421
422 ent->client->ps.stats[STAT_HEALTH] = ent->health;
423
424 //
425 // ammo
426 //
427 if (!ent->client->ammo_index /* || !ent->client->pers.inventory[ent->client->ammo_index] */)
428 {
429 ent->client->ps.stats[STAT_AMMO_ICON] = 0;
430 ent->client->ps.stats[STAT_AMMO] = 0;
431 }
432 else
433 {
434 item = &itemlist[ent->client->ammo_index];
435 ent->client->ps.stats[STAT_AMMO_ICON] = gi.imageindex (item->icon);
436 ent->client->ps.stats[STAT_AMMO] = ent->client->pers.inventory[ent->client->ammo_index];
437 }
438
439 //
440 // armor
441 //
442 power_armor_type = PowerArmorType (ent);
443 if (power_armor_type)
444 {
445 cells = ent->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))];
446 if (cells == 0)
447 { // ran out of cells for power armor
448 ent->flags &= ~FL_POWER_ARMOR;
449 gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0);
450 power_armor_type = 0;;
451 }
452 }
453
454 index = ArmorIndex (ent);
455 if (power_armor_type && (!index || (level.framenum & 8) ) )
456 { // flash between power armor and other armor icon
457 ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex ("i_powershield");
458 ent->client->ps.stats[STAT_ARMOR] = cells;
459 }
460 else if (index)
461 {
462 item = GetItemByIndex (index);
463 ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex (item->icon);
464 ent->client->ps.stats[STAT_ARMOR] = ent->client->pers.inventory[index];
465 }
466 else
467 {
468 ent->client->ps.stats[STAT_ARMOR_ICON] = 0;
469 ent->client->ps.stats[STAT_ARMOR] = 0;
470 }
471
472 //
473 // pickup message
474 //
475 if (level.time > ent->client->pickup_msg_time)
476 {
477 ent->client->ps.stats[STAT_PICKUP_ICON] = 0;
478 ent->client->ps.stats[STAT_PICKUP_STRING] = 0;
479 }
480
481 //
482 // timers
483 //
484 if (ent->client->quad_framenum > level.framenum)
485 {
486 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_quad");
487 ent->client->ps.stats[STAT_TIMER] = (ent->client->quad_framenum - level.framenum)/10;
488 }
489 else if (ent->client->invincible_framenum > level.framenum)
490 {
491 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_invulnerability");
492 ent->client->ps.stats[STAT_TIMER] = (ent->client->invincible_framenum - level.framenum)/10;
493 }
494 else if (ent->client->enviro_framenum > level.framenum)
495 {
496 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_envirosuit");
497 ent->client->ps.stats[STAT_TIMER] = (ent->client->enviro_framenum - level.framenum)/10;
498 }
499 else if (ent->client->breather_framenum > level.framenum)
500 {
501 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_rebreather");
502 ent->client->ps.stats[STAT_TIMER] = (ent->client->breather_framenum - level.framenum)/10;
503 }
504 else
505 {
506 ent->client->ps.stats[STAT_TIMER_ICON] = 0;
507 ent->client->ps.stats[STAT_TIMER] = 0;
508 }
509
510 //
511 // selected item
512 //
513 if (ent->client->pers.selected_item == -1)
514 ent->client->ps.stats[STAT_SELECTED_ICON] = 0;
515 else
516 ent->client->ps.stats[STAT_SELECTED_ICON] = gi.imageindex (itemlist[ent->client->pers.selected_item].icon);
517
518 ent->client->ps.stats[STAT_SELECTED_ITEM] = ent->client->pers.selected_item;
519
520 //
521 // layouts
522 //
523 ent->client->ps.stats[STAT_LAYOUTS] = 0;
524 //ent->client->ps.stats[STAT_LAYOUTS] = 0;
525 if (deathmatch->value)
526 {
527 if (ent->client->pers.health <= 0 || level.intermissiontime
528 || ent->client->showscores)
529 ent->client->ps.stats[STAT_LAYOUTS] |= 1;
530 if (ent->client->showinventory && ent->client->pers.health > 0)
531 ent->client->ps.stats[STAT_LAYOUTS] |= 2;
532 }
533 else
534 {
535 if (ent->client->showscores || ent->client->showhelp)
536 ent->client->ps.stats[STAT_LAYOUTS] |= 1;
537 if (ent->client->showinventory && ent->client->pers.health > 0)
538 ent->client->ps.stats[STAT_LAYOUTS] |= 2;
539 }
540
541 //
542 // frags
543 //
544 ent->client->ps.stats[STAT_FRAGS] = ent->client->resp.score;
545
546 //
547 // help icon / current weapon if not shown
548 //
549 if (ent->client->pers.helpchanged && (level.framenum&8) )
550 ent->client->ps.stats[STAT_HELPICON] = gi.imageindex ("i_help");
551 else if ( (ent->client->pers.hand == CENTER_HANDED || ent->client->ps.fov > 91
552 //SKULL
553 /* Display the current weapon if chase is on */
554 || (ent->client->chasetoggle)
555 //END
556 ) && ent->client->pers.weapon)
557 ent->client->ps.stats[STAT_HELPICON] = gi.imageindex (ent->client->pers.weapon->icon);
558 else
559 ent->client->ps.stats[STAT_HELPICON] = 0;
560
561 ent->client->ps.stats[STAT_SPECTATOR] = 0;
562 MatrixSetStats(ent);
563 }
564
565 /*
566 ===============
567 G_CheckChaseStats
568 ===============
569 */
G_CheckChaseStats(edict_t * ent)570 void G_CheckChaseStats (edict_t *ent)
571 {
572 int i;
573 gclient_t *cl;
574
575 for (i = 1; i <= maxclients->value; i++) {
576 cl = g_edicts[i].client;
577 if (!g_edicts[i].inuse || cl->chase_target != ent)
578 continue;
579 memcpy(cl->ps.stats, ent->client->ps.stats, sizeof(cl->ps.stats));
580 G_SetSpectatorStats(g_edicts + i);
581 }
582 }
583
584 /*
585 ===============
586 G_SetSpectatorStats
587 ===============
588 */
G_SetSpectatorStats(edict_t * ent)589 void G_SetSpectatorStats (edict_t *ent)
590 {
591 gclient_t *cl = ent->client;
592
593 if (!cl->chase_target)
594 G_SetStats (ent);
595
596 cl->ps.stats[STAT_SPECTATOR] = 1;
597
598 // layouts are independant in spectator
599 cl->ps.stats[STAT_LAYOUTS] = 0;
600 if (cl->pers.health <= 0 || level.intermissiontime || cl->showscores)
601 cl->ps.stats[STAT_LAYOUTS] |= 1;
602 if (cl->showinventory && cl->pers.health > 0)
603 cl->ps.stats[STAT_LAYOUTS] |= 2;
604
605 if (cl->chase_target && cl->chase_target->inuse)
606 cl->ps.stats[STAT_CHASE] = CS_PLAYERSKINS +
607 (cl->chase_target - g_edicts) - 1;
608 else
609 cl->ps.stats[STAT_CHASE] = 0;
610 }
611
612