/* * 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. * * ======================================================================= * * This file implements all static entities at client site. * * ======================================================================= */ #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 ; pnumnum_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; ent.skinnum = (s1->skinnum >> ((randk() % 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) V_AddLight (ent.origin, 225, 1.0f, 0.1f, 0.1f); else if (effects & EF_FLAG2) 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); i = (randk()%100) + 100; 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 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; 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 */ 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); }