618 lines
13 KiB
C
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
|