heretic2-sdk/Toolkit/Programming/GameCode/game/p_hud.c
1998-11-24 00:00:00 +00:00

618 lines
13 KiB
C

#include "g_local.h"
#include "random.h"
#include "vector.h"
#include "g_playstats.h"
#include "g_itemstats.h"
qboolean PossessCorrectItem(edict_t *ent, gitem_t *item);
#if 0
// cursor positioning
xl <value>
xr <value>
yb <value>
yt <value>
// drawing
statpic <name>
pic <stat>
num <fieldwidth> <stat>
string <stat>
// control
if <stat>
ifeq <stat> <value>
ifbit <stat> <value>
endif
#endif
char *single_statusbar =
"yb -74 "
"xl 16 " // green mana
"gm "
"yb -44 "
"xl 40 "
"pic 4 " // Weapon
"xl 76 " // Ammo
"pic 2 "
"am "
"xr -112 "
"pic 0 "
"hnum " // Health
"if 6 "
"yb -44 "
"xr -72 "
"pic 6 " // Defence
"endif "
"yb -74 "
"xr -32 "
"bm " // blue mana
" yt 16 "
"if 28 "
" xl 32 "
" lt " // Lung time left
"endif "
"if 25 "
" xr -96 "
" pt " // Powerup time left
"endif "
"yt 16 "
"xc 0 " // Inventory Puzzle Item 1
"pici 18 "
"xc 40 " // Puzzle 2
"pici 19 "
"xc 80 " // Puzzle 3
"pici 20 "
"xc 120 " // Puzzle 4
"pici 21 "
"if 31 "
" xl 32 "
" bl " // Boss Life Meter
"endif "
;
char *dm_statusbar =
"yb -74 "
"xl 16 " // green mana
"gm "
"yb -44 "
"xl 40 "
"pic 4 " // Weapon
"xl 76 " // Ammo
"pic 2 "
"am "
"xr -112 "
"pic 0 "
"hnum " // Health
"yb -44 "
"xr -72 "
"pic 6 " // Defence
"yb -74 "
"xr -32 "
"bm " // blue mana
" yt 16 "
"if 28 "
" xl 32 "
" lt " // Lung time left
"endif "
"if 25 "
" xr -96 "
" pt " // Powerup time left
"endif "
#if 0
"xc 0 " // Frag
"num 3 15 "
#endif
;
/*
======================================================================
INTERMISSION
======================================================================
*/
void MoveClientToIntermission(edict_t *ent)
{
if(deathmatch->value)
ent->client->playerinfo.showscores = true;
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_INTERMISSION;
ent->client->ps.rdflags &= ~RDF_UNDERWATER;
// Clean up powerup info.
ent->client->invincible_framenum = 0;
ent->viewheight = 0;
ent->s.modelindex = 0;
ent->s.effects = 0;
ent->s.sound = 0;
ent->solid = SOLID_NOT;
// Add the layout.
if(deathmatch->value)
{
DeathmatchScoreboardMessage(ent,NULL);
gi.unicast(ent,true);
}
}
void BeginIntermission(edict_t *targ)
{
int i;
edict_t *ent,
*client;
// Already activated?
if(level.intermissiontime)
return;
game.autosaved = false;
// 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 (!deathmatch->value)
{
// Go immediately to the next level if not deathmatch.
level.exitintermission = 1;
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 = irand(0, 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 x, y;
gclient_t *cl;
edict_t *cl_ent;
#if 0
int picnum;
char *tag;
#endif
// Sort the clients by score.
total = 0;
for(i = 0; i < game.maxclients; i++)
{
cl_ent = g_edicts + 1 + i;
if (!cl_ent->inuse)
{
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];
x = (i >= 6) ? 160 : 0;
y = 32 + 32 * (i % 6);
#if 0
picnum = gi.imageindex("icons/i_teleport.m8");
// Add a dogtag.
if (cl_ent == ent)
tag = "tag1";
else if (cl_ent == killer)
tag = "tag2";
else
tag = NULL;
if(tag)
{
Com_sprintf(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;
}
#endif
// Send the layout.
Com_sprintf (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, true);
}
/*
==================
Cmd_Score_f
Display the scoreboard
==================
*/
void Cmd_Score_f (edict_t *ent)
{
if (!deathmatch->value)
return;
if (ent->client->playerinfo.showscores)
{
ent->client->playerinfo.showscores = false;
return;
}
ent->client->playerinfo.showscores = true;
DeathmatchScoreboard (ent);
}
//=======================================================================
/*
===============
G_GetShrineTime
===============
*/
short GetShrineTime(float time)
{
float duration;
short result;
duration = time - level.time;
if(duration < 0.0)
{
return(0);
}
result = (short)ceil(duration);
return(result);
}
static char *healthicons[2] =
{
"icons/i_health.m8",
"icons/i_health2.m8",
};
// ************************************************************************************************
// G_SetStats
// ----------
// ************************************************************************************************
void G_SetStats (edict_t *ent)
{
int i, count;
gitem_t *item;
gclient_t *pi;
player_state_t *ps;
client_persistant_t *pers;
float time;
pi = ent->client;
ps = &ent->client->ps;
pers = &ent->client->playerinfo.pers;
// ********************************************************************************************
// Frags
// ********************************************************************************************
ps->stats[STAT_FRAGS] = pi->resp.score;
// ********************************************************************************************
// Health.
// ********************************************************************************************
ps->stats[STAT_HEALTH_ICON]=gi.imageindex (healthicons[Q_ftol(level.time*2)&1]);
ps->stats[STAT_HEALTH]=ent->health;
// ********************************************************************************************
// Weapon / defence.
// ********************************************************************************************
ps->stats[STAT_WEAPON_ICON]=gi.imageindex(pers->weapon->icon);
if (pers->defence)
ps->stats[STAT_DEFENCE_ICON]=gi.imageindex(pers->defence->icon);
// ********************************************************************************************
// Weapon ammo.
// ********************************************************************************************
if(pers->weapon->ammo&&pers->weapon->count_width)
{
item=FindItem(pers->weapon->ammo);
ps->stats[STAT_AMMO_ICON]=gi.imageindex(item->icon);
ps->stats[STAT_AMMO] = pers->inventory.Items[ITEM_INDEX(item)];
}
else
{
ps->stats[STAT_AMMO_ICON] = 0;
}
// ********************************************************************************************
// Offensive mana.
// ********************************************************************************************
ps->stats[STAT_OFFMANA_ICON]=gi.imageindex("icons/green-mana.m8");
ps->stats[STAT_OFFMANA_BACK]=gi.imageindex("icons/green-mana2.m8");
item = FindItem("Off-mana");
ps->stats[STAT_OFFMANA] = (pers->inventory.Items[ITEM_INDEX(item)] * 100) / MAX_OFF_MANA;
if(ps->stats[STAT_OFFMANA] < 0)
ps->stats[STAT_OFFMANA] = 0;
// ********************************************************************************************
// Defensive mana.
// ********************************************************************************************
ps->stats[STAT_DEFMANA_ICON]=gi.imageindex("icons/blue-mana.m8");
ps->stats[STAT_DEFMANA_BACK]=gi.imageindex("icons/blue-mana2.m8");
item = FindItem("Def-mana");
ps->stats[STAT_DEFMANA] = (pers->inventory.Items[ITEM_INDEX(item)] * 100) / MAX_DEF_MANA;
if(ps->stats[STAT_DEFMANA] < 0)
ps->stats[STAT_DEFMANA] = 0;
// ********************************************************************************************
// Shrine timers.
// ********************************************************************************************
ps->stats[STAT_POWERUP_BACK] = gi.imageindex("icons/powerup2.m8");
ps->stats[STAT_POWERUP_ICON] = gi.imageindex("icons/powerup.m8");
ps->stats[STAT_POWERUP_TIMER] = (GetShrineTime(pi->playerinfo.powerup_timer) * 100) / POWERUP_DURATION;
// Cheating sets the powerup timer to something huge, so let's avoid a crash here.
if (ps->stats[STAT_POWERUP_TIMER] > 100)
ps->stats[STAT_POWERUP_TIMER]=100;
ps->stats[STAT_LUNG_BACK] = gi.imageindex("icons/breath2.m8");
ps->stats[STAT_LUNG_ICON] = gi.imageindex("icons/breath.m8");
ps->stats[STAT_LUNG_TIMER] = 0;
if((ent->waterlevel > 2) && !(ent->flags & FL_INLAVA))
{
// Make negative if we have lungs powerup.
if(pi->playerinfo.lungs_timer)
{
time = pi->playerinfo.lungs_timer + ent->air_finished - level.time;
if(time > 0)
{
ps->stats[STAT_LUNG_TIMER] = -(time * 100) / (HOLD_BREATH_TIME + LUNGS_DURATION);
}
}
else
{
time = ent->air_finished - level.time;
if(time > 0)
{
ps->stats[STAT_LUNG_TIMER] = (time * 100) / HOLD_BREATH_TIME;
}
}
}
// ********************************************************************************************
// Puzzle items.
// ********************************************************************************************
ps->stats[STAT_PUZZLE_ITEM1] = 0;
ps->stats[STAT_PUZZLE_ITEM2] = 0;
ps->stats[STAT_PUZZLE_ITEM3] = 0;
ps->stats[STAT_PUZZLE_ITEM4] = 0;
// Scan through inventory to handle puzzle pieces.
item = p_itemlist;
count = STAT_PUZZLE_ITEM1;
ps->stats[STAT_PUZZLE_COUNT] = 0;
for(i = 0; i < MAX_ITEMS; i++, item++)
{
if((item->flags & IT_PUZZLE) && pers->inventory.Items[i])
{
if(count > STAT_PUZZLE_ITEM4)
{
break;
}
else
{
ps->stats[count] = gi.imageindex (item->icon);
ps->stats[STAT_PUZZLE_COUNT]++;
if(PossessCorrectItem(ent, item))
{
ps->stats[count] |= 0x8000;
}
count++;
}
}
}
// ********************************************************************************************
// Layouts.
// ********************************************************************************************
ent->client->ps.stats[STAT_LAYOUTS] = 0;
// Inventory gets activated when player is in a use puzzle trigger field.
if(ent->target_ent)
{
if(!strcmp(ent->target_ent->classname, "trigger_playerusepuzzle"))
{
ps->stats[STAT_LAYOUTS] |= 4;
}
}
if (ent->client->playerinfo.showpuzzleinventory)
{
// Show puzzle inventory.
ps->stats[STAT_LAYOUTS] |= 4;
}
if (deathmatch->value)
{
if (pers->health <= 0 || level.intermissiontime || ent->client->playerinfo.showscores)
ps->stats[STAT_LAYOUTS] |= 1;
}
else
{
if (ent->client->playerinfo.showscores)
ps->stats[STAT_LAYOUTS] |= 1;
}
}
// end