yquake2remaster/src/client/cl_entities.c

696 lines
18 KiB
C
Raw Normal View History

/*
* 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.
*
2010-06-18 15:14:54 +00:00
* =======================================================================
*
* This file implements all static entities at client site.
*
* =======================================================================
*/
2009-03-03 13:43:32 +00:00
#include "header/client.h"
extern struct model_s *cl_mod_powerscreen;
int vidref_val;
struct model_s *S_RegisterSexedModel (entity_state_t *ent, char *base) {
int n;
char *p;
struct model_s *md2;
char model[MAX_QPATH];
char buffer[MAX_QPATH];
/* determine what model the client is using */
model[0] = 0;
n = CS_PLAYERSKINS + ent->number - 1;
if (cl.configstrings[n][0]) {
p = strchr(cl.configstrings[n], '\\');
if (p) {
p += 1;
strcpy(model, p);
p = strchr(model, '/');
if (p)
*p = 0;
}
}
/* if we can't figure it out, they're male */
if (!model[0])
strcpy(model, "male");
Com_sprintf (buffer, sizeof(buffer), "players/%s/%s", model, base+1);
md2 = re.RegisterModel(buffer);
if (!md2) {
/* not found, try default weapon model */
Com_sprintf (buffer, sizeof(buffer), "players/%s/weapon.md2", model);
md2 = re.RegisterModel(buffer);
if (!md2) {
/* no, revert to the male model */
Com_sprintf (buffer, sizeof(buffer), "players/%s/%s", "male", base+1);
md2 = re.RegisterModel(buffer);
if (!md2) {
/* last try, default male weapon.md2 */
Com_sprintf (buffer, sizeof(buffer), "players/male/weapon.md2");
md2 = re.RegisterModel(buffer);
}
}
}
return md2;
}
extern int Developer_searchpath (int who);
void CL_AddPacketEntities (frame_t *frame) {
entity_t ent = {0};
entity_state_t *s1;
float autorotate;
int i;
int pnum;
centity_t *cent;
int autoanim;
clientinfo_t *ci;
unsigned int effects, renderfx;
/* bonus items rotate at a fixed rate */
autorotate = anglemod(cl.time*0.1f);
/* brush models can auto animate their frames */
autoanim = 2*cl.time/1000;
for (pnum = 0 ; pnum<frame->num_entities ; pnum++) {
s1 = &cl_parse_entities[(frame->parse_entities+pnum)&(MAX_PARSE_ENTITIES-1)];
cent = &cl_entities[s1->number];
effects = s1->effects;
renderfx = s1->renderfx;
/* set frame */
if (effects & EF_ANIM01)
ent.frame = autoanim & 1;
else if (effects & EF_ANIM23)
ent.frame = 2 + (autoanim & 1);
else if (effects & EF_ANIM_ALL)
ent.frame = autoanim;
else if (effects & EF_ANIM_ALLFAST)
ent.frame = cl.time / 100;
else
ent.frame = s1->frame;
/* quad and pent can do different things on client */
if (effects & EF_PENT) {
effects &= ~EF_PENT;
effects |= EF_COLOR_SHELL;
renderfx |= RF_SHELL_RED;
}
if (effects & EF_QUAD) {
effects &= ~EF_QUAD;
effects |= EF_COLOR_SHELL;
renderfx |= RF_SHELL_BLUE;
}
if (effects & EF_DOUBLE) {
effects &= ~EF_DOUBLE;
effects |= EF_COLOR_SHELL;
renderfx |= RF_SHELL_DOUBLE;
}
if (effects & EF_HALF_DAMAGE) {
effects &= ~EF_HALF_DAMAGE;
effects |= EF_COLOR_SHELL;
renderfx |= RF_SHELL_HALF_DAM;
}
ent.oldframe = cent->prev.frame;
ent.backlerp = 1.0f - cl.lerpfrac;
if (renderfx & (RF_FRAMELERP|RF_BEAM)) {
/* step origin discretely, because the frames
* do the animation properly */
VectorCopy (cent->current.origin, ent.origin);
VectorCopy (cent->current.old_origin, ent.oldorigin);
} else {
/* interpolate origin */
for (i=0 ; i<3 ; i++) {
ent.origin[i] = ent.oldorigin[i] = cent->prev.origin[i] + cl.lerpfrac *
(cent->current.origin[i] - cent->prev.origin[i]);
}
}
/* tweak the color of beams */
if ( renderfx & RF_BEAM ) {
/* the four beam colors are encoded in 32 bits of skinnum (hack) */
ent.alpha = 0.30f;
2010-06-17 15:11:51 +00:00
ent.skinnum = (s1->skinnum >> ((rand() % 4)*8)) & 0xff;
ent.model = NULL;
} else {
/* set skin */
if (s1->modelindex == 255) {
/* use custom player skin */
ent.skinnum = 0;
ci = &cl.clientinfo[s1->skinnum & 0xff];
ent.skin = ci->skin;
ent.model = ci->model;
if (!ent.skin || !ent.model) {
ent.skin = cl.baseclientinfo.skin;
ent.model = cl.baseclientinfo.model;
}
if (renderfx & RF_USE_DISGUISE) {
if (ent.skin != NULL) {
if(!strncmp((char *)ent.skin, "players/male", 12)) {
ent.skin = re.RegisterSkin ("players/male/disguise.pcx");
ent.model = re.RegisterModel ("players/male/tris.md2");
} else if(!strncmp((char *)ent.skin, "players/female", 14)) {
ent.skin = re.RegisterSkin ("players/female/disguise.pcx");
ent.model = re.RegisterModel ("players/female/tris.md2");
} else if(!strncmp((char *)ent.skin, "players/cyborg", 14)) {
ent.skin = re.RegisterSkin ("players/cyborg/disguise.pcx");
ent.model = re.RegisterModel ("players/cyborg/tris.md2");
}
}
}
} else {
ent.skinnum = s1->skinnum;
ent.skin = NULL;
ent.model = cl.model_draw[s1->modelindex];
}
}
/* only used for black hole model right now */
if (renderfx & RF_TRANSLUCENT && !(renderfx & RF_BEAM))
ent.alpha = 0.70f;
/* render effects (fullbright, translucent, etc) */
if ((effects & EF_COLOR_SHELL))
ent.flags = 0; /* renderfx go on color shell entity */
else
ent.flags = renderfx;
/* calculate angles */
if (effects & EF_ROTATE) {
/* some bonus items auto-rotate */
ent.angles[0] = 0;
ent.angles[1] = autorotate;
ent.angles[2] = 0;
} else if (effects & EF_SPINNINGLIGHTS) {
ent.angles[0] = 0;
ent.angles[1] = anglemod(cl.time/2) + s1->angles[1];
ent.angles[2] = 180;
{
vec3_t forward;
vec3_t start;
AngleVectors (ent.angles, forward, NULL, NULL);
VectorMA (ent.origin, 64, forward, start);
V_AddLight (start, 100, 1, 0, 0);
}
} else {
/* interpolate angles */
float a1, a2;
for (i=0 ; i<3 ; i++) {
a1 = cent->current.angles[i];
a2 = cent->prev.angles[i];
ent.angles[i] = LerpAngle (a2, a1, cl.lerpfrac);
}
}
if (s1->number == cl.playernum+1) {
ent.flags |= RF_VIEWERMODEL;
if (effects & EF_FLAG1)
2009-03-01 11:40:49 +00:00
V_AddLight (ent.origin, 225, 1.0f, 0.1f, 0.1f);
else if (effects & EF_FLAG2)
2009-03-01 11:40:49 +00:00
V_AddLight (ent.origin, 225, 0.1f, 0.1f, 1.0f);
else if (effects & EF_TAGTRAIL)
V_AddLight (ent.origin, 225, 1.0f, 1.0f, 0.0f);
else if (effects & EF_TRACKERTRAIL)
V_AddLight (ent.origin, 225, -1.0f, -1.0f, -1.0f);
continue;
}
/* if set to invisible, skip */
if (!s1->modelindex)
continue;
if (effects & EF_BFG) {
ent.flags |= RF_TRANSLUCENT;
ent.alpha = 0.30f;
}
if (effects & EF_PLASMA) {
ent.flags |= RF_TRANSLUCENT;
ent.alpha = 0.6f;
}
if (effects & EF_SPHERETRANS) {
ent.flags |= RF_TRANSLUCENT;
if (effects & EF_TRACKERTRAIL)
ent.alpha = 0.6f;
else
ent.alpha = 0.3f;
}
/* add to refresh list */
V_AddEntity (&ent);
/* color shells generate a seperate entity for the main model */
if (effects & EF_COLOR_SHELL) {
/* all of the solo colors are fine. we need to catch any of
* the combinations that look bad (double & half) and turn
* them into the appropriate color, and make double/quad
* something special */
if (renderfx & RF_SHELL_HALF_DAM) {
if(Developer_searchpath(2) == 2) {
/* ditch the half damage shell if any of red, blue, or double are on */
if (renderfx & (RF_SHELL_RED|RF_SHELL_BLUE|RF_SHELL_DOUBLE))
renderfx &= ~RF_SHELL_HALF_DAM;
}
}
if (renderfx & RF_SHELL_DOUBLE) {
if(Developer_searchpath(2) == 2) {
/* lose the yellow shell if we have a red, blue, or green shell */
if (renderfx & (RF_SHELL_RED|RF_SHELL_BLUE|RF_SHELL_GREEN))
renderfx &= ~RF_SHELL_DOUBLE;
/* if we have a red shell, turn it to purple by adding blue */
if (renderfx & RF_SHELL_RED)
renderfx |= RF_SHELL_BLUE;
/* if we have a blue shell (and not a red shell), turn it to cyan by adding green */
else if (renderfx & RF_SHELL_BLUE) {
/* go to green if it's on already, otherwise do cyan (flash green) */
if (renderfx & RF_SHELL_GREEN)
renderfx &= ~RF_SHELL_BLUE;
else
renderfx |= RF_SHELL_GREEN;
}
}
}
ent.flags = renderfx | RF_TRANSLUCENT;
ent.alpha = 0.30f;
V_AddEntity (&ent);
}
ent.skin = NULL; /* never use a custom skin on others */
ent.skinnum = 0;
ent.flags = 0;
ent.alpha = 0;
/* duplicate for linked models */
if (s1->modelindex2) {
if (s1->modelindex2 == 255) {
/* custom weapon */
ci = &cl.clientinfo[s1->skinnum & 0xff];
i = (s1->skinnum >> 8); /* 0 is default weapon model */
if (!cl_vwep->value || i > MAX_CLIENTWEAPONMODELS - 1)
i = 0;
ent.model = ci->weaponmodel[i];
if (!ent.model) {
if (i != 0)
ent.model = ci->weaponmodel[0];
if (!ent.model)
ent.model = cl.baseclientinfo.weaponmodel[0];
}
} else
ent.model = cl.model_draw[s1->modelindex2];
/* check for the defender sphere shell and make it translucent */
if (!Q_strcasecmp (cl.configstrings[CS_MODELS+(s1->modelindex2)], "models/items/shell/tris.md2")) {
ent.alpha = 0.32f;
ent.flags = RF_TRANSLUCENT;
}
V_AddEntity (&ent);
ent.flags = 0;
ent.alpha = 0;
}
if (s1->modelindex3) {
ent.model = cl.model_draw[s1->modelindex3];
V_AddEntity (&ent);
}
if (s1->modelindex4) {
ent.model = cl.model_draw[s1->modelindex4];
V_AddEntity (&ent);
}
if ( effects & EF_POWERSCREEN ) {
ent.model = cl_mod_powerscreen;
ent.oldframe = 0;
ent.frame = 0;
ent.flags |= (RF_TRANSLUCENT | RF_SHELL_GREEN);
ent.alpha = 0.30f;
V_AddEntity (&ent);
}
/* add automatic particle trails */
if ( (effects&~EF_ROTATE) ) {
if (effects & EF_ROCKET) {
CL_RocketTrail (cent->lerp_origin, ent.origin, cent);
V_AddLight (ent.origin, 200, 1, 0.25f, 0);
}
/* Do not reorder EF_BLASTER and EF_HYPERBLASTER.
* EF_BLASTER | EF_TRACKER is a special case for
* EF_BLASTER2 */
else if (effects & EF_BLASTER) {
if (effects & EF_TRACKER) {
CL_BlasterTrail2 (cent->lerp_origin, ent.origin);
V_AddLight (ent.origin, 200, 0, 1, 0);
} else {
CL_BlasterTrail (cent->lerp_origin, ent.origin);
V_AddLight (ent.origin, 200, 1, 1, 0);
}
} else if (effects & EF_HYPERBLASTER) {
if (effects & EF_TRACKER)
V_AddLight (ent.origin, 200, 0, 1, 0);
else
V_AddLight (ent.origin, 200, 1, 1, 0);
} else if (effects & EF_GIB) {
CL_DiminishingTrail (cent->lerp_origin, ent.origin, cent, effects);
} else if (effects & EF_GRENADE) {
CL_DiminishingTrail (cent->lerp_origin, ent.origin, cent, effects);
} else if (effects & EF_FLIES) {
CL_FlyEffect (cent, ent.origin);
} else if (effects & EF_BFG) {
static int bfg_lightramp[6] = {300, 400, 600, 300, 150, 75};
if (effects & EF_ANIM_ALLFAST) {
CL_BfgParticles (&ent);
i = 200;
} else {
i = bfg_lightramp[s1->frame];
}
V_AddLight (ent.origin, i, 0, 1, 0);
} else if (effects & EF_TRAP) {
ent.origin[2] += 32;
CL_TrapParticles (&ent);
2010-06-17 15:11:51 +00:00
i = (rand()%100) + 100;
2009-03-01 11:40:49 +00:00
V_AddLight (ent.origin, i, 1, 0.8f, 0.1f);
} else if (effects & EF_FLAG1) {
CL_FlagTrail (cent->lerp_origin, ent.origin, 242);
V_AddLight (ent.origin, 225, 1, 0.1f, 0.1f);
} else if (effects & EF_FLAG2) {
CL_FlagTrail (cent->lerp_origin, ent.origin, 115);
V_AddLight (ent.origin, 225, 0.1f, 0.1f, 1);
} else if (effects & EF_TAGTRAIL) {
CL_TagTrail (cent->lerp_origin, ent.origin, 220);
V_AddLight (ent.origin, 225, 1.0, 1.0, 0.0);
} else if (effects & EF_TRACKERTRAIL) {
if (effects & EF_TRACKER) {
float intensity;
intensity = 50 + (500 * ((float)sin(cl.time/500.0f) + 1.0f));
if(vidref_val == VIDREF_GL)
V_AddLight (ent.origin, intensity, -1.0, -1.0, -1.0);
else
2009-03-01 11:40:49 +00:00
V_AddLight (ent.origin, -1.0f * intensity, 1.0f, 1.0f, 1.0f);
} else {
CL_Tracker_Shell (cent->lerp_origin);
V_AddLight (ent.origin, 155, -1.0, -1.0, -1.0);
}
} else if (effects & EF_TRACKER) {
CL_TrackerTrail (cent->lerp_origin, ent.origin, 0);
if(vidref_val == VIDREF_GL)
V_AddLight (ent.origin, 200, -1, -1, -1);
else
V_AddLight (ent.origin, -200, 1, 1, 1);
} else if (effects & EF_IONRIPPER) {
CL_IonripperTrail (cent->lerp_origin, ent.origin);
V_AddLight (ent.origin, 100, 1, 0.5, 0.5);
} else if (effects & EF_BLUEHYPERBLASTER) {
V_AddLight (ent.origin, 200, 0, 0, 1);
} else if (effects & EF_PLASMA) {
if (effects & EF_ANIM_ALLFAST) {
CL_BlasterTrail (cent->lerp_origin, ent.origin);
}
V_AddLight (ent.origin, 130, 1, 0.5, 0.5);
}
}
VectorCopy (ent.origin, cent->lerp_origin);
}
}
void CL_AddViewWeapon (player_state_t *ps, player_state_t *ops) {
entity_t gun = {0}; /* view model */
int i;
/* allow the gun to be completely removed */
if (!cl_gun->value)
return;
/* don't draw gun if in wide angle view and drawing not forced */
if (ps->fov > 90)
if (cl_gun->value < 2)
return;
if (gun_model)
gun.model = gun_model;
else
gun.model = cl.model_draw[ps->gunindex];
if (!gun.model)
return;
/* set up gun position */
for (i=0 ; i<3 ; i++) {
gun.origin[i] = cl.refdef.vieworg[i] + ops->gunoffset[i]
+ cl.lerpfrac * (ps->gunoffset[i] - ops->gunoffset[i]);
gun.angles[i] = cl.refdef.viewangles[i] + LerpAngle (ops->gunangles[i],
ps->gunangles[i], cl.lerpfrac);
}
if (gun_frame) {
gun.frame = gun_frame;
gun.oldframe = gun_frame;
} else {
gun.frame = ps->gunframe;
if (gun.frame == 0)
gun.oldframe = 0; /* just changed weapons, don't lerp from old */
else
gun.oldframe = ops->gunframe;
}
gun.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL;
gun.backlerp = 1.0f - cl.lerpfrac;
VectorCopy (gun.origin, gun.oldorigin); /* don't lerp at all */
V_AddEntity (&gun);
}
/*
* Sets cl.refdef view values
*/
void CL_CalcViewValues (void) {
int i;
float lerp, backlerp;
centity_t *ent;
frame_t *oldframe;
player_state_t *ps, *ops;
/* find the previous frame to interpolate from */
ps = &cl.frame.playerstate;
i = (cl.frame.serverframe - 1) & UPDATE_MASK;
oldframe = &cl.frames[i];
if (oldframe->serverframe != cl.frame.serverframe-1 || !oldframe->valid)
oldframe = &cl.frame; /* previous frame was dropped or invalid */
ops = &oldframe->playerstate;
/* see if the player entity was teleported this frame */
if ( fabs(ops->pmove.origin[0] - ps->pmove.origin[0]) > 256*8
|| abs(ops->pmove.origin[1] - ps->pmove.origin[1]) > 256*8
|| abs(ops->pmove.origin[2] - ps->pmove.origin[2]) > 256*8)
ops = ps; /* don't interpolate */
ent = &cl_entities[cl.playernum+1];
lerp = cl.lerpfrac;
/* calculate the origin */
if ((cl_predict->value) && !(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION)) {
/* use predicted values */
unsigned delta;
backlerp = 1.0f - lerp;
for (i=0 ; i<3 ; i++) {
cl.refdef.vieworg[i] = cl.predicted_origin[i] + ops->viewoffset[i]
+ cl.lerpfrac * (ps->viewoffset[i] - ops->viewoffset[i])
- backlerp * cl.prediction_error[i];
}
/* smooth out stair climbing */
delta = cls.realtime - cl.predicted_step_time;
if (delta < 100)
cl.refdef.vieworg[2] -= cl.predicted_step * (100 - delta) * 0.01;
} else {
/* just use interpolated values */
for (i=0 ; i<3 ; i++)
cl.refdef.vieworg[i] = ops->pmove.origin[i]*0.125 + ops->viewoffset[i]
+ lerp * (ps->pmove.origin[i]*0.125 + ps->viewoffset[i]
- (ops->pmove.origin[i]*0.125 + ops->viewoffset[i]) );
}
/* if not running a demo or on a locked frame, add the local angle movement */
if ( cl.frame.playerstate.pmove.pm_type < PM_DEAD ) {
/* use predicted values */
for (i=0 ; i<3 ; i++)
cl.refdef.viewangles[i] = cl.predicted_angles[i];
} else {
/* just use interpolated values */
for (i=0 ; i<3 ; i++)
cl.refdef.viewangles[i] = LerpAngle (ops->viewangles[i], ps->viewangles[i], lerp);
}
for (i=0 ; i<3 ; i++)
cl.refdef.viewangles[i] += LerpAngle (ops->kick_angles[i], ps->kick_angles[i], lerp);
AngleVectors (cl.refdef.viewangles, cl.v_forward, cl.v_right, cl.v_up);
/* interpolate field of view */
cl.refdef.fov_x = ops->fov + lerp * (ps->fov - ops->fov);
/* don't interpolate blend color */
for (i=0 ; i<4 ; i++)
cl.refdef.blend[i] = ps->blend[i];
/* add the weapon */
CL_AddViewWeapon (ps, ops);
}
/*
* Emits all entities, particles, and lights to the refresh
*/
void CL_AddEntities (void) {
if (cls.state != ca_active)
return;
if (cl.time > cl.frame.servertime) {
if (cl_showclamp->value)
Com_Printf ("high clamp %i\n", cl.time - cl.frame.servertime);
cl.time = cl.frame.servertime;
cl.lerpfrac = 1.0;
} else if (cl.time < cl.frame.servertime - 100) {
if (cl_showclamp->value)
Com_Printf ("low clamp %i\n", cl.frame.servertime-100 - cl.time);
cl.time = cl.frame.servertime - 100;
cl.lerpfrac = 0;
} else
cl.lerpfrac = 1.0 - (cl.frame.servertime - cl.time) * 0.01f;
if (cl_timedemo->value)
cl.lerpfrac = 1.0;
CL_CalcViewValues ();
CL_AddPacketEntities (&cl.frame);
CL_AddTEnts ();
CL_AddParticles ();
CL_AddDLights ();
CL_AddLightStyles ();
}
/*
* Called to get the sound spatialization origin
*/
void CL_GetEntitySoundOrigin (int ent, vec3_t org) {
centity_t *old;
if (ent < 0 || ent >= MAX_EDICTS)
Com_Error (ERR_DROP, "CL_GetEntitySoundOrigin: bad ent");
old = &cl_entities[ent];
VectorCopy (old->lerp_origin, org);
}
2010-06-17 06:27:32 +00:00