game: sync rogue player code

This commit is contained in:
Denis Pauk 2023-10-22 12:39:37 +03:00
parent 5103bd6f72
commit 5c87db6e88
7 changed files with 338 additions and 2412 deletions

View File

@ -1523,9 +1523,9 @@ ROGUE_OBJS_ = \
src/game/monster/widow/widow.o \ src/game/monster/widow/widow.o \
src/game/monster/widow/widow2.o \ src/game/monster/widow/widow2.o \
src/game/player/client.o \ src/game/player/client.o \
src/rogue/player/hud.o \ src/game/player/hud.o \
src/rogue/player/trail.o \ src/game/player/trail.o \
src/rogue/player/view.o \ src/game/player/view.o \
src/game/player/weapon.o \ src/game/player/weapon.o \
src/rogue/savegame/savegame.o src/rogue/savegame/savegame.o

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (c) ZeniMax Media Inc.
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -292,11 +293,16 @@ ED_CallSpawn(edict_t *ent)
} }
char * char *
ED_NewString(char *string) ED_NewString(const char *string)
{ {
char *newb, *new_p; char *newb, *new_p;
int i, l; int i, l;
if (!string)
{
return NULL;
}
l = strlen(string) + 1; l = strlen(string) + 1;
newb = gi.TagMalloc(l, TAG_LEVEL); newb = gi.TagMalloc(l, TAG_LEVEL);
@ -332,16 +338,21 @@ ED_NewString(char *string)
* the binary values in an edict * the binary values in an edict
*/ */
void void
ED_ParseField(char *key, char *value, edict_t *ent) ED_ParseField(const char *key, const char *value, edict_t *ent)
{ {
field_t *f; field_t *f;
byte *b; byte *b;
float v; float v;
vec3_t vec; vec3_t vec;
if (!ent || !value || !key)
{
return;
}
for (f = fields; f->name; f++) for (f = fields; f->name; f++)
{ {
if (!Q_stricmp(f->name, key)) if (!(f->flags & FFL_NOSPAWN) && !Q_strcasecmp(f->name, (char *)key))
{ {
/* found it */ /* found it */
if (f->flags & FFL_SPAWNTEMP) if (f->flags & FFL_SPAWNTEMP)
@ -365,13 +376,13 @@ ED_ParseField(char *key, char *value, edict_t *ent)
((float *)(b + f->ofs))[2] = vec[2]; ((float *)(b + f->ofs))[2] = vec[2];
break; break;
case F_INT: case F_INT:
*(int *)(b + f->ofs) = atoi(value); *(int *)(b + f->ofs) = (int)strtol(value, (char **)NULL, 10);
break; break;
case F_FLOAT: case F_FLOAT:
*(float *)(b + f->ofs) = atof(value); *(float *)(b + f->ofs) = (float)strtod(value, (char **)NULL);
break; break;
case F_ANGLEHACK: case F_ANGLEHACK:
v = atof(value); v = (float)strtod(value, (char **)NULL);
((float *)(b + f->ofs))[0] = 0; ((float *)(b + f->ofs))[0] = 0;
((float *)(b + f->ofs))[1] = v; ((float *)(b + f->ofs))[1] = v;
((float *)(b + f->ofs))[2] = 0; ((float *)(b + f->ofs))[2] = 0;
@ -391,7 +402,7 @@ ED_ParseField(char *key, char *value, edict_t *ent)
/* /*
* Parses an edict out of the given string, * Parses an edict out of the given string,
* returning the new position ed should be * returning the new position. ed should be
* a properly initialized empty edict. * a properly initialized empty edict.
*/ */
char * char *
@ -399,10 +410,16 @@ ED_ParseEdict(char *data, edict_t *ent)
{ {
qboolean init; qboolean init;
char keyname[256]; char keyname[256];
char *com_token; const char *com_token;
if (!ent)
{
return NULL;
}
init = false; init = false;
memset(&st, 0, sizeof(st)); memset(&st, 0, sizeof(st));
st.skyautorotate = 1;
/* go through all the dictionary pairs */ /* go through all the dictionary pairs */
while (1) while (1)
@ -420,7 +437,7 @@ ED_ParseEdict(char *data, edict_t *ent)
gi.error("ED_ParseEntity: EOF without closing brace"); gi.error("ED_ParseEntity: EOF without closing brace");
} }
strncpy(keyname, com_token, sizeof(keyname) - 1); Q_strlcpy(keyname, com_token, sizeof(keyname));
/* parse value */ /* parse value */
com_token = COM_Parse(&data); com_token = COM_Parse(&data);
@ -437,9 +454,9 @@ ED_ParseEdict(char *data, edict_t *ent)
init = true; init = true;
/* keynames with a leading underscore are used /* keynames with a leading underscore are
for utility comments, and are immediately used for utility comments, and are
discarded by quake */ immediately discarded by quake */
if (keyname[0] == '_') if (keyname[0] == '_')
{ {
continue; continue;
@ -462,6 +479,77 @@ ED_ParseEdict(char *data, edict_t *ent)
* All but the first will have the FL_TEAMSLAVE flag set. * All but the first will have the FL_TEAMSLAVE flag set.
* All but the last will have the teamchain field set to the next one * All but the last will have the teamchain field set to the next one
*/ */
static void
G_FixTeams(void)
{
edict_t *e, *e2, *chain;
int i, j;
int c, c2;
c = 0;
c2 = 0;
for (i = 1, e = g_edicts + i; i < globals.num_edicts; i++, e++)
{
if (!e->inuse)
{
continue;
}
if (!e->team)
{
continue;
}
if (!strcmp(e->classname, "func_train"))
{
if (e->flags & FL_TEAMSLAVE)
{
chain = e;
e->teammaster = e;
e->teamchain = NULL;
e->flags &= ~FL_TEAMSLAVE;
c++;
c2++;
for (j = 1, e2 = g_edicts + j;
j < globals.num_edicts;
j++, e2++)
{
if (e2 == e)
{
continue;
}
if (!e2->inuse)
{
continue;
}
if (!e2->team)
{
continue;
}
if (!strcmp(e->team, e2->team))
{
c2++;
chain->teamchain = e2;
e2->teammaster = e;
e2->teamchain = NULL;
chain = e2;
e2->flags |= FL_TEAMSLAVE;
e2->movetype = MOVETYPE_PUSH;
e2->speed = e->speed;
}
}
}
}
}
gi.dprintf("%i teams repaired\n", c);
}
void void
G_FindTeams(void) G_FindTeams(void)
{ {
@ -522,6 +610,8 @@ G_FindTeams(void)
} }
} }
G_FixTeams();
gi.dprintf("%i teams with %i entities.\n", c, c2); gi.dprintf("%i teams with %i entities.\n", c, c2);
} }
@ -534,10 +624,15 @@ SpawnEntities(const char *mapname, char *entities, const char *spawnpoint)
{ {
edict_t *ent; edict_t *ent;
int inhibit; int inhibit;
char *com_token; const char *com_token;
int i; int i;
float skill_level; float skill_level;
if (!mapname || !entities || !spawnpoint)
{
return;
}
skill_level = floor(skill->value); skill_level = floor(skill->value);
if (skill_level < 0) if (skill_level < 0)
@ -562,8 +657,8 @@ SpawnEntities(const char *mapname, char *entities, const char *spawnpoint)
memset(&level, 0, sizeof(level)); memset(&level, 0, sizeof(level));
memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0]));
strncpy(level.mapname, mapname, sizeof(level.mapname) - 1); Q_strlcpy(level.mapname, mapname, sizeof(level.mapname));
strncpy(game.spawnpoint, spawnpoint, sizeof(game.spawnpoint) - 1); Q_strlcpy(game.spawnpoint, spawnpoint, sizeof(game.spawnpoint));
/* set client fields on player ents */ /* set client fields on player ents */
for (i = 0; i < game.maxclients; i++) for (i = 0; i < game.maxclients; i++)
@ -603,12 +698,34 @@ SpawnEntities(const char *mapname, char *entities, const char *spawnpoint)
/* yet another map hack */ /* yet another map hack */
if (!Q_stricmp(level.mapname, "command") && if (!Q_stricmp(level.mapname, "command") &&
!Q_stricmp(ent->classname, !Q_stricmp(ent->classname, "trigger_once") &&
"trigger_once") && !Q_stricmp(ent->model, "*27")) !Q_stricmp(ent->model, "*27"))
{ {
ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; ent->spawnflags &= ~SPAWNFLAG_NOT_HARD;
} }
/* ahh, the joys of map hacks .. */
if (!Q_stricmp(level.mapname, "rhangar2") &&
!Q_stricmp(ent->classname, "func_door_rotating") &&
ent->targetname && !Q_stricmp(ent->targetname, "t265"))
{
ent->spawnflags &= ~SPAWNFLAG_NOT_COOP;
}
if (!Q_stricmp(level.mapname, "rhangar2") &&
!Q_stricmp(ent->classname, "trigger_always") &&
ent->target && !Q_stricmp(ent->target, "t265"))
{
ent->spawnflags |= SPAWNFLAG_NOT_COOP;
}
if (!Q_stricmp(level.mapname, "rhangar2") &&
!Q_stricmp(ent->classname, "func_wall") &&
!Q_stricmp(ent->model, "*15"))
{
ent->spawnflags |= SPAWNFLAG_NOT_COOP;
}
/* remove things (except the world) from /* remove things (except the world) from
different skill levels or deathmatch */ different skill levels or deathmatch */
if (ent != g_edicts) if (ent != g_edicts)

View File

@ -60,6 +60,12 @@ MoveClientToIntermission(edict_t *ent)
ent->client->quadfire_framenum = 0; ent->client->quadfire_framenum = 0;
ent->client->trap_blew_up = false; ent->client->trap_blew_up = false;
ent->client->trap_time = 0; ent->client->trap_time = 0;
ent->client->ir_framenum = 0;
ent->client->nuke_framenum = 0;
ent->client->double_framenum = 0;
ent->client->ps.rdflags &= ~RDF_IRGOGGLES;
ent->viewheight = 0; ent->viewheight = 0;
ent->s.modelindex = 0; ent->s.modelindex = 0;
@ -307,6 +313,15 @@ DeathmatchScoreboardMessage(edict_t *ent, edict_t *killer)
tag = NULL; tag = NULL;
} }
/* allow new DM games to override the tag picture */
if (gamerules && gamerules->value)
{
if (DMGame.DogTag)
{
DMGame.DogTag(cl_ent, killer, &tag);
}
}
if (tag) if (tag)
{ {
Com_sprintf(entry, sizeof(entry), Com_sprintf(entry, sizeof(entry),
@ -342,6 +357,9 @@ DeathmatchScoreboardMessage(edict_t *ent, edict_t *killer)
gi.WriteString(string); gi.WriteString(string);
} }
/*
* Draw help computer.
*/
void void
HelpComputerMessage(edict_t *ent) HelpComputerMessage(edict_t *ent)
{ {
@ -391,6 +409,9 @@ HelpComputerMessage(edict_t *ent)
gi.WriteString(string); gi.WriteString(string);
} }
/*
* Display the current help message
*/
void void
InventoryMessage(edict_t *ent) InventoryMessage(edict_t *ent)
{ {
@ -494,6 +515,12 @@ G_SetStats(edict_t *ent)
ent->client->ps.stats[STAT_TIMER] = ent->client->ps.stats[STAT_TIMER] =
(ent->client->quad_framenum - level.framenum) / 10; (ent->client->quad_framenum - level.framenum) / 10;
} }
else if (ent->client->double_framenum > level.framenum)
{
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_double");
ent->client->ps.stats[STAT_TIMER] =
(ent->client->double_framenum - level.framenum) / 10;
}
else if (ent->client->quadfire_framenum > level.framenum) else if (ent->client->quadfire_framenum > level.framenum)
{ {
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_quadfire"); ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_quadfire");
@ -519,6 +546,35 @@ G_SetStats(edict_t *ent)
ent->client->ps.stats[STAT_TIMER] = ent->client->ps.stats[STAT_TIMER] =
(ent->client->breather_framenum - level.framenum) / 10; (ent->client->breather_framenum - level.framenum) / 10;
} }
else if (ent->client->owned_sphere)
{
if (ent->client->owned_sphere->spawnflags == 1) /* defender */
{
ent->client->ps.stats[STAT_TIMER_ICON] =
gi.imageindex("p_defender");
}
else if (ent->client->owned_sphere->spawnflags == 2) /* hunter */
{
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_hunter");
}
else if (ent->client->owned_sphere->spawnflags == 4) /* vengeance */
{
ent->client->ps.stats[STAT_TIMER_ICON] =
gi.imageindex("p_vengeance");
}
else /* error case */
{
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("i_fixme");
}
ent->client->ps.stats[STAT_TIMER] =
(int)(ent->client->owned_sphere->wait - level.time);
}
else if (ent->client->ir_framenum > level.framenum)
{
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_ir");
ent->client->ps.stats[STAT_TIMER] =
(ent->client->ir_framenum - level.framenum) / 10; }
else else
{ {
ent->client->ps.stats[STAT_TIMER_ICON] = 0; ent->client->ps.stats[STAT_TIMER_ICON] = 0;

View File

@ -281,6 +281,12 @@ SV_CalcViewOffset(edict_t *ent)
float delta; float delta;
vec3_t v; vec3_t v;
if (!ent)
{
return;
}
/* base angles */ /* base angles */
angles = ent->client->ps.kick_angles; angles = ent->client->ps.kick_angles;
@ -289,8 +295,17 @@ SV_CalcViewOffset(edict_t *ent)
{ {
VectorClear(angles); VectorClear(angles);
if (ent->flags & FL_SAM_RAIMI)
{
ent->client->ps.viewangles[ROLL] = 0;
ent->client->ps.viewangles[PITCH] = 0;
}
else
{
ent->client->ps.viewangles[ROLL] = 40; ent->client->ps.viewangles[ROLL] = 40;
ent->client->ps.viewangles[PITCH] = -15; ent->client->ps.viewangles[PITCH] = -15;
}
ent->client->ps.viewangles[YAW] = ent->client->killer_yaw; ent->client->ps.viewangles[YAW] = ent->client->killer_yaw;
} }
else else
@ -352,6 +367,8 @@ SV_CalcViewOffset(edict_t *ent)
angles[ROLL] += delta; angles[ROLL] += delta;
} }
/* =================================== */
/* base origin */ /* base origin */
VectorClear(v); VectorClear(v);
@ -419,12 +436,21 @@ SV_CalcGunOffset(edict_t *ent)
{ {
int i; int i;
float delta; float delta;
static gitem_t *heatbeam;
if (!ent) if (!ent)
{ {
return; return;
} }
if (!heatbeam)
{
heatbeam = FindItemByClassname("weapon_plasmabeam");
}
/* heatbeam shouldn't bob so the beam looks right */
if (ent->client->pers.weapon != heatbeam)
{
/* gun angles from bobbing */ /* gun angles from bobbing */
ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005; ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005;
ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01; ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01;
@ -440,7 +466,8 @@ SV_CalcGunOffset(edict_t *ent)
/* gun angles from delta movement */ /* gun angles from delta movement */
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
{ {
delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i]; delta = ent->client->oldviewangles[i] -
ent->client->ps.viewangles[i];
if (delta > 180) if (delta > 180)
{ {
@ -469,6 +496,14 @@ SV_CalcGunOffset(edict_t *ent)
ent->client->ps.gunangles[i] += 0.2 * delta; ent->client->ps.gunangles[i] += 0.2 * delta;
} }
}
else
{
for (i = 0; i < 3; i++)
{
ent->client->ps.gunangles[i] = 0;
}
}
/* gun height */ /* gun height */
VectorClear(ent->client->ps.gunoffset); VectorClear(ent->client->ps.gunoffset);
@ -563,6 +598,21 @@ SV_CalcBlend(edict_t *ent)
SV_AddBlend(0, 0, 1, 0.08, ent->client->ps.blend); SV_AddBlend(0, 0, 1, 0.08, ent->client->ps.blend);
} }
} }
else if (ent->client->double_framenum > level.framenum)
{
remaining = ent->client->double_framenum - level.framenum;
if (remaining == 30) /* beginning to fade */
{
gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/ddamage2.wav"),
1, ATTN_NORM, 0);
}
if ((remaining > 30) || (remaining & 4))
{
SV_AddBlend(0.9, 0.7, 0, 0.08, ent->client->ps.blend);
}
}
else if (ent->client->quadfire_framenum > level.framenum) else if (ent->client->quadfire_framenum > level.framenum)
{ {
remaining = ent->client->quadfire_framenum - level.framenum; remaining = ent->client->quadfire_framenum - level.framenum;
@ -624,6 +674,32 @@ SV_CalcBlend(edict_t *ent)
} }
} }
if (ent->client->nuke_framenum > level.framenum)
{
float brightness;
brightness = (ent->client->nuke_framenum - level.framenum) / 20.0;
SV_AddBlend(1, 1, 1, brightness, ent->client->ps.blend);
}
if (ent->client->ir_framenum > level.framenum)
{
remaining = ent->client->ir_framenum - level.framenum;
if ((remaining > 30) || (remaining & 4))
{
ent->client->ps.rdflags |= RDF_IRGOGGLES;
SV_AddBlend(1, 0, 0, 0.2, ent->client->ps.blend);
}
else
{
ent->client->ps.rdflags &= ~RDF_IRGOGGLES;
}
}
else
{
ent->client->ps.rdflags &= ~RDF_IRGOGGLES;
}
/* add for damage */ /* add for damage */
if (ent->client->damage_alpha > 0) if (ent->client->damage_alpha > 0)
{ {
@ -991,6 +1067,8 @@ G_SetClientEffects(edict_t *ent)
} }
ent->s.effects = 0; ent->s.effects = 0;
/* player is always ir visible, even dead. */
ent->s.renderfx = RF_IR_VISIBLE; ent->s.renderfx = RF_IR_VISIBLE;
if ((ent->health <= 0) || level.intermissiontime) if ((ent->health <= 0) || level.intermissiontime)
@ -998,6 +1076,19 @@ G_SetClientEffects(edict_t *ent)
return; return;
} }
if (ent->flags & FL_DISGUISED)
{
ent->s.renderfx |= RF_USE_DISGUISE;
}
if (gamerules && gamerules->value)
{
if (DMGame.PlayerEffects)
{
DMGame.PlayerEffects(ent);
}
}
if (ent->powerarmor_time > level.time) if (ent->powerarmor_time > level.time)
{ {
pa_type = PowerArmorType(ent); pa_type = PowerArmorType(ent);
@ -1023,6 +1114,16 @@ G_SetClientEffects(edict_t *ent)
} }
} }
if (ent->client->double_framenum > level.framenum)
{
remaining = ent->client->double_framenum - level.framenum;
if ((remaining > 30) || (remaining & 4))
{
ent->s.effects |= EF_DOUBLE;
}
}
if (ent->client->quadfire_framenum > level.framenum) if (ent->client->quadfire_framenum > level.framenum)
{ {
remaining = ent->client->quadfire_framenum - level.framenum; remaining = ent->client->quadfire_framenum - level.framenum;
@ -1033,6 +1134,17 @@ G_SetClientEffects(edict_t *ent)
} }
} }
if ((ent->client->owned_sphere) &&
(ent->client->owned_sphere->spawnflags == 1))
{
ent->s.effects |= EF_HALF_DAMAGE;
}
if (ent->client->tracker_pain_framenum > level.framenum)
{
ent->s.effects |= EF_TRACKERTRAIL;
}
if (ent->client->invincible_framenum > level.framenum) if (ent->client->invincible_framenum > level.framenum)
{ {
remaining = ent->client->invincible_framenum - level.framenum; remaining = ent->client->invincible_framenum - level.framenum;
@ -1416,13 +1528,9 @@ ClientEndServerFrame(edict_t *ent)
} }
G_CheckChaseStats(ent); G_CheckChaseStats(ent);
G_SetClientEvent(ent); G_SetClientEvent(ent);
G_SetClientEffects(ent); G_SetClientEffects(ent);
G_SetClientSound(ent); G_SetClientSound(ent);
G_SetClientFrame(ent); G_SetClientFrame(ent);
VectorCopy(ent->velocity, ent->client->oldvelocity); VectorCopy(ent->velocity, ent->client->oldvelocity);

