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