p_hud.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. #include "g_local.h"
  16. /*
  17. ======================================================================
  18. INTERMISSION
  19. ======================================================================
  20. */
  21. void MoveClientToIntermission (edict_t *ent)
  22. {
  23. if (deathmatch->value || coop->value)
  24. ent->client->showscores = true;
  25. VectorCopy (level.intermission_origin, ent->s.origin);
  26. ent->client->ps.pmove.origin[0] = level.intermission_origin[0]*8;
  27. ent->client->ps.pmove.origin[1] = level.intermission_origin[1]*8;
  28. ent->client->ps.pmove.origin[2] = level.intermission_origin[2]*8;
  29. VectorCopy (level.intermission_angle, ent->client->ps.viewangles);
  30. ent->client->ps.pmove.pm_type = PM_FREEZE;
  31. ent->client->ps.gunindex = 0;
  32. ent->client->ps.blend[3] = 0;
  33. ent->client->ps.rdflags &= ~RDF_UNDERWATER;
  34. // clean up powerup info
  35. ent->client->quad_framenum = 0;
  36. ent->client->invincible_framenum = 0;
  37. ent->client->breather_framenum = 0;
  38. ent->client->enviro_framenum = 0;
  39. ent->client->grenade_blew_up = false;
  40. ent->client->grenade_time = 0;
  41. ent->viewheight = 0;
  42. ent->s.modelindex = 0;
  43. ent->s.modelindex2 = 0;
  44. ent->s.modelindex3 = 0;
  45. ent->s.modelindex = 0;
  46. ent->s.effects = 0;
  47. ent->s.sound = 0;
  48. ent->solid = SOLID_NOT;
  49. // add the layout
  50. if (deathmatch->value || coop->value)
  51. {
  52. DeathmatchScoreboardMessage (ent, NULL);
  53. gi.unicast (ent, true);
  54. }
  55. }
  56. void BeginIntermission (edict_t *targ)
  57. {
  58. int i, n;
  59. edict_t *ent, *client;
  60. if (level.intermissiontime)
  61. return; // already activated
  62. game.autosaved = false;
  63. // respawn any dead clients
  64. for (i=0 ; i<maxclients->value ; i++)
  65. {
  66. client = g_edicts + 1 + i;
  67. if (!client->inuse)
  68. continue;
  69. if (client->health <= 0)
  70. respawn(client);
  71. }
  72. level.intermissiontime = level.time;
  73. level.changemap = targ->map;
  74. if (strstr(level.changemap, "*"))
  75. {
  76. if (coop->value)
  77. {
  78. for (i=0 ; i<maxclients->value ; i++)
  79. {
  80. client = g_edicts + 1 + i;
  81. if (!client->inuse)
  82. continue;
  83. // strip players of all keys between units
  84. for (n = 0; n < MAX_ITEMS; n++)
  85. {
  86. if (itemlist[n].flags & IT_KEY)
  87. client->client->pers.inventory[n] = 0;
  88. }
  89. }
  90. }
  91. }
  92. else
  93. {
  94. if (!deathmatch->value)
  95. {
  96. level.exitintermission = 1; // go immediately to the next level
  97. return;
  98. }
  99. }
  100. level.exitintermission = 0;
  101. // find an intermission spot
  102. ent = G_Find (NULL, FOFS(classname), "info_player_intermission");
  103. if (!ent)
  104. { // the map creator forgot to put in an intermission point...
  105. ent = G_Find (NULL, FOFS(classname), "info_player_start");
  106. if (!ent)
  107. ent = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
  108. }
  109. else
  110. { // chose one of four spots
  111. i = rand() & 3;
  112. while (i--)
  113. {
  114. ent = G_Find (ent, FOFS(classname), "info_player_intermission");
  115. if (!ent) // wrap around the list
  116. ent = G_Find (ent, FOFS(classname), "info_player_intermission");
  117. }
  118. }
  119. VectorCopy (ent->s.origin, level.intermission_origin);
  120. VectorCopy (ent->s.angles, level.intermission_angle);
  121. // move all clients to the intermission point
  122. for (i=0 ; i<maxclients->value ; i++)
  123. {
  124. client = g_edicts + 1 + i;
  125. if (!client->inuse)
  126. continue;
  127. MoveClientToIntermission (client);
  128. }
  129. }
  130. /*
  131. ==================
  132. DeathmatchScoreboardMessage
  133. ==================
  134. */
  135. void DeathmatchScoreboardMessage (edict_t *ent, edict_t *killer)
  136. {
  137. char entry[1024];
  138. char string[1400];
  139. int stringlength;
  140. int i, j, k;
  141. int sorted[MAX_CLIENTS];
  142. int sortedscores[MAX_CLIENTS];
  143. int score, total;
  144. int picnum;
  145. int x, y;
  146. gclient_t *cl;
  147. edict_t *cl_ent;
  148. char *tag;
  149. // sort the clients by score
  150. total = 0;
  151. for (i=0 ; i<game.maxclients ; i++)
  152. {
  153. cl_ent = g_edicts + 1 + i;
  154. if (!cl_ent->inuse || game.clients[i].resp.spectator)
  155. continue;
  156. score = game.clients[i].resp.score;
  157. for (j=0 ; j<total ; j++)
  158. {
  159. if (score > sortedscores[j])
  160. break;
  161. }
  162. for (k=total ; k>j ; k--)
  163. {
  164. sorted[k] = sorted[k-1];
  165. sortedscores[k] = sortedscores[k-1];
  166. }
  167. sorted[j] = i;
  168. sortedscores[j] = score;
  169. total++;
  170. }
  171. // print level name and exit rules
  172. string[0] = 0;
  173. stringlength = strlen(string);
  174. // add the clients in sorted order
  175. if (total > 12)
  176. total = 12;
  177. for (i=0 ; i<total ; i++)
  178. {
  179. cl = &game.clients[sorted[i]];
  180. cl_ent = g_edicts + 1 + sorted[i];
  181. picnum = gi.imageindex ("i_fixme");
  182. x = (i>=6) ? 160 : 0;
  183. y = 32 + 32 * (i%6);
  184. // add a dogtag
  185. if (cl_ent == ent)
  186. tag = "tag1";
  187. else if (cl_ent == killer)
  188. tag = "tag2";
  189. else
  190. tag = NULL;
  191. if (tag)
  192. {
  193. Com_sprintf (entry, sizeof(entry),
  194. "xv %i yv %i picn %s ",x+32, y, tag);
  195. j = strlen(entry);
  196. if (stringlength + j > 1024)
  197. break;
  198. strcpy (string + stringlength, entry);
  199. stringlength += j;
  200. }
  201. // send the layout
  202. Com_sprintf (entry, sizeof(entry),
  203. "client %i %i %i %i %i %i ",
  204. x, y, sorted[i], cl->resp.score, cl->ping, (level.framenum - cl->resp.enterframe)/600);
  205. j = strlen(entry);
  206. if (stringlength + j > 1024)
  207. break;
  208. strcpy (string + stringlength, entry);
  209. stringlength += j;
  210. }
  211. gi.WriteByte (svc_layout);
  212. gi.WriteString (string);
  213. }
  214. /*
  215. ==================
  216. DeathmatchScoreboard
  217. Draw instead of help message.
  218. Note that it isn't that hard to overflow the 1400 byte message limit!
  219. ==================
  220. */
  221. void DeathmatchScoreboard (edict_t *ent)
  222. {
  223. DeathmatchScoreboardMessage (ent, ent->enemy);
  224. gi.unicast (ent, true);
  225. }
  226. /*
  227. ==================
  228. Cmd_Score_f
  229. Display the scoreboard
  230. ==================
  231. */
  232. void Cmd_Score_f (edict_t *ent)
  233. {
  234. ent->client->showinventory = false;
  235. ent->client->showhelp = false;
  236. if (!deathmatch->value && !coop->value)
  237. return;
  238. if (ent->client->showscores)
  239. {
  240. ent->client->showscores = false;
  241. return;
  242. }
  243. ent->client->showscores = true;
  244. DeathmatchScoreboard (ent);
  245. }
  246. /*
  247. ==================
  248. HelpComputer
  249. Draw help computer.
  250. ==================
  251. */
  252. void HelpComputer (edict_t *ent)
  253. {
  254. char string[1024];
  255. char *sk;
  256. if (skill->value == 0)
  257. sk = "easy";
  258. else if (skill->value == 1)
  259. sk = "medium";
  260. else if (skill->value == 2)
  261. sk = "hard";
  262. else
  263. sk = "hard+";
  264. // send the layout
  265. Com_sprintf (string, sizeof(string),
  266. "xv 32 yv 8 picn help " // background
  267. "xv 202 yv 12 string2 \"%s\" " // skill
  268. "xv 0 yv 24 cstring2 \"%s\" " // level name
  269. "xv 0 yv 54 cstring2 \"%s\" " // help 1
  270. "xv 0 yv 110 cstring2 \"%s\" " // help 2
  271. "xv 50 yv 164 string2 \" kills goals secrets\" "
  272. "xv 50 yv 172 string2 \"%3i/%3i %i/%i %i/%i\" ",
  273. sk,
  274. level.level_name,
  275. game.helpmessage1,
  276. game.helpmessage2,
  277. level.killed_monsters, level.total_monsters,
  278. level.found_goals, level.total_goals,
  279. level.found_secrets, level.total_secrets);
  280. gi.WriteByte (svc_layout);
  281. gi.WriteString (string);
  282. gi.unicast (ent, true);
  283. }
  284. /*
  285. ==================
  286. Cmd_Help_f
  287. Display the current help message
  288. ==================
  289. */
  290. void Cmd_Help_f (edict_t *ent)
  291. {
  292. // this is for backwards compatability
  293. if (deathmatch->value)
  294. {
  295. Cmd_Score_f (ent);
  296. return;
  297. }
  298. ent->client->showinventory = false;
  299. ent->client->showscores = false;
  300. if (ent->client->showhelp && (ent->client->pers.game_helpchanged == game.helpchanged))
  301. {
  302. ent->client->showhelp = false;
  303. return;
  304. }
  305. ent->client->showhelp = true;
  306. ent->client->pers.helpchanged = 0;
  307. HelpComputer (ent);
  308. }
  309. //=======================================================================
  310. /*
  311. ===============
  312. G_SetStats
  313. ===============
  314. */
  315. void G_SetStats (edict_t *ent)
  316. {
  317. gitem_t *item;
  318. int index, cells;
  319. int power_armor_type;
  320. //
  321. // health
  322. //
  323. ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health;
  324. ent->client->ps.stats[STAT_HEALTH] = ent->health;
  325. //
  326. // ammo
  327. //
  328. if (!ent->client->ammo_index /* || !ent->client->pers.inventory[ent->client->ammo_index] */)
  329. {
  330. ent->client->ps.stats[STAT_AMMO_ICON] = 0;
  331. ent->client->ps.stats[STAT_AMMO] = 0;
  332. }
  333. else
  334. {
  335. item = &itemlist[ent->client->ammo_index];
  336. ent->client->ps.stats[STAT_AMMO_ICON] = gi.imageindex (item->icon);
  337. ent->client->ps.stats[STAT_AMMO] = ent->client->pers.inventory[ent->client->ammo_index];
  338. }
  339. //
  340. // armor
  341. //
  342. power_armor_type = PowerArmorType (ent);
  343. if (power_armor_type)
  344. {
  345. cells = ent->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))];
  346. if (cells == 0)
  347. { // ran out of cells for power armor
  348. ent->flags &= ~FL_POWER_ARMOR;
  349. gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0);
  350. power_armor_type = 0;;
  351. }
  352. }
  353. index = ArmorIndex (ent);
  354. if (power_armor_type && (!index || (level.framenum & 8) ) )
  355. { // flash between power armor and other armor icon
  356. ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex ("i_powershield");
  357. ent->client->ps.stats[STAT_ARMOR] = cells;
  358. }
  359. else if (index)
  360. {
  361. item = GetItemByIndex (index);
  362. ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex (item->icon);
  363. ent->client->ps.stats[STAT_ARMOR] = ent->client->pers.inventory[index];
  364. }
  365. else
  366. {
  367. ent->client->ps.stats[STAT_ARMOR_ICON] = 0;
  368. ent->client->ps.stats[STAT_ARMOR] = 0;
  369. }
  370. //
  371. // pickup message
  372. //
  373. if (level.time > ent->client->pickup_msg_time)
  374. {
  375. ent->client->ps.stats[STAT_PICKUP_ICON] = 0;
  376. ent->client->ps.stats[STAT_PICKUP_STRING] = 0;
  377. }
  378. //
  379. // timers
  380. //
  381. if (ent->client->quad_framenum > level.framenum)
  382. {
  383. ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_quad");
  384. ent->client->ps.stats[STAT_TIMER] = (ent->client->quad_framenum - level.framenum)/10;
  385. }
  386. else if (ent->client->invincible_framenum > level.framenum)
  387. {
  388. ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_invulnerability");
  389. ent->client->ps.stats[STAT_TIMER] = (ent->client->invincible_framenum - level.framenum)/10;
  390. }
  391. else if (ent->client->enviro_framenum > level.framenum)
  392. {
  393. ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_envirosuit");
  394. ent->client->ps.stats[STAT_TIMER] = (ent->client->enviro_framenum - level.framenum)/10;
  395. }
  396. else if (ent->client->breather_framenum > level.framenum)
  397. {
  398. ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_rebreather");
  399. ent->client->ps.stats[STAT_TIMER] = (ent->client->breather_framenum - level.framenum)/10;
  400. }
  401. else
  402. {
  403. ent->client->ps.stats[STAT_TIMER_ICON] = 0;
  404. ent->client->ps.stats[STAT_TIMER] = 0;
  405. }
  406. //
  407. // selected item
  408. //
  409. if (ent->client->pers.selected_item == -1)
  410. ent->client->ps.stats[STAT_SELECTED_ICON] = 0;
  411. else
  412. ent->client->ps.stats[STAT_SELECTED_ICON] = gi.imageindex (itemlist[ent->client->pers.selected_item].icon);
  413. ent->client->ps.stats[STAT_SELECTED_ITEM] = ent->client->pers.selected_item;
  414. //
  415. // layouts
  416. //
  417. ent->client->ps.stats[STAT_LAYOUTS] = 0;
  418. if (deathmatch->value)
  419. {
  420. if (ent->client->pers.health <= 0 || level.intermissiontime
  421. || ent->client->showscores)
  422. ent->client->ps.stats[STAT_LAYOUTS] |= 1;
  423. if (ent->client->showinventory && ent->client->pers.health > 0)
  424. ent->client->ps.stats[STAT_LAYOUTS] |= 2;
  425. }
  426. else
  427. {
  428. if (ent->client->showscores || ent->client->showhelp)
  429. ent->client->ps.stats[STAT_LAYOUTS] |= 1;
  430. if (ent->client->showinventory && ent->client->pers.health > 0)
  431. ent->client->ps.stats[STAT_LAYOUTS] |= 2;
  432. }
  433. //
  434. // frags
  435. //
  436. ent->client->ps.stats[STAT_FRAGS] = ent->client->resp.score;
  437. //
  438. // help icon / current weapon if not shown
  439. //
  440. if (ent->client->pers.helpchanged && (level.framenum&8) )
  441. ent->client->ps.stats[STAT_HELPICON] = gi.imageindex ("i_help");
  442. else if ( (ent->client->pers.hand == CENTER_HANDED || ent->client->ps.fov > 91)
  443. && ent->client->pers.weapon)
  444. ent->client->ps.stats[STAT_HELPICON] = gi.imageindex (ent->client->pers.weapon->icon);
  445. else
  446. ent->client->ps.stats[STAT_HELPICON] = 0;
  447. ent->client->ps.stats[STAT_SPECTATOR] = 0;
  448. }
  449. /*
  450. ===============
  451. G_CheckChaseStats
  452. ===============
  453. */
  454. void G_CheckChaseStats (edict_t *ent)
  455. {
  456. int i;
  457. gclient_t *cl;
  458. for (i = 1; i <= maxclients->value; i++) {
  459. cl = g_edicts[i].client;
  460. if (!g_edicts[i].inuse || cl->chase_target != ent)
  461. continue;
  462. memcpy(cl->ps.stats, ent->client->ps.stats, sizeof(cl->ps.stats));
  463. G_SetSpectatorStats(g_edicts + i);
  464. }
  465. }
  466. /*
  467. ===============
  468. G_SetSpectatorStats
  469. ===============
  470. */
  471. void G_SetSpectatorStats (edict_t *ent)
  472. {
  473. gclient_t *cl = ent->client;
  474. if (!cl->chase_target)
  475. G_SetStats (ent);
  476. cl->ps.stats[STAT_SPECTATOR] = 1;
  477. // layouts are independant in spectator
  478. cl->ps.stats[STAT_LAYOUTS] = 0;
  479. if (cl->pers.health <= 0 || level.intermissiontime || cl->showscores)
  480. cl->ps.stats[STAT_LAYOUTS] |= 1;
  481. if (cl->showinventory && cl->pers.health > 0)
  482. cl->ps.stats[STAT_LAYOUTS] |= 2;
  483. if (cl->chase_target && cl->chase_target->inuse)
  484. cl->ps.stats[STAT_CHASE] = CS_PLAYERSKINS +
  485. (cl->chase_target - g_edicts) - 1;
  486. else
  487. cl->ps.stats[STAT_CHASE] = 0;
  488. }