p_hud.c 12 KB

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