View File

@ -1,677 +0,0 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (c) ZeniMax Media 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.
*
* =======================================================================
*
* HUD, deathmatch scoreboard, help computer and intermission stuff.
*
* =======================================================================
*/
#include "../header/local.h"
void
MoveClientToIntermission(edict_t *ent)
{
if (!ent)
{
return;
}
if (deathmatch->value || coop->value)
{
ent->client->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_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 = false;
ent->client->grenade_time = 0;
ent->client->ps.rdflags &= ~RDF_IRGOGGLES;
ent->client->ir_framenum = 0;
ent->client->nuke_framenum = 0;
ent->client->double_framenum = 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;
gi.linkentity(ent);
/* add the layout */
if (deathmatch->value || coop->value)
{
DeathmatchScoreboardMessage(ent, NULL);
gi.unicast(ent, true);
}
}
void
BeginIntermission(edict_t *targ)
{
int i, n;
edict_t *ent, *client;
if (!targ)
{
return;
}
if (level.intermissiontime)
{
return; /* already activated */
}
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 (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 < game.num_items; n++)
{
if (itemlist[n].flags & IT_KEY)
{
client->client->pers.inventory[n] = 0;
}
}
client->client->pers.power_cubes = 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);
}
}
void
DeathmatchScoreboardMessage(edict_t *ent, edict_t *killer /* can be NULL */)
{
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;
char *tag;
if (!ent)
{
return;
}
/* 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];
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;
}
/* allow new DM games to override the tag picture */
if (gamerules && gamerules->value)
{
if (DMGame.DogTag)
{
DMGame.DogTag(cl_ent, killer, &tag);
}
}
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;
}
/* 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);
}
/*
* Draw help computer.
*/
void
HelpComputerMessage(edict_t *ent)
{
char string[1024];
char *sk;
if (!ent)
{
return;
}
if (skill->value == SKILL_EASY)
{
sk = "easy";
}
else if (skill->value == SKILL_MEDIUM)
{
sk = "medium";
}
else if (skill->value == SKILL_HARD)
{
sk = "hard";
}
else
{
sk = "hard+";
}
/* send the layout */
Com_sprintf(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);
}
/*
* Display the current help message
*/
void
InventoryMessage(edict_t *ent)
{
int i;
if (!ent)
{
return;
}
gi.WriteByte(svc_inventory);
for (i = 0; i < MAX_ITEMS; i++)
{
gi.WriteShort(ent->client->pers.inventory[i]);
}
}
/* ======================================================================= */
void
G_SetStats(edict_t *ent)
{
gitem_t *item;
int index, cells;
int power_armor_type;
if (!ent)
{
return;
}
/* health */
ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health;
ent->client->ps.stats[STAT_HEALTH] = (ent->health < -99) ? -99 : ent->health;
/* ammo */
if (!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->double_framenum > level.framenum)
{
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_double");
ent->client->ps.stats[STAT_TIMER] =
(ent->client->double_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 if (ent->client->owned_sphere)
{
if (ent->client->owned_sphere->spawnflags == 1) /* defender */
{
ent->client->ps.stats[STAT_TIMER_ICON] =
gi.imageindex("p_defender");
}
else if (ent->client->owned_sphere->spawnflags == 2) /* hunter */
{
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_hunter");
}
else if (ent->client->owned_sphere->spawnflags == 4) /* vengeance */
{
ent->client->ps.stats[STAT_TIMER_ICON] =
gi.imageindex("p_vengeance");
}
else /* error case */
{
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("i_fixme");
}
ent->client->ps.stats[STAT_TIMER] =
(int)(ent->client->owned_sphere->wait - level.time);
}
else if (ent->client->ir_framenum > level.framenum)
{
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_ir");
ent->client->ps.stats[STAT_TIMER] =
(ent->client->ir_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)
{
cvar_t *gun;
gun = gi.cvar("cl_gun", "2", 0);
if (gun->value != 2)
{
ent->client->ps.stats[STAT_HELPICON] = gi.imageindex(ent->client->pers.weapon->icon);
}
else
{
ent->client->ps.stats[STAT_HELPICON] = 0;
}
}
else
{
ent->client->ps.stats[STAT_HELPICON] = 0;
}
ent->client->ps.stats[STAT_SPECTATOR] = 0;
}
void
G_CheckChaseStats(edict_t *ent)
{
int i;
gclient_t *cl;
if (!ent)
{
return;
}
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);
}
}
void
G_SetSpectatorStats(edict_t *ent)
{
if (!ent)
{
return;
}
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;
}
}

View File

@ -1,176 +0,0 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (c) ZeniMax Media 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.
*
* =======================================================================
*
* The player trail, used by monsters to locate the player.
*
* =======================================================================
*/
#include "../header/local.h"
/*
* This is a circular list containing the a list of points of where
* the player has been recently. It is used by monsters for pursuit.
*
* .origin the spot
* .owner forward link
* .aiment backward link
*/
#define NEXT(n) (((n) + 1) & (TRAIL_LENGTH - 1))
#define PREV(n) (((n) - 1) & (TRAIL_LENGTH - 1))
#define TRAIL_LENGTH 8
edict_t *trail[TRAIL_LENGTH];
int trail_head;
qboolean trail_active = false;
void
PlayerTrail_Init(void)
{
int n;
if (deathmatch->value)
{
return;
}
for (n = 0; n < TRAIL_LENGTH; n++)
{
trail[n] = G_Spawn();
trail[n]->classname = "player_trail";
}
trail_head = 0;
trail_active = true;
}
void
PlayerTrail_Add(vec3_t spot)
{
vec3_t temp;
if (!trail_active)
{
return;
}
VectorCopy(spot, trail[trail_head]->s.origin);
trail[trail_head]->timestamp = level.time;
VectorSubtract(spot, trail[PREV(trail_head)]->s.origin, temp);
trail[trail_head]->s.angles[1] = vectoyaw(temp);
trail_head = NEXT(trail_head);
}
void
PlayerTrail_New(vec3_t spot)
{
if (!trail_active)
{
return;
}
PlayerTrail_Init();
PlayerTrail_Add(spot);
}
edict_t *
PlayerTrail_PickFirst(edict_t *self)
{
int marker;
int n;
if (!self)
{
return NULL;
}
if (!trail_active)
{
return NULL;
}
for (marker = trail_head, n = TRAIL_LENGTH; n; n--)
{
if (trail[marker]->timestamp <= self->monsterinfo.trail_time)
{
marker = NEXT(marker);
}
else
{
break;
}
}
if (visible(self, trail[marker]))
{
return trail[marker];
}
if (visible(self, trail[PREV(marker)]))
{
return trail[PREV(marker)];
}
return trail[marker];
}
edict_t *
PlayerTrail_PickNext(edict_t *self)
{
int marker;
int n;
if (!self)
{
return NULL;
}
if (!trail_active)
{
return NULL;
}
for (marker = trail_head, n = TRAIL_LENGTH; n; n--)
{
if (trail[marker]->timestamp <= self->monsterinfo.trail_time)
{
marker = NEXT(marker);
}
else
{
break;
}
}
return trail[marker];
}
edict_t *
PlayerTrail_LastSpot(void)
{
return trail[PREV(trail_head)];
}

File diff suppressed because it is too large Load Diff