summaryrefslogtreecommitdiff
path: root/source/baseq2/p_hud.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/baseq2/p_hud.c')
-rw-r--r--source/baseq2/p_hud.c571
1 files changed, 571 insertions, 0 deletions
diff --git a/source/baseq2/p_hud.c b/source/baseq2/p_hud.c
new file mode 100644
index 0000000..c3c7ab2
--- /dev/null
+++ b/source/baseq2/p_hud.c
@@ -0,0 +1,571 @@
+/*
+Copyright (C) 1997-2001 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#include "g_local.h"
+
+
+
+/*
+======================================================================
+
+INTERMISSION
+
+======================================================================
+*/
+
+void MoveClientToIntermission (edict_t *ent)
+{
+ if (deathmatch->value || coop->value)
+ ent->client->showscores = qtrue;
+ VectorCopy (level.intermission_origin, ent->s.origin);
+ ent->client->ps.pmove.origin[0] = level.intermission_origin[0]*8;
+ ent->client->ps.pmove.origin[1] = level.intermission_origin[1]*8;
+ ent->client->ps.pmove.origin[2] = level.intermission_origin[2]*8;
+ VectorCopy (level.intermission_angle, ent->client->ps.viewangles);
+ ent->client->ps.pmove.pm_type = PM_FREEZE;
+ ent->client->ps.gunindex = 0;
+ ent->client->ps.blend[3] = 0;
+ ent->client->ps.rdflags &= ~RDF_UNDERWATER;
+
+ // clean up powerup info
+ ent->client->quad_framenum = 0;
+ ent->client->invincible_framenum = 0;
+ ent->client->breather_framenum = 0;
+ ent->client->enviro_framenum = 0;
+ ent->client->grenade_blew_up = qfalse;
+ ent->client->grenade_time = 0;
+
+ ent->viewheight = 0;
+ ent->s.modelindex = 0;
+ ent->s.modelindex2 = 0;
+ ent->s.modelindex3 = 0;
+ ent->s.modelindex = 0;
+ ent->s.effects = 0;
+ ent->s.sound = 0;
+ ent->solid = SOLID_NOT;
+
+ // add the layout
+
+ if (deathmatch->value || coop->value)
+ {
+ DeathmatchScoreboardMessage (ent, NULL);
+ gi.unicast (ent, qtrue);
+ }
+
+}
+
+void BeginIntermission (edict_t *targ)
+{
+ int i, n;
+ edict_t *ent, *client;
+
+ if (level.intermissiontime)
+ return; // already activated
+
+ game.autosaved = qfalse;
+
+ // respawn any dead clients
+ for (i=0 ; i<maxclients->value ; i++)
+ {
+ client = g_edicts + 1 + i;
+ if (!client->inuse)
+ continue;
+ if (client->health <= 0)
+ respawn(client);
+ }
+
+ level.intermissiontime = level.time;
+ level.changemap = targ->map;
+
+ if (strstr(level.changemap, "*"))
+ {
+ if (coop->value)
+ {
+ for (i=0 ; i<maxclients->value ; i++)
+ {
+ client = g_edicts + 1 + i;
+ if (!client->inuse)
+ continue;
+ // strip players of all keys between units
+ for (n = 0; n < MAX_ITEMS; n++)
+ {
+ if (itemlist[n].flags & IT_KEY)
+ client->client->pers.inventory[n] = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!deathmatch->value)
+ {
+ level.exitintermission = 1; // go immediately to the next level
+ return;
+ }
+ }
+
+ level.exitintermission = 0;
+
+ // find an intermission spot
+ ent = G_Find (NULL, FOFS(classname), "info_player_intermission");
+ if (!ent)
+ { // the map creator forgot to put in an intermission point...
+ ent = G_Find (NULL, FOFS(classname), "info_player_start");
+ if (!ent)
+ ent = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
+ }
+ else
+ { // chose one of four spots
+ i = rand() & 3;
+ while (i--)
+ {
+ ent = G_Find (ent, FOFS(classname), "info_player_intermission");
+ if (!ent) // wrap around the list
+ ent = G_Find (ent, FOFS(classname), "info_player_intermission");
+ }
+ }
+
+ VectorCopy (ent->s.origin, level.intermission_origin);
+ VectorCopy (ent->s.angles, level.intermission_angle);
+
+ // move all clients to the intermission point
+ for (i=0 ; i<maxclients->value ; i++)
+ {
+ client = g_edicts + 1 + i;
+ if (!client->inuse)
+ continue;
+ MoveClientToIntermission (client);
+ }
+}
+
+
+/*
+==================
+DeathmatchScoreboardMessage
+
+==================
+*/
+void DeathmatchScoreboardMessage (edict_t *ent, edict_t *killer)
+{
+ char entry[1024];
+ char string[1400];
+ int stringlength;
+ int i, j, k;
+ int sorted[MAX_CLIENTS];
+ int sortedscores[MAX_CLIENTS];
+ int score, total;
+ int picnum;
+ int x, y;
+ gclient_t *cl;
+ edict_t *cl_ent;
+ char *tag;
+
+ // sort the clients by score
+ total = 0;
+ for (i=0 ; i<game.maxclients ; i++)
+ {
+ cl_ent = g_edicts + 1 + i;
+ if (!cl_ent->inuse || game.clients[i].resp.spectator)
+ continue;
+ score = game.clients[i].resp.score;
+ for (j=0 ; j<total ; j++)
+ {
+ if (score > sortedscores[j])
+ break;
+ }
+ for (k=total ; k>j ; k--)
+ {
+ sorted[k] = sorted[k-1];
+ sortedscores[k] = sortedscores[k-1];
+ }
+ sorted[j] = i;
+ sortedscores[j] = score;
+ total++;
+ }
+
+ // print level name and exit rules
+ string[0] = 0;
+
+ stringlength = strlen(string);
+
+ // add the clients in sorted order
+ if (total > 12)
+ total = 12;
+
+ for (i=0 ; i<total ; i++)
+ {
+ cl = &game.clients[sorted[i]];
+ cl_ent = g_edicts + 1 + sorted[i];
+
+ picnum = gi.imageindex ("i_fixme");
+ x = (i>=6) ? 160 : 0;
+ y = 32 + 32 * (i%6);
+
+ // add a dogtag
+ if (cl_ent == ent)
+ tag = "tag1";
+ else if (cl_ent == killer)
+ tag = "tag2";
+ else
+ tag = NULL;
+ if (tag)
+ {
+ Q_snprintf (entry, sizeof(entry),
+ "xv %i yv %i picn %s ",x+32, y, tag);
+ j = strlen(entry);
+ if (stringlength + j > 1024)
+ break;
+ strcpy (string + stringlength, entry);
+ stringlength += j;
+ }
+
+ // send the layout
+ Q_snprintf (entry, sizeof(entry),
+ "client %i %i %i %i %i %i ",
+ x, y, sorted[i], cl->resp.score, cl->ping, (level.framenum - cl->resp.enterframe)/600);
+ j = strlen(entry);
+ if (stringlength + j > 1024)
+ break;
+ strcpy (string + stringlength, entry);
+ stringlength += j;
+ }
+
+ gi.WriteByte (svc_layout);
+ gi.WriteString (string);
+}
+
+
+/*
+==================
+DeathmatchScoreboard
+
+Draw instead of help message.
+Note that it isn't that hard to overflow the 1400 byte message limit!
+==================
+*/
+void DeathmatchScoreboard (edict_t *ent)
+{
+ DeathmatchScoreboardMessage (ent, ent->enemy);
+ gi.unicast (ent, qtrue);
+}
+
+
+/*
+==================
+Cmd_Score_f
+
+Display the scoreboard
+==================
+*/
+void Cmd_Score_f (edict_t *ent)
+{
+ ent->client->showinventory = qfalse;
+ ent->client->showhelp = qfalse;
+
+ if (!deathmatch->value && !coop->value)
+ return;
+
+ if (ent->client->showscores)
+ {
+ ent->client->showscores = qfalse;
+ return;
+ }
+
+ ent->client->showscores = qtrue;
+ DeathmatchScoreboard (ent);
+}
+
+
+/*
+==================
+HelpComputer
+
+Draw help computer.
+==================
+*/
+void HelpComputer (edict_t *ent)
+{
+ char string[1024];
+ char *sk;
+
+ if (skill->value == 0)
+ sk = "easy";
+ else if (skill->value == 1)
+ sk = "medium";
+ else if (skill->value == 2)
+ sk = "hard";
+ else
+ sk = "hard+";
+
+ // send the layout
+ Q_snprintf (string, sizeof(string),
+ "xv 32 yv 8 picn help " // background
+ "xv 202 yv 12 string2 \"%s\" " // skill
+ "xv 0 yv 24 cstring2 \"%s\" " // level name
+ "xv 0 yv 54 cstring2 \"%s\" " // help 1
+ "xv 0 yv 110 cstring2 \"%s\" " // help 2
+ "xv 50 yv 164 string2 \" kills goals secrets\" "
+ "xv 50 yv 172 string2 \"%3i/%3i %i/%i %i/%i\" ",
+ sk,
+ level.level_name,
+ game.helpmessage1,
+ game.helpmessage2,
+ level.killed_monsters, level.total_monsters,
+ level.found_goals, level.total_goals,
+ level.found_secrets, level.total_secrets);
+
+ gi.WriteByte (svc_layout);
+ gi.WriteString (string);
+ gi.unicast (ent, qtrue);
+}
+
+
+/*
+==================
+Cmd_Help_f
+
+Display the current help message
+==================
+*/
+void Cmd_Help_f (edict_t *ent)
+{
+ // this is for backwards compatability
+ if (deathmatch->value)
+ {
+ Cmd_Score_f (ent);
+ return;
+ }
+
+ ent->client->showinventory = qfalse;
+ ent->client->showscores = qfalse;
+
+ if (ent->client->showhelp && (ent->client->pers.game_helpchanged == game.helpchanged))
+ {
+ ent->client->showhelp = qfalse;
+ return;
+ }
+
+ ent->client->showhelp = qtrue;
+ ent->client->pers.helpchanged = 0;
+ HelpComputer (ent);
+}
+
+
+//=======================================================================
+
+/*
+===============
+G_SetStats
+===============
+*/
+void G_SetStats (edict_t *ent)
+{
+ gitem_t *item;
+ int index, cells;
+ int power_armor_type;
+
+ //
+ // health
+ //
+ ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health;
+ ent->client->ps.stats[STAT_HEALTH] = ent->health;
+
+ //
+ // ammo
+ //
+ if (!ent->client->ammo_index /* || !ent->client->pers.inventory[ent->client->ammo_index] */)
+ {
+ ent->client->ps.stats[STAT_AMMO_ICON] = 0;
+ ent->client->ps.stats[STAT_AMMO] = 0;
+ }
+ else
+ {
+ item = &itemlist[ent->client->ammo_index];
+ ent->client->ps.stats[STAT_AMMO_ICON] = gi.imageindex (item->icon);
+ ent->client->ps.stats[STAT_AMMO] = ent->client->pers.inventory[ent->client->ammo_index];
+ }
+
+ //
+ // armor
+ //
+ power_armor_type = PowerArmorType (ent);
+ if (power_armor_type)
+ {
+ cells = ent->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))];
+ if (cells == 0)
+ { // ran out of cells for power armor
+ ent->flags &= ~FL_POWER_ARMOR;
+ gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0);
+ power_armor_type = 0;;
+ }
+ }
+
+ index = ArmorIndex (ent);
+ if (power_armor_type && (!index || (level.framenum & 8) ) )
+ { // flash between power armor and other armor icon
+ ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex ("i_powershield");
+ ent->client->ps.stats[STAT_ARMOR] = cells;
+ }
+ else if (index)
+ {
+ item = GetItemByIndex (index);
+ ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex (item->icon);
+ ent->client->ps.stats[STAT_ARMOR] = ent->client->pers.inventory[index];
+ }
+ else
+ {
+ ent->client->ps.stats[STAT_ARMOR_ICON] = 0;
+ ent->client->ps.stats[STAT_ARMOR] = 0;
+ }
+
+ //
+ // pickup message
+ //
+ if (level.time > ent->client->pickup_msg_time)
+ {
+ ent->client->ps.stats[STAT_PICKUP_ICON] = 0;
+ ent->client->ps.stats[STAT_PICKUP_STRING] = 0;
+ }
+
+ //
+ // timers
+ //
+ if (ent->client->quad_framenum > level.framenum)
+ {
+ ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_quad");
+ ent->client->ps.stats[STAT_TIMER] = (ent->client->quad_framenum - level.framenum)/10;
+ }
+ else if (ent->client->invincible_framenum > level.framenum)
+ {
+ ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_invulnerability");
+ ent->client->ps.stats[STAT_TIMER] = (ent->client->invincible_framenum - level.framenum)/10;
+ }
+ else if (ent->client->enviro_framenum > level.framenum)
+ {
+ ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_envirosuit");
+ ent->client->ps.stats[STAT_TIMER] = (ent->client->enviro_framenum - level.framenum)/10;
+ }
+ else if (ent->client->breather_framenum > level.framenum)
+ {
+ ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_rebreather");
+ ent->client->ps.stats[STAT_TIMER] = (ent->client->breather_framenum - level.framenum)/10;
+ }
+ else
+ {
+ ent->client->ps.stats[STAT_TIMER_ICON] = 0;
+ ent->client->ps.stats[STAT_TIMER] = 0;
+ }
+
+ //
+ // selected item
+ //
+ if (ent->client->pers.selected_item == -1)
+ ent->client->ps.stats[STAT_SELECTED_ICON] = 0;
+ else
+ ent->client->ps.stats[STAT_SELECTED_ICON] = gi.imageindex (itemlist[ent->client->pers.selected_item].icon);
+
+ ent->client->ps.stats[STAT_SELECTED_ITEM] = ent->client->pers.selected_item;
+
+ //
+ // layouts
+ //
+ ent->client->ps.stats[STAT_LAYOUTS] = 0;
+
+ if (deathmatch->value)
+ {
+ if (ent->client->pers.health <= 0 || level.intermissiontime
+ || ent->client->showscores)
+ ent->client->ps.stats[STAT_LAYOUTS] |= 1;
+ if (ent->client->showinventory && ent->client->pers.health > 0)
+ ent->client->ps.stats[STAT_LAYOUTS] |= 2;
+ }
+ else
+ {
+ if (ent->client->showscores || ent->client->showhelp)
+ ent->client->ps.stats[STAT_LAYOUTS] |= 1;
+ if (ent->client->showinventory && ent->client->pers.health > 0)
+ ent->client->ps.stats[STAT_LAYOUTS] |= 2;
+ }
+
+ //
+ // frags
+ //
+ ent->client->ps.stats[STAT_FRAGS] = ent->client->resp.score;
+
+ //
+ // help icon / current weapon if not shown
+ //
+ if (ent->client->pers.helpchanged && (level.framenum&8) )
+ ent->client->ps.stats[STAT_HELPICON] = gi.imageindex ("i_help");
+ else if ( (ent->client->pers.hand == CENTER_HANDED || ent->client->ps.fov > 91)
+ && ent->client->pers.weapon)
+ ent->client->ps.stats[STAT_HELPICON] = gi.imageindex (ent->client->pers.weapon->icon);
+ else
+ ent->client->ps.stats[STAT_HELPICON] = 0;
+
+ ent->client->ps.stats[STAT_SPECTATOR] = 0;
+}
+
+/*
+===============
+G_CheckChaseStats
+===============
+*/
+void G_CheckChaseStats (edict_t *ent)
+{
+ int i;
+ gclient_t *cl;
+
+ for (i = 1; i <= maxclients->value; i++) {
+ cl = g_edicts[i].client;
+ if (!g_edicts[i].inuse || cl->chase_target != ent)
+ continue;
+ memcpy(cl->ps.stats, ent->client->ps.stats, sizeof(cl->ps.stats));
+ G_SetSpectatorStats(g_edicts + i);
+ }
+}
+
+/*
+===============
+G_SetSpectatorStats
+===============
+*/
+void G_SetSpectatorStats (edict_t *ent)
+{
+ gclient_t *cl = ent->client;
+
+ if (!cl->chase_target)
+ G_SetStats (ent);
+
+ cl->ps.stats[STAT_SPECTATOR] = 1;
+
+ // layouts are independant in spectator
+ cl->ps.stats[STAT_LAYOUTS] = 0;
+ if (cl->pers.health <= 0 || level.intermissiontime || cl->showscores)
+ cl->ps.stats[STAT_LAYOUTS] |= 1;
+ if (cl->showinventory && cl->pers.health > 0)
+ cl->ps.stats[STAT_LAYOUTS] |= 2;
+
+ if (cl->chase_target && cl->chase_target->inuse)
+ cl->ps.stats[STAT_CHASE] = CS_PLAYERSKINS +
+ (cl->chase_target - g_edicts) - 1;
+ else
+ cl->ps.stats[STAT_CHASE] = 0;
+}
+