p_hud.c (12529B)
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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 // 423 // armor 424 // 425 power_armor_type = PowerArmorType (ent); 426 if (power_armor_type) 427 { 428 cells = ent->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))]; 429 if (cells == 0) 430 { // ran out of cells for power armor 431 ent->flags &= ~FL_POWER_ARMOR; 432 gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0); 433 power_armor_type = 0;; 434 } 435 } 436 437 index = ArmorIndex (ent); 438 if (power_armor_type && (!index || (level.framenum & 8) ) ) 439 { // flash between power armor and other armor icon 440 ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex ("i_powershield"); 441 ent->client->ps.stats[STAT_ARMOR] = cells; 442 } 443 else if (index) 444 { 445 item = GetItemByIndex (index); 446 ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex (item->icon); 447 ent->client->ps.stats[STAT_ARMOR] = ent->client->pers.inventory[index]; 448 } 449 else 450 { 451 ent->client->ps.stats[STAT_ARMOR_ICON] = 0; 452 ent->client->ps.stats[STAT_ARMOR] = 0; 453 } 454 455 // 456 // pickup message 457 // 458 if (level.time > ent->client->pickup_msg_time) 459 { 460 ent->client->ps.stats[STAT_PICKUP_ICON] = 0; 461 ent->client->ps.stats[STAT_PICKUP_STRING] = 0; 462 } 463 464 // 465 // timers 466 // 467 if (ent->client->quad_framenum > level.framenum) 468 { 469 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_quad"); 470 ent->client->ps.stats[STAT_TIMER] = (ent->client->quad_framenum - level.framenum)/10; 471 } 472 else if (ent->client->invincible_framenum > level.framenum) 473 { 474 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_invulnerability"); 475 ent->client->ps.stats[STAT_TIMER] = (ent->client->invincible_framenum - level.framenum)/10; 476 } 477 else if (ent->client->enviro_framenum > level.framenum) 478 { 479 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_envirosuit"); 480 ent->client->ps.stats[STAT_TIMER] = (ent->client->enviro_framenum - level.framenum)/10; 481 } 482 else if (ent->client->breather_framenum > level.framenum) 483 { 484 ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_rebreather"); 485 ent->client->ps.stats[STAT_TIMER] = (ent->client->breather_framenum - level.framenum)/10; 486 } 487 else 488 { 489 ent->client->ps.stats[STAT_TIMER_ICON] = 0; 490 ent->client->ps.stats[STAT_TIMER] = 0; 491 } 492 493 // 494 // selected item 495 // 496 if (ent->client->pers.selected_item == -1) 497 ent->client->ps.stats[STAT_SELECTED_ICON] = 0; 498 else 499 ent->client->ps.stats[STAT_SELECTED_ICON] = gi.imageindex (itemlist[ent->client->pers.selected_item].icon); 500 501 ent->client->ps.stats[STAT_SELECTED_ITEM] = ent->client->pers.selected_item; 502 503 // 504 // layouts 505 // 506 ent->client->ps.stats[STAT_LAYOUTS] = 0; 507 508 if (deathmatch->value) 509 { 510 if (ent->client->pers.health <= 0 || level.intermissiontime 511 || ent->client->showscores) 512 ent->client->ps.stats[STAT_LAYOUTS] |= 1; 513 if (ent->client->showinventory && ent->client->pers.health > 0) 514 ent->client->ps.stats[STAT_LAYOUTS] |= 2; 515 } 516 else 517 { 518 if (ent->client->showscores || ent->client->showhelp) 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 524 // 525 // frags 526 // 527 ent->client->ps.stats[STAT_FRAGS] = ent->client->resp.score; 528 529 // 530 // help icon / current weapon if not shown 531 // 532 if (ent->client->resp.helpchanged && (level.framenum&8) ) 533 ent->client->ps.stats[STAT_HELPICON] = gi.imageindex ("i_help"); 534 else if ( (ent->client->pers.hand == CENTER_HANDED || ent->client->ps.fov > 91) 535 && ent->client->pers.weapon) 536 ent->client->ps.stats[STAT_HELPICON] = gi.imageindex (ent->client->pers.weapon->icon); 537 else 538 ent->client->ps.stats[STAT_HELPICON] = 0; 539 540 //ZOID 541 SetCTFStats(ent); 542 //ZOID 543 } 544