#include "image.h"
#include "csprogs.h"
#include "r_shadow.h"
+#include "libcurl.h"
// we need to declare some mouse variables here, because the menu system
// references them even when on a unix system.
cvar_t freelook = {CVAR_SAVE, "freelook", "1","mouse controls pitch instead of forward/back"};
+#ifdef AUTODEMO_BROKEN
cvar_t cl_autodemo = {0, "cl_autodemo", "0", "records every game played, using the date/time and map name to name the demo file" };
cvar_t cl_autodemo_nameformat = {0, "cl_autodemo_nameformat", "%Y-%m-%d_%H-%M", "The format of the cl_autodemo filename, followed by the map name" };
+#endif
cvar_t r_draweffects = {0, "r_draweffects", "1","renders temporary sprite effects"};
cvar_t cl_stainmaps_clearonload = {CVAR_SAVE, "cl_stainmaps_clearonload", "1","clear stainmaps on map restart"};
cvar_t cl_beams_polygons = {CVAR_SAVE, "cl_beams_polygons", "1","use beam polygons instead of models"};
-cvar_t cl_beams_relative = {CVAR_SAVE, "cl_beams_relative", "1","beams are relative to owner (smooth sweeps)"};
-cvar_t cl_beams_lightatend = {CVAR_SAVE, "cl_beams_lightatend", "0","make a light at the end of the beam"};
+cvar_t cl_beams_quakepositionhack = {CVAR_SAVE, "cl_beams_quakepositionhack", "1", "makes your lightning gun appear to fire from your waist (as in Quake and QuakeWorld)"};
+cvar_t cl_beams_instantaimhack = {CVAR_SAVE, "cl_beams_instantaimhack", "1", "makes your lightning gun aiming update instantly"};
+cvar_t cl_beams_lightatend = {CVAR_SAVE, "cl_beams_lightatend", "0", "make a light at the end of the beam"};
cvar_t cl_noplayershadow = {CVAR_SAVE, "cl_noplayershadow", "0","hide player shadow"};
return;
}
InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), key, value);
- if (cls.state == ca_connected)
+ if (cls.state == ca_connected && cls.netcon)
{
if (cls.protocol == PROTOCOL_QUAKEWORLD)
{
// stop sounds (especially looping!)
S_StopAllSounds ();
+ cl.parsingtextexpectingpingforscores = 0; // just in case no reply has come yet
+
// clear contents blends
cl.cshifts[0].percent = 0;
cl.cshifts[1].percent = 0;
// stop demo loop in case this fails
CL_Disconnect();
+ // if downloads are running, cancel their finishing action
+ Curl_Clear_forthismap();
+
// make sure the client ports are open before attempting to connect
NetConn_UpdateSockets();
for (i = 0, ent = cl.entities;i < cl.num_entities;i++, ent++)
{
+ const char* modelname;
+
if (!ent->state_current.active)
continue;
if (ent->render.model)
- strlcpy (name, ent->render.model->name, 25);
+ modelname = ent->render.model->name;
else
- strcpy(name, "--no model--");
+ modelname = "--no model--";
+ strlcpy(name, modelname, 25);
for (j = (int)strlen(name);j < 25;j++)
name[j] = ' ';
Con_Printf("%3i: %s:%4i (%5i %5i %5i) [%3i %3i %3i] %4.2f %5.3f\n", i, name, ent->render.frame, (int) ent->render.matrix.m[0][3], (int) ent->render.matrix.m[1][3], (int) ent->render.matrix.m[2][3], (int) ent->render.angles[0] % 360, (int) ent->render.angles[1] % 360, (int) ent->render.angles[2] % 360, ent->render.scale, ent->render.alpha);
extern void V_CalcRefdef(void);
// note this is a recursive function, but it can never get in a runaway loop (because of the delayedlink flags)
-void CL_LinkNetworkEntity(entity_t *e)
+void CL_UpdateNetworkEntity(entity_t *e)
{
matrix4x4_t *matrix, blendmatrix, tempmatrix, matrix2;
//matrix4x4_t dlightmatrix;
if (!e->csqc)
{
if (cl.viewentity)
- CL_LinkNetworkEntity(cl.entities + cl.viewentity);
+ CL_UpdateNetworkEntity(cl.entities + cl.viewentity);
if (e == &cl.viewent && cl.entities[cl.viewentity].state_current.active)
{
e->state_current.alpha = cl.entities[cl.viewentity].state_current.alpha;
if (!t->state_current.active)
return;
// note: this can link to world
- CL_LinkNetworkEntity(t);
+ CL_UpdateNetworkEntity(t);
// make relative to the entity
matrix = &t->render.matrix;
// some properties of the tag entity carry over
// movement lerp
// if it's the player entity, update according to client movement
- if (e == cl.entities + cl.playerentity && cl.movement)// && !e->csqc)
+ if (e == cl.entities + cl.playerentity && cl.movement_predicted)// && !e->csqc)
{
lerp = (cl.time - cl.movement_time[1]) / (cl.movement_time[0] - cl.movement_time[1]);
lerp = bound(0, lerp, 1);
&& !(e->render.flags & (RENDER_VIEWMODEL | RENDER_TRANSPARENT))
&& (!(e->render.flags & RENDER_EXTERIORMODEL) || (!cl.intermission && cls.protocol != PROTOCOL_NEHAHRAMOVIE && !cl_noplayershadow.integer)))
e->render.flags |= RENDER_SHADOW;
- // as soon as player is known we can call V_CalcRefDef
- if (!csqc_loaded)
- if (e->state_current.number == cl.viewentity)
- V_CalcRefdef();
if (e->render.model && e->render.model->name[0] == '*' && e->render.model->TraceBox)
cl.brushmodel_entities[cl.num_brushmodel_entities++] = e->state_current.number;
+ // because the player may be attached to another entity, V_CalcRefdef must be deferred until here...
+ if (e->state_current.number == cl.viewentity)
+ V_CalcRefdef();
+ }
+}
+
+
+/*
+===============
+CL_UpdateEntities
+===============
+*/
+static void CL_UpdateEntities(void)
+{
+ entity_t *ent;
+ int i;
+
+ ent = &cl.viewent;
+ ent->state_previous = ent->state_current;
+ ent->state_current = defaultstate;
+ ent->state_current.time = cl.time;
+ ent->state_current.number = -1;
+ ent->state_current.active = true;
+ ent->state_current.modelindex = cl.stats[STAT_WEAPON];
+ ent->state_current.frame = cl.stats[STAT_WEAPONFRAME];
+ ent->state_current.flags = RENDER_VIEWMODEL;
+ if ((cl.stats[STAT_HEALTH] <= 0 && cl_deathnoviewmodel.integer) || cl.intermission)
+ ent->state_current.modelindex = 0;
+ else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
+ {
+ if (gamemode == GAME_TRANSFUSION)
+ ent->state_current.alpha = 128;
+ else
+ ent->state_current.modelindex = 0;
+ }
+
+ // reset animation interpolation on weaponmodel if model changed
+ if (ent->state_previous.modelindex != ent->state_current.modelindex)
+ {
+ ent->render.frame = ent->render.frame1 = ent->render.frame2 = ent->state_current.frame;
+ ent->render.frame1time = ent->render.frame2time = cl.time;
+ ent->render.framelerp = 1;
+ }
+
+ // start on the entity after the world
+ entitylinkframenumber++;
+ for (i = 1;i < cl.num_entities;i++)
+ {
+ if (cl.entities_active[i])
+ {
+ ent = cl.entities + i;
+ if (ent->state_current.active)
+ CL_UpdateNetworkEntity(ent);
+ else
+ cl.entities_active[i] = false;
+ }
+ }
+ CL_UpdateNetworkEntity(&cl.viewent);
+}
+
+// note this is a recursive function, but it can never get in a runaway loop (because of the delayedlink flags)
+void CL_LinkNetworkEntity(entity_t *e)
+{
+ entity_t *t;
+ if (e->persistent.linkframe != entitylinkframenumber)
+ {
+ e->persistent.linkframe = entitylinkframenumber;
+ // skip inactive entities and world
+ if (!e->state_current.active || e == cl.entities || e == cl.csqcentities)
+ return;
+ if (e->render.flags & RENDER_VIEWMODEL && !e->state_current.tagentity)
+ {
+ if (!r_drawviewmodel.integer || chase_active.integer || r_refdef.envmap)
+ return;
+ if (!e->csqc)
+ if (cl.viewentity)
+ CL_LinkNetworkEntity(cl.entities + cl.viewentity);
+ }
+ else
+ {
+ // if the tag entity is currently impossible, skip it
+ if (!e->csqc)
+ {
+ if (e->state_current.tagentity >= cl.num_entities)
+ return;
+ t = cl.entities + e->state_current.tagentity;
+ }
+ else
+ {
+ if (e->state_current.tagentity >= cl.num_csqcentities)
+ return;
+ t = cl.csqcentities + e->state_current.tagentity;
+ }
+ // if the tag entity is inactive, skip it
+ if (!t->state_current.active)
+ return;
+ // note: this can link to world
+ CL_LinkNetworkEntity(t);
+ }
+
// don't show entities with no modelindex (note: this still shows
// entities which have a modelindex that resolved to a NULL model)
if (e->render.model && !(e->render.effects & EF_NODRAW) && r_refdef.numentities < r_refdef.maxentities)
r_refdef.worldmodel = cl.worldmodel;
}
-void CL_RelinkCSQCWorld(void) //[515]: csqc
-{
- entity_t *ent = &cl.csqcentities[0];
- if(!csqc_loaded)
- return;
-// cl.brushmodel_entities[cl.num_brushmodel_entities++] = 0;
- // FIXME: this should be done at load
- ent->render.matrix = identitymatrix;
- ent->render.inversematrix = identitymatrix;
- R_LerpAnimation(&ent->render);
- CL_BoundingBoxForEntity(&ent->render);
- ent->render.flags = RENDER_SHADOW;
- if (!r_fullbright.integer)
- ent->render.flags |= RENDER_LIGHT;
- VectorSet(ent->render.colormod, 1, 1, 1);
-// r_refdef.worldentity = &ent->render;
-// r_refdef.worldmodel = cl.worldmodel;
-}
-
static void CL_RelinkStaticEntities(void)
{
int i;
CL_RelinkEntities
===============
*/
-static void CL_RelinkNetworkEntities(int drawmask)
+static void CL_RelinkNetworkEntities(void)
{
entity_t *ent;
- int i, k;
-
- if(!csqc_loaded)
- {
- ent = &cl.viewent;
- ent->state_previous = ent->state_current;
- ent->state_current = defaultstate;
- ent->state_current.time = cl.time;
- ent->state_current.number = -1;
- ent->state_current.active = true;
- ent->state_current.modelindex = cl.stats[STAT_WEAPON];
- ent->state_current.frame = cl.stats[STAT_WEAPONFRAME];
- ent->state_current.flags = RENDER_VIEWMODEL;
- if ((cl.stats[STAT_HEALTH] <= 0 && cl_deathnoviewmodel.integer) || cl.intermission)
- ent->state_current.modelindex = 0;
- else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
- {
- if (gamemode == GAME_TRANSFUSION)
- ent->state_current.alpha = 128;
- else
- ent->state_current.modelindex = 0;
- }
-
- // reset animation interpolation on weaponmodel if model changed
- if (ent->state_previous.modelindex != ent->state_current.modelindex)
- {
- ent->render.frame = ent->render.frame1 = ent->render.frame2 = ent->state_current.frame;
- ent->render.frame1time = ent->render.frame2time = cl.time;
- ent->render.framelerp = 1;
- }
- }
+ int i;
// start on the entity after the world
- entitylinkframenumber++;
- if(drawmask & ENTMASK_ENGINE || !csqc_loaded)
+ for (i = 1;i < cl.num_entities;i++)
{
- for (i = 1;i < cl.num_entities;i++)
+ if (cl.entities_active[i])
{
- if (cl.entities_active[i])
- {
- ent = cl.entities + i;
- if (!(drawmask & ENTMASK_ENGINEVIEWMODELS))
- if (ent->state_current.flags & RENDER_VIEWMODEL) //[515]: csqc drawmask
- {
- cl.entities_active[i] = false;
- continue;
- }
- if (ent->state_current.active)
- CL_LinkNetworkEntity(ent);
- else
- cl.entities_active[i] = false;
- }
- }
- }
-
- //[515]: csqc
- if(csqc_loaded)
- {
- for (i=1,k=cl.num_csqcentities;k;i++)
- {
- if (cl.csqcentities_active[i])
- {
- --k;
- ent = cl.csqcentities + i;
- if (ent->state_current.active)
- CL_LinkNetworkEntity(ent);
- else
- cl.csqcentities_active[i] = false;
- }
+ ent = cl.entities + i;
+ if (ent->state_current.active)
+ CL_LinkNetworkEntity(ent);
+ else
+ cl.entities_active[i] = false;
}
}
- else
- CL_LinkNetworkEntity(&cl.viewent);
}
static void CL_RelinkEffects(void)
}
}
+void CL_Beam_CalculatePositions(const beam_t *b, vec3_t start, vec3_t end)
+{
+ VectorCopy(b->start, start);
+ VectorCopy(b->end, end);
+
+ // if coming from the player, update the start position
+ if (b->entity == cl.viewentity)
+ {
+ if (cl_beams_quakepositionhack.integer && !chase_active.integer)
+ {
+ // LordHavoc: this is a stupid hack from Quake that makes your
+ // lightning appear to come from your waist and cover less of your
+ // view
+ // in Quake this hack was applied to all players (causing the
+ // infamous crotch-lightning), but in darkplaces and QuakeWorld it
+ // only applies to your own lightning, and only in first person
+ Matrix4x4_OriginFromMatrix(&cl.entities[cl.viewentity].render.matrix, start);
+ }
+ if (cl_beams_instantaimhack.integer)
+ {
+ vec3_t dir, localend;
+ vec_t len;
+ // LordHavoc: this updates the beam direction to match your
+ // viewangles
+ VectorSubtract(end, start, dir);
+ len = VectorLength(dir);
+ VectorNormalize(dir);
+ VectorSet(localend, len, 0, 0);
+ Matrix4x4_Transform(&r_view.matrix, localend, end);
+ }
+ }
+}
+
void CL_RelinkBeams(void)
{
int i;
beam_t *b;
- vec3_t dist, org;
+ vec3_t dist, org, start, end;
float d;
entity_t *ent;
float yaw, pitch;
continue;
}
- // if coming from the player, update the start position
- //if (b->entity == cl.viewentity)
- // Matrix4x4_OriginFromMatrix(&cl.entities[cl.viewentity].render.matrix, b->start);
- if (cl_beams_relative.integer >= 1 && b->entity && (b->entity == cl.viewentity || cl_beams_relative.integer >= 2) && cl.entities[b->entity].state_current.active && b->relativestartvalid)
- {
- entity_render_t *r = &cl.entities[b->entity].render;
- //Matrix4x4_OriginFromMatrix(&r->matrix, origin);
- //Matrix4x4_CreateFromQuakeEntity(&matrix, r->origin[0], r->origin[1], r->origin[2] + 16, r->angles[0], r->angles[1], r->angles[2], 1);
- Matrix4x4_Transform(&r->matrix, b->relativestart, b->start);
- Matrix4x4_Transform(&r->matrix, b->relativeend, b->end);
- }
+ CL_Beam_CalculatePositions(b, start, end);
if (b->lightning)
{
if (cl_beams_lightatend.integer)
{
// FIXME: create a matrix from the beam start/end orientation
- Matrix4x4_CreateTranslate(&tempmatrix, b->end[0], b->end[1], b->end[2]);
+ Matrix4x4_CreateTranslate(&tempmatrix, end[0], end[1], end[2]);
CL_AllocDlight (NULL, &tempmatrix, 200, 0.3, 0.7, 1, 0, 0, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
}
if (cl_beams_polygons.integer)
}
// calculate pitch and yaw
- VectorSubtract (b->end, b->start, dist);
-
+ // (this is similar to the QuakeC builtin function vectoangles)
+ VectorSubtract(end, start, dist);
if (dist[1] == 0 && dist[0] == 0)
{
yaw = 0;
}
// add new entities for the lightning
- VectorCopy (b->start, org);
+ VectorCopy (start, org);
d = VectorNormalizeLength(dist);
while (d > 0)
{
void CSQC_RelinkAllEntities (int drawmask)
{
- cl.num_brushmodel_entities = 0;
- CL_RelinkNetworkEntities(drawmask);
- if(drawmask & ENTMASK_ENGINE)
- {
- // move particles
- CL_MoveParticles();
- R_MoveExplosions();
- }
-
// link stuff
- CL_RelinkWorld();
- CL_RelinkCSQCWorld(); //[515]: csqc
- if(drawmask & ENTMASK_ENGINE)
+ if (drawmask & ENTMASK_ENGINE)
{
- CL_RelinkStaticEntities();
- CL_RelinkBeams();
- CL_RelinkEffects();
+ CL_RelinkNetworkEntities();
CL_RelinkQWNails();
}
+
+ if (drawmask & ENTMASK_ENGINEVIEWMODELS)
+ CL_LinkNetworkEntity(&cl.viewent); // link gun model
}
/*
Read all incoming data from the server
===============
*/
-extern void CL_ClientMovement_Replay();
+extern void CL_ClientMovement_Replay(void);
+extern void CL_StairSmoothing(void);//view.c
int CL_ReadFromServer(void)
{
V_DriftPitch();
V_FadeViewFlashs();
- // relink network entities (note: this sets up the view!)
+ // move particles
+ CL_MoveParticles();
+ R_MoveExplosions();
+
+ // process network entities (note: this sets up the view!)
CL_ClientMovement_Replay();
+ cl.num_brushmodel_entities = 0;
+ // now that the player entity has been updated we can call V_CalcRefdef
+ V_CalcRefdef();
+ CL_UpdateEntities();
+
+ entitylinkframenumber++;
+ // link stuff
+ CL_RelinkWorld();
+ CL_RelinkStaticEntities();
+ CL_RelinkBeams();
+ CL_RelinkEffects();
+
if(!csqc_loaded) //[515]: csqc
{
- cl.num_brushmodel_entities = 0;
- CL_RelinkNetworkEntities(65535);
-
- // move particles
- CL_MoveParticles();
- R_MoveExplosions();
-
- // link stuff
- CL_RelinkWorld();
- CL_RelinkCSQCWorld(); //[515]: csqc
- CL_RelinkStaticEntities();
- CL_RelinkBeams();
- CL_RelinkEffects();
+ CL_RelinkNetworkEntities();
+ CL_LinkNetworkEntity(&cl.viewent); // link gun model
CL_RelinkQWNails();
}
else
csqc_frame = true;
- CL_UpdateLights();
-
// update view blend
V_CalcViewBlend();
+ CL_UpdateLights();
+ CL_StairSmoothing();
+
// update the r_refdef time again because cl.time may have changed
r_refdef.time = cl.time;
}
Cmd_AddCommand ("playdemo", CL_PlayDemo_f, "watch a demo file");
Cmd_AddCommand ("timedemo", CL_TimeDemo_f, "play back a demo as fast as possible and save statistics to benchmark.log");
+#ifdef AUTODEMO_BROKEN
Cvar_RegisterVariable (&cl_autodemo);
Cvar_RegisterVariable (&cl_autodemo_nameformat);
+#endif
Cmd_AddCommand ("fog", CL_Fog_f, "set global fog parameters (density red green blue)");
Cvar_RegisterVariable(&cl_stainmaps);
Cvar_RegisterVariable(&cl_stainmaps_clearonload);
Cvar_RegisterVariable(&cl_beams_polygons);
- Cvar_RegisterVariable(&cl_beams_relative);
+ Cvar_RegisterVariable(&cl_beams_quakepositionhack);
+ Cvar_RegisterVariable(&cl_beams_instantaimhack);
Cvar_RegisterVariable(&cl_beams_lightatend);
Cvar_RegisterVariable(&cl_noplayershadow);