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