p_hud.c 13 KB

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