From 59cbcfe3555c935a15f7a6683cf0713466a4e272 Mon Sep 17 00:00:00 2001 From: Spoike Date: Mon, 22 Jun 2015 11:49:15 +0000 Subject: [PATCH] rewrote cam tracking code. should make cl_chasecam 0 more robust and avoid spam about invalid clients to track (and the associated ptrack spam). smoothed out cl_chasecam 0 angles. server browser no longer counts spectators as players. also sorts players by frags. fix ezhud r_tracking_frame issue, by making it technically 0 height when not tracking. r_showfields is now a separate cvar, instead of being rolled into r_showbboxes. now shows only a single entity. fix qport issue. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4915 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_cam.c | 105 +++++++++++++++++++++--------------- engine/client/cl_demo.c | 4 +- engine/client/cl_ents.c | 21 +++++--- engine/client/cl_input.c | 2 +- engine/client/cl_master.h | 16 +++--- engine/client/cl_parse.c | 2 +- engine/client/cl_pred.c | 102 +++++++++++++++++++++-------------- engine/client/cl_tent.c | 4 +- engine/client/client.h | 25 +++++---- engine/client/fragstats.c | 2 +- engine/client/m_master.c | 20 ++++--- engine/client/net_master.c | 36 +++++++++++-- engine/client/r_part.c | 4 +- engine/client/renderer.c | 2 + engine/client/sbar.c | 63 ++++++++++++---------- engine/client/view.c | 77 ++++++++++++++++---------- engine/common/cmd.c | 5 -- engine/common/net_chan.c | 2 +- engine/droid/fte.cfg | 6 ++- engine/gl/gl_alias.c | 5 +- engine/gl/gl_backend.c | 51 +++++++++--------- engine/gl/gl_heightmap.c | 36 +++++++++---- engine/gl/gl_hlmdl.c | 6 +-- engine/server/sv_ents.c | 2 + plugins/ezhud/ezquakeisms.h | 2 + plugins/ezhud/hud_common.c | 6 +++ 26 files changed, 377 insertions(+), 229 deletions(-) diff --git a/engine/client/cl_cam.c b/engine/client/cl_cam.c index 73a816ab3..ce69c81eb 100644 --- a/engine/client/cl_cam.c +++ b/engine/client/cl_cam.c @@ -146,7 +146,7 @@ static int CL_FindHighTrack(int seat, char *rule) //set a default to the currently tracked player, to reuse the current player we're tracking if someone lower equalises. j = cl.playerview[seat].cam_spec_track; - if (j >= 0 && cl.players[j].name[0] && !cl.players[j].spectator) + if (j >= 0 && cl.players[j].userid && cl.players[j].name[0] && !cl.players[j].spectator) max = CL_TrackScore(&cl.players[j], rule); else { @@ -158,7 +158,7 @@ static int CL_FindHighTrack(int seat, char *rule) { s = &cl.players[i]; score = CL_TrackScore(s, rule); - if (s->name[0] && !s->spectator && score > max) + if (s->userid && s->name[0] && !s->spectator && score > max) { if (j == i) //this was our default. continue; @@ -210,7 +210,7 @@ qboolean Cam_DrawViewModel(playerview_t *pv) { if (cl.spectator) { - if (pv->cam_auto && pv->cam_locked && cl_chasecam.ival) + if (pv->cam_state == CAM_EYECAM && cl_chasecam.ival) return true; return false; } @@ -224,18 +224,17 @@ qboolean Cam_DrawViewModel(playerview_t *pv) int Cam_TrackNum(playerview_t *pv) { - if (!pv->cam_auto) - return -1; - return pv->cam_spec_track; + if (pv->cam_state == CAM_EYECAM) + return pv->cam_spec_track; + return -1; } void Cam_Unlock(playerview_t *pv) { - if (pv->cam_auto) + if (pv->cam_state) { CL_SendClientCommand(true, "ptrack"); - pv->cam_auto = CAM_NONE; - pv->cam_locked = false; + pv->cam_state = CAM_FREECAM; pv->viewentity = (cls.demoplayback)?0:(pv->playernum+1); //free floating Sbar_Changed(); } @@ -245,12 +244,12 @@ void Cam_Lock(playerview_t *pv, int playernum) { int i; - pv->cam_lastviewtime = -1000; + pv->cam_lastviewtime = -1000; //allow the wallcam to re-snap as soon as it can CL_SendClientCommand(true, "ptrack %i", playernum); pv->cam_spec_track = playernum; - pv->cam_locked = false; + pv->cam_state = CAM_PENDING; pv->viewentity = (cls.demoplayback)?0:(pv->playernum+1); //free floating until actually locked @@ -259,10 +258,16 @@ void Cam_Lock(playerview_t *pv, int playernum) if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) { memcpy(&pv->stats, cl.players[playernum].stats, sizeof(pv->stats)); - pv->cam_locked = true; //instantly lock if the player is valid. +// pv->cam_state = CAM_; + +// pv->viewentity = playernum+1; + /* + pv->cam_state = cl_chasecam.ival?CAM_EYECAM:CAM_PENDING; //instantly lock if the player is valid. pv->viewentity = playernum+1; + */ if (cls.z_ext & Z_EXT_VIEWHEIGHT) pv->viewheight = cl.players[playernum].statsf[STAT_VIEWHEIGHT]; + } Sbar_Changed(); @@ -389,7 +394,7 @@ static qboolean InitFlyby(playerview_t *pv, vec3_t selforigin, vec3_t playerorig return false; } - pv->cam_locked = true; + pv->cam_state = CAM_WALLCAM; pv->viewentity = pv->playernum+1;//pv->cam_spec_track+1; VectorCopy(vec, pv->cam_desired_position); return true; @@ -403,19 +408,18 @@ static void Cam_CheckHighTarget(playerview_t *pv) j = CL_AutoTrack_Choose(pv - cl.playerview); if (j >= 0) { - if (pv->cam_spec_track != j || !pv->cam_locked) + if (pv->cam_spec_track != j || pv->cam_state == CAM_FREECAM) { if (cl.teamplay) Stats_Message("Now tracking:\n%s\n%s", cl.players[j].name, cl.players[j].team); else Stats_Message("Now tracking:\n%s", cl.players[j].name); - pv->cam_auto++; Cam_Lock(pv, j); //un-lock any higher seats watching our new target. this keeps things ordered. for (spv = pv+1; spv >= cl.playerview && spv < &cl.playerview[cl.splitclients]; spv++) { if (Cam_TrackNum(spv) == j) - spv->cam_locked = false; + spv->cam_state = CAM_FREECAM; } } } @@ -445,9 +449,9 @@ void Cam_SelfTrack(playerview_t *pv) } else { //view from a random wall - if (!pv->cam_locked || !Cam_IsVisible(pv->simorg, pv->cam_desired_position)) + if (pv->cam_state != CAM_WALLCAM || !Cam_IsVisible(pv->simorg, pv->cam_desired_position)) { - if (!pv->cam_locked || realtime - pv->cam_lastviewtime > 0.1) + if (pv->cam_state != CAM_WALLCAM || realtime - pv->cam_lastviewtime > 0.1) { if (!InitFlyby(pv, pv->cam_desired_position, pv->simorg, pv->simangles, true)) InitFlyby(pv, pv->cam_desired_position, pv->simorg, pv->simangles, false); @@ -460,7 +464,7 @@ void Cam_SelfTrack(playerview_t *pv) } //tracking failed. - if (!pv->cam_locked) + if (pv->cam_state != CAM_WALLCAM) return; } @@ -474,6 +478,23 @@ void Cam_SelfTrack(playerview_t *pv) } } +//player entity became visible, lock on to them (now that we know where they are etc) +void Cam_NowLocked(playerview_t *pv) +{ + pv->cam_lastviewtime = realtime; + if (!cl_chasecam.ival) + { + if (pv->cam_state != CAM_WALLCAM || !Cam_IsVisible(pv->simorg, pv->cam_desired_position)) + { + if (pv->cam_state != CAM_WALLCAM || realtime - pv->cam_lastviewtime > 0.1) + { + if (!InitFlyby(pv, pv->cam_desired_position, pv->simorg, pv->simangles, true)) + InitFlyby(pv, pv->cam_desired_position, pv->simorg, pv->simangles, false); + } + } + } +} + // ZOID // // Take over the user controls and track a player. @@ -488,15 +509,15 @@ void Cam_Track(playerview_t *pv, usercmd_t *cmd) if (!cl.spectator || !cl.worldmodel) //can happen when the server changes level return; - if (autotrackmode != TM_USER && !pv->cam_locked) + if (autotrackmode != TM_USER && pv->cam_state == CAM_FREECAM) Cam_CheckHighTarget(pv); - if (!pv->cam_auto || cls.state != ca_active || cl.worldmodel->loadstate != MLS_LOADED) + if (pv->cam_state == CAM_FREECAM || cls.state != ca_active || cl.worldmodel->loadstate != MLS_LOADED) return; - if (pv->cam_locked && (!cl.players[pv->cam_spec_track].name[0] || cl.players[pv->cam_spec_track].spectator)) + if (CAM_ISLOCKED(pv) && (!cl.players[pv->cam_spec_track].name[0] || cl.players[pv->cam_spec_track].spectator)) { - pv->cam_locked = false; + pv->cam_state = CAM_FREECAM; if (autotrackmode != TM_USER) Cam_CheckHighTarget(pv); else @@ -508,22 +529,24 @@ void Cam_Track(playerview_t *pv, usercmd_t *cmd) player = frame->playerstate + pv->cam_spec_track; self = frame->playerstate + pv->playernum; - if (!cl_chasecam.value && (!pv->cam_locked || !Cam_IsVisible(player->origin, pv->cam_desired_position))) + if (!cl_chasecam.value && (pv->cam_state != CAM_WALLCAM || !Cam_IsVisible(player->origin, pv->cam_desired_position))) { - if (!pv->cam_locked || realtime - pv->cam_lastviewtime > 0.1) + if (pv->cam_state != CAM_WALLCAM || realtime - pv->cam_lastviewtime > 0.1) { if (!InitFlyby(pv, self->origin, player->origin, player->viewangles, true)) InitFlyby(pv, self->origin, player->origin, player->viewangles, false); pv->cam_lastviewtime = realtime; } } + else if (cl_chasecam.value && pv->cam_state == CAM_WALLCAM) + pv->cam_state = CAM_PENDING; else { pv->cam_lastviewtime = realtime; } //tracking failed. - if (!pv->cam_locked || !pv->cam_auto) + if (pv->cam_state == CAM_FREECAM || pv->cam_state == CAM_PENDING) return; @@ -574,12 +597,12 @@ void Cam_Track(playerview_t *pv, usercmd_t *cmd) // move there locally immediately VectorCopy(pv->cam_desired_position, self->origin); - VectorSubtract(player->origin, pv->cam_desired_position, vec); - VectorAngles(vec, NULL, pv->viewangles); - pv->viewangles[0] = -pv->viewangles[0]; +// VectorSubtract(player->origin, pv->cam_desired_position, vec); +// VectorAngles(vec, NULL, pv->viewangles); +// pv->viewangles[0] = -pv->viewangles[0]; } -void Cam_SetAutoTrack(int userid) +void Cam_SetModAutoTrack(int userid) { //this is a hint from the server about who to track int slot; playerview_t *pv = &cl.playerview[0]; @@ -624,7 +647,6 @@ void Cam_TrackCrosshairedPlayer(playerview_t *pv) // Con_Printf("Track %i? %f\n", best, bestdot); if (best != -1) //did we actually get someone? { - pv->cam_auto++; Cam_Lock(pv, best); } } @@ -719,9 +741,8 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd) if (!(pv->cam_oldbuttons & BUTTON_ATTACK)) { pv->cam_oldbuttons |= BUTTON_ATTACK; - pv->cam_auto++; - if (pv->cam_auto > CAM_TRACK) + if (pv->cam_state != CAM_FREECAM) { Cam_Unlock(pv); VectorCopy(pv->viewangles, cmd->angles); @@ -735,7 +756,7 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd) else { pv->cam_oldbuttons &= ~BUTTON_ATTACK; - if (!pv->cam_auto && autotrackmode == TM_USER) + if (pv->cam_state == CAM_FREECAM && autotrackmode == TM_USER) { if ((cmd->buttons & BUTTON_JUMP) && !(pv->cam_oldbuttons & BUTTON_JUMP)) Cam_TrackCrosshairedPlayer(pv); @@ -744,7 +765,7 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd) } } - if (pv->cam_auto && autotrackmode != TM_USER) + if (autotrackmode != TM_USER) { if ((cmd->buttons & BUTTON_JUMP) && !(pv->cam_oldbuttons & BUTTON_JUMP)) autotrackmode = TM_USER; @@ -755,7 +776,7 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd) } } - if (pv->cam_locked) + if (pv->cam_state != CAM_FREECAM) { if ((cmd->buttons & BUTTON_JUMP) && (pv->cam_oldbuttons & BUTTON_JUMP)) return; // don't pogo stick @@ -770,8 +791,8 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd) // Con_Printf("Selecting track target...\n"); - if (pv->cam_locked && pv->cam_auto) - end = (pv->cam_spec_track + 1) % MAX_CLIENTS; + if (pv->cam_state != CAM_FREECAM) + end = (pv->cam_spec_track + 1) % MAX_CLIENTS; else end = pv->cam_spec_track; i = end; @@ -796,7 +817,7 @@ void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd) return; } Con_Printf("No target found ...\n"); - pv->cam_auto = pv->cam_locked = false; + pv->cam_state = CAM_FREECAM; } void Cam_Reset(void) @@ -805,7 +826,7 @@ void Cam_Reset(void) for (pnum = 0; pnum < MAX_SPLITS; pnum++) { playerview_t *pv = &cl.playerview[pnum]; - pv->cam_auto = CAM_NONE; + pv->cam_state = CAM_FREECAM; pv->cam_spec_track = 0; } } @@ -919,11 +940,7 @@ void Cam_TrackPlayer(int seat, char *cmdname, char *plrarg) } } - pv->cam_auto = CAM_TRACK; Cam_Lock(pv, slot); - //and force the lock here and now - pv->cam_locked = true; - pv->viewentity = slot+1; } void Cam_Track_f(void) diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 3657f7e2d..b1cb8b86c 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -758,7 +758,7 @@ readit: for (seat = 0; seat < cl.splitclients; seat++) { tracknum = cl.playerview[seat].cam_spec_track; - if (!cl.playerview[seat].cam_auto) + if (cl.playerview[seat].cam_state == CAM_FREECAM) tracknum = -1; if (tracknum == -1 || !(cls_lastto & (1 << tracknum))) continue; @@ -780,7 +780,7 @@ readit: for (seat = 0; seat < maxseat; seat++) { tracknum = cl.playerview[seat].cam_spec_track; - if (!cl.playerview[seat].cam_auto) + if (cl.playerview[seat].cam_state == CAM_FREECAM) tracknum = -1; if (tracknum == -1 || (cls_lastto != tracknum)) continue; diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 50029849d..852acc90f 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -2535,7 +2535,17 @@ void CLQ1_AddVisibleBBoxes(void) CLQ1_AddOrientedCylinder(s, rad*2, height*2, true, matrix, (e->v->solid || e->v->movetype)?0.1:0, (e->v->movetype == MOVETYPE_STEP || e->v->movetype == MOVETYPE_TOSS || e->v->movetype == MOVETYPE_BOUNCE)?0.1:0, ((int)e->v->flags & (FL_ONGROUND | ((e->v->movetype == MOVETYPE_STEP)?FL_FLY:0)))?0.1:0, 1); } else - CLQ1_AddOrientedCube(s, min, max, NULL, (e->v->solid || e->v->movetype)?0.1:0, (e->v->movetype == MOVETYPE_STEP || e->v->movetype == MOVETYPE_TOSS || e->v->movetype == MOVETYPE_BOUNCE)?0.1:0, ((int)e->v->flags & (FL_ONGROUND | ((e->v->movetype == MOVETYPE_STEP)?FL_FLY:0)))?0.1:0, 1); + { + if (!e->v->solid && !e->v->movetype) + { + vec3_t ep = {1,1,1}; + VectorAdd(max, ep, max); + VectorSubtract(min, ep, min); + CLQ1_AddOrientedCube(s, min, max, NULL, 0, 0.1, 0, 1); + } + else + CLQ1_AddOrientedCube(s, min, max, NULL, (e->v->solid || e->v->movetype)?0.1:0, (e->v->movetype == MOVETYPE_STEP || e->v->movetype == MOVETYPE_TOSS || e->v->movetype == MOVETYPE_BOUNCE)?0.1:0, ((int)e->v->flags & (FL_ONGROUND | ((e->v->movetype == MOVETYPE_STEP)?FL_FLY:0)))?0.1:0, 1); + } } } @@ -3861,8 +3871,7 @@ void CL_ParsePlayerinfo (void) info->prevcount = cl.parsecount; if (cls.findtrack && info->stats[STAT_HEALTH] > 0) - { - cl.playerview[0].cam_auto = CAM_TRACK; + { //FIXME: is this still needed with the autotrack stuff? Cam_Lock(&cl.playerview[0], num); cls.findtrack = false; } @@ -3950,15 +3959,13 @@ void CL_ParsePlayerinfo (void) for (i = 0; i < cl.splitclients; i++) { playerview_t *pv = &cl.playerview[i]; - if (pv->cam_auto && pv->cam_spec_track == num) + if (pv->cam_state != CAM_FREECAM && pv->cam_spec_track == num) return; } if (i == cl.splitclients) { playerview_t *pv = &cl.playerview[cl.splitclients++]; - pv->cam_auto = CAM_TRACK; - pv->cam_spec_track = num; Cam_Lock(pv, num); } } @@ -4503,7 +4510,7 @@ void CL_LinkPlayers (void) angles[ROLL] = 0; angles[ROLL] = V_CalcRoll (angles, state->velocity)*4; - if (j+1 == r_refdef.playerview->viewentity || (cl.spectator && r_refdef.playerview->cam_locked && r_refdef.playerview->cam_spec_track == j)) + if (j+1 == r_refdef.playerview->viewentity || (r_refdef.playerview->cam_state == CAM_EYECAM && r_refdef.playerview->cam_spec_track == j)) ent->flags |= RF_EXTERNALMODEL; // the player object gets added with flags | 2 for (pnum = 0; pnum < cl.splitclients; pnum++) diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 48f867c7f..be5963614 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -286,7 +286,7 @@ void IN_JumpDown (void) cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[cl.playerview[pnum].playernum].messagenum == cl.validsequence && cl.playerview[pnum].waterlevel >= 2 && (!cl.teamfortress || !(in_forward.state[pnum] & 1)) ) KeyDown(&in_up); - else if (condition && cl.spectator && Cam_TrackNum(&cl.playerview[pnum]) == -1) + else if (condition && cl.spectator && !CAM_ISLOCKED(&cl.playerview[pnum])) KeyDown(&in_up); else KeyDown(&in_jump); diff --git a/engine/client/cl_master.h b/engine/client/cl_master.h index f398808be..e8a1a39ad 100644 --- a/engine/client/cl_master.h +++ b/engine/client/cl_master.h @@ -55,8 +55,9 @@ typedef enum SLKEY_MOD, SLKEY_PROTOCOL, - SLKEY_NUMBOTS, - SLKEY_NUMHUMANS, + SLKEY_NUMBOTS, //uninteresting bots that will presumably get kicked if people join. + SLKEY_NUMSPECTATORS,//spectators + SLKEY_NUMHUMANS, //actual players SLKEY_QCSTATUS, // SLKEY_PLAYERS, //eep! SLKEY_ISFAVORITE,//eep! @@ -92,17 +93,18 @@ typedef struct serverdetailedinfo_s int numplayers; - struct + struct serverdetailedplayerinfo_s { int userid; int frags; float time; int ping; char name[64]; - char skin[16]; + char skin[16]; //is this even useful? char team[16]; char topc; char botc; + qbyte isspec; } players[MAX_CLIENTS]; } serverdetailedinfo_t; @@ -115,15 +117,15 @@ typedef struct serverinfo_s short special; //flags short protocol; - unsigned char players; - unsigned char maxplayers; + qbyte players; + qbyte maxplayers; qbyte sends; qbyte insortedlist; + qbyte numspectators; qbyte numhumans; qbyte numbots; qbyte freeslots; - qbyte pad; char modname[8+1]; char qcstatus[8+1]; diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 543334339..2be1f2a3d 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -5764,7 +5764,7 @@ void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from n } else if (!strncmp(stufftext, "//at ", 5)) { - Cam_SetAutoTrack(atoi(stufftext+5)); + Cam_SetModAutoTrack(atoi(stufftext+5)); } else if (!strncmp(stufftext, "//wps ", 5)) { diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 0c8550ca9..d4d54efb0 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -752,14 +752,15 @@ static void CL_EntStateToPlayerState(player_state_t *plstate, entity_state_t *st else VectorScale(state->u.q1.velocity, 1/8.0, plstate->velocity); VectorCopy(state->angles, plstate->viewangles); - if (state->u.q1.pmovetype) - plstate->viewangles[0] *= -3; - plstate->viewangles[2] = V_CalcRoll(plstate->viewangles, plstate->velocity); +// plstate->viewangles[2] = V_CalcRoll(plstate->viewangles, plstate->velocity); plstate->viewangles[0] = SHORT2ANGLE(state->u.q1.vangle[0]); plstate->viewangles[1] = SHORT2ANGLE(state->u.q1.vangle[1]); plstate->viewangles[2] = SHORT2ANGLE(state->u.q1.vangle[2]); + if (state->u.q1.pmovetype) + plstate->viewangles[0] *= -3; + a[0] = ((-192-state->u.q1.gravitydir[0])/256.0f) * 360; a[1] = (state->u.q1.gravitydir[1]/256.0f) * 360; a[2] = 0; @@ -850,6 +851,8 @@ void CL_PredictMovePNum (int seat) lerpents_t *le; qboolean nopred; qboolean lerpangles = false; + int trackent; + qboolean cam_nowlocked = false; //these are to make svc_viewentity work better float netfps = cl_netfps.value; @@ -890,6 +893,9 @@ void CL_PredictMovePNum (int seat) pv->nolocalplayer = !!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || (cls.protocol != CP_QUAKEWORLD); + if (!cl.spectator) //just in case + pv->cam_state = CAM_FREECAM; + #ifdef Q2CLIENT if (cls.protocol == CP_QUAKE2) { @@ -901,7 +907,7 @@ void CL_PredictMovePNum (int seat) } #endif - if (cl.paused && !(cls.demoplayback!=DPB_MVD && cls.demoplayback!=DPB_EZTV) && (!cl.spectator || !pv->cam_auto)) + if (cl.paused && !(cls.demoplayback!=DPB_MVD && cls.demoplayback!=DPB_EZTV) && pv->cam_state == CAM_FREECAM) return; if (!cl.validsequence) @@ -937,31 +943,23 @@ void CL_PredictMovePNum (int seat) } } - if (pv->cam_locked && pv->cam_spec_track >= 0) + //if we now know where our target player is, we can finally lock on to them. + if (pv->cam_state == CAM_PENDING && pv->cam_spec_track >= 0 && pv->cam_spec_track < cl.allocated_client_slots && pv->viewentity != pv->cam_spec_track+1) { - extern cvar_t cl_chasecam; - if (!cl_chasecam.ival) + if ((cl.inframes[cl.validsequence & UPDATE_MASK].playerstate[pv->cam_spec_track].messagenum == cl.validsequence) || + (pv->cam_spec_track+1 < cl.maxlerpents && cl.lerpents[pv->cam_spec_track+1].sequence == cl.lerpentssequence)) { - //FIXME: don't early out, so that we can smooth out angles too - VectorCopy(pv->cam_desired_position, pv->simorg); - VectorClear(pv->simvel); - return; + extern cvar_t cl_chasecam; + pv->cam_state = CAM_EYECAM; + pv->viewentity = pv->cam_spec_track+1; + cam_nowlocked = true; } } - if (!pv->cam_locked && pv->cam_auto && cl.spectator && pv->cam_spec_track >= 0 && pv->cam_spec_track < cl.allocated_client_slots && pv->viewentity != pv->cam_spec_track+1) - { - if (cl.inframes[cl.validsequence & UPDATE_MASK].playerstate[pv->cam_spec_track].messagenum == cl.validsequence) - { - pv->cam_locked = true; - pv->viewentity = pv->cam_spec_track+1; - } - else if (pv->cam_spec_track+1 < cl.maxlerpents && cl.lerpents[pv->cam_spec_track+1].sequence == cl.lerpentssequence) - { - pv->cam_locked = true; - pv->viewentity = pv->cam_spec_track+1; - } - } + if (pv->cam_state == CAM_WALLCAM) + trackent = pv->cam_spec_track+1; + else + trackent = pv->viewentity; nopred = cl_nopred.ival; @@ -973,7 +971,7 @@ void CL_PredictMovePNum (int seat) //these things also force-disable prediction if ((cls.demoplayback==DPB_MVD || cls.demoplayback == DPB_EZTV) || - cl.paused || pv->pmovetype == PM_NONE || pv->pmovetype == PM_FREEZE || pv->cam_locked) + cl.paused || pv->pmovetype == PM_NONE || pv->pmovetype == PM_FREEZE || CAM_ISLOCKED(pv)) { nopred = true; } @@ -1044,10 +1042,10 @@ void CL_PredictMovePNum (int seat) // Con_DPrintf("sim%f, %i(%i-%i): old%f, cur%f\n", simtime, cl.ackedmovesequence, fromframe, toframe, fromtime, totime); - if (pv->cam_locked && cl.spectator && pv->viewentity && pv->viewentity <= cl.allocated_client_slots) + if ((pv->cam_state == CAM_WALLCAM || pv->cam_state == CAM_EYECAM) && trackent && trackent <= cl.allocated_client_slots) { - fromstate = &cl.inframes[fromframe & UPDATE_MASK].playerstate[pv->viewentity-1]; - tostate = &cl.inframes[toframe & UPDATE_MASK].playerstate[pv->viewentity-1]; + fromstate = &cl.inframes[fromframe & UPDATE_MASK].playerstate[trackent-1]; + tostate = &cl.inframes[toframe & UPDATE_MASK].playerstate[trackent-1]; } else { @@ -1078,7 +1076,7 @@ void CL_PredictMovePNum (int seat) pe = &cl.inframes[fromframe & UPDATE_MASK].packet_entities; for (i = 0; i < pe->num_entities; i++) { - if (pe->entities[i].number == pv->viewentity) + if (pe->entities[i].number == trackent) { CL_EntStateToPlayerState(fromstate, &pe->entities[i]); if (nopred) @@ -1089,7 +1087,7 @@ void CL_PredictMovePNum (int seat) pe = &cl.inframes[toframe & UPDATE_MASK].packet_entities; for (i = 0; i < pe->num_entities; i++) { - if (pe->entities[i].number == pv->viewentity) + if (pe->entities[i].number == trackent) { CL_EntStateToPlayerState(tostate, &pe->entities[i]); if (nopred) @@ -1107,14 +1105,14 @@ void CL_PredictMovePNum (int seat) break; } } - if (pv->nolocalplayer && pv->viewentity < cl.maxlerpents) - le = &cl.lerpents[pv->viewentity]; + if (pv->nolocalplayer && trackent < cl.maxlerpents) + le = &cl.lerpents[trackent]; } // predict forward until cl.time <= to->senttime oldphysent = pmove.numphysent; CL_SetSolidPlayers(); - pmove.skipent = pv->viewentity; + pmove.skipent = trackent; //just in case we don't run any prediction VectorCopy(tostate->gravitydir, pmove.gravitydir); @@ -1166,7 +1164,7 @@ void CL_PredictMovePNum (int seat) tostate = &framebuf[i&1]; // Con_DPrintf(" pred %i: %f-%f\n", cl.ackedmovesequence+i, fromtime, totime); - CL_PredictUsercmd (seat, pv->viewentity, fromstate, tostate, cmdto); + CL_PredictUsercmd (seat, trackent, fromstate, tostate, cmdto); } if (simtime > totime) @@ -1201,7 +1199,7 @@ void CL_PredictMovePNum (int seat) cmdto->msec = bound(0, msec, 250); // Con_DPrintf(" extrap %i: %f-%f (%g)\n", toframe, fromtime, simtime, simtime-fromtime); - CL_PredictUsercmd (seat, pv->viewentity, fromstate, tostate, cmdto); + CL_PredictUsercmd (seat, trackent, fromstate, tostate, cmdto); } } pv->onground = pmove.onground; @@ -1215,7 +1213,7 @@ void CL_PredictMovePNum (int seat) VectorCopy (tostate->velocity, pv->simvel); VectorCopy (tostate->origin, pv->simorg); - if (pv->viewentity && pv->viewentity != pv->playernum+1 && pv->cam_locked) + if (trackent && trackent != pv->playernum+1 && pv->cam_state == CAM_EYECAM) VectorCopy(tostate->viewangles, pv->simangles); //Con_DPrintf("%f %f %f\n", fromtime, simtime, totime); } @@ -1245,7 +1243,7 @@ void CL_PredictMovePNum (int seat) pv->simorg[i] = (1-f)*fromstate->origin[i] + f*tostate->origin[i]; pv->simvel[i] = (1-f)*fromstate->velocity[i] + f*tostate->velocity[i]; - if (pv->viewentity && pv->viewentity != pv->playernum+1 && pv->cam_locked) + if (trackent && trackent != pv->playernum+1 && pv->cam_state == CAM_EYECAM) { pv->simangles[i] = LerpAngles360(fromstate->viewangles[i], tostate->viewangles[i], f);// * (360.0/65535); // pv->viewangles[i] = LerpAngles16(fromstate->command.angles[i], tostate->command.angles[i], f) * (360.0/65535); @@ -1262,9 +1260,12 @@ void CL_PredictMovePNum (int seat) CL_CalcCrouch (pv); pv->waterlevel = pmove.waterlevel; - VectorCopy(pmove.gravitydir, pv->gravitydir); + if (!DotProduct(pmove.gravitydir,pmove.gravitydir)) + VectorSet(pmove.gravitydir, 0, 0, -1); + else + VectorCopy(pmove.gravitydir, pv->gravitydir); - if (le) + if (le && pv->cam_state == CAM_FREECAM) { //keep the entity tracking the prediction position, so mirrors don't go all weird VectorMA(pv->simorg, -pv->crouch, pv->gravitydir, le->origin); @@ -1277,6 +1278,29 @@ void CL_PredictMovePNum (int seat) le->angles[0] *= -0.333; } } + + if (cam_nowlocked) + Cam_NowLocked(pv); + if (pv->cam_state == CAM_WALLCAM) + { + vec3_t dir; + + VectorSubtract(pv->simorg, pv->cam_desired_position, dir); + VectorAngles(dir, NULL, pv->simangles); + pv->simangles[0] *= -1; + VectorCopy(pv->simangles, pv->viewangles); + pv->viewangles[0] = anglemod(pv->viewangles[0]); + if (pv->viewangles[0] > 180) + pv->viewangles[0] -= 360; + VectorCopy(pv->cam_desired_position, pv->simorg); + VectorClear(pv->simvel); + } + if (cam_nowlocked) + { + //invalidate the roll, so we don't spin when switching povs + pv->rollangle = V_CalcRoll(pv->simangles, pv->simvel); + pv->vm.oldmodel = NULL; //invalidate the viewmodel, so the lerps get reset + } } void CL_PredictMove (void) diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index f8db9e5b1..76b4d268d 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -853,7 +853,7 @@ void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ n for (i = 0; i < cl.splitclients; i++) { playerview_t *pv = &cl.playerview[i]; - if (ent == (pv->cam_auto?(pv->cam_spec_track+1):(pv->playernum+1))) + if (ent == ((pv->cam_state == CAM_EYECAM)?(pv->cam_spec_track+1):(pv->playernum+1))) { VectorCopy(end, playerbeam_end[i]); break; @@ -3435,7 +3435,7 @@ void CL_UpdateBeams (void) for (j = 0; j < cl.splitclients; j++) { playerview_t *pv = &cl.playerview[j]; - if (b->entity == ((cl.spectator&&pv->cam_auto)?pv->cam_spec_track+1:(pv->playernum+1))) + if (b->entity == ((pv->cam_state == CAM_EYECAM)?pv->cam_spec_track+1:(pv->playernum+1))) { // player_state_t *pl; // VectorSubtract(cl.simorg, b->start, org); diff --git a/engine/client/client.h b/engine/client/client.h index 5b591e185..a10870b87 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -628,17 +628,20 @@ struct playerview_s vec3_t cam_desired_position; // where the camera wants to be - qboolean cam_locked; // int cam_oldbuttons; // vec3_t cam_viewangles; // - double cam_lastviewtime; // + double cam_lastviewtime; // timer for wallcam float cam_reautotrack; // timer to throttle tracking changes. - int cam_spec_track; // player# of who we are tracking + int cam_spec_track; // player# of who we are tracking / want to track / might want to track enum { - CAM_NONE = 0, - CAM_TRACK = 1 - } cam_auto; // + CAM_FREECAM = 0, //not attached to another player. we are our own thing (or actually playing). + CAM_PENDING = 1, //we want to lock on to cam_spec_track, but we don't have their position / stats yet. still freecamming + CAM_WALLCAM = 2, //locked, cl_chasecam=0. we're watching them from a wall. + CAM_EYECAM = 3 //locked, cl_chasecam=1. we know where they are, we're in their eyes. + +#define CAM_ISLOCKED(pv) ((pv)->cam_state > CAM_PENDING) + } cam_state; // vec3_t vw_axis[3]; //weapons should be positioned relative to this vec3_t vw_origin; //weapons should be positioned relative to this @@ -1277,18 +1280,18 @@ void CL_CalcClientTime(void); // qboolean Cam_DrawViewModel(playerview_t *pv); int Cam_TrackNum(playerview_t *pv); -void Cam_Unlock(playerview_t *pv); -void Cam_Lock(playerview_t *pv, int playernum); +void Cam_Unlock(playerview_t *pv); //revert to freecam or so, because that entity failed. +void Cam_Lock(playerview_t *pv, int playernum); //attempt to lock on to the given player. +void Cam_NowLocked(playerview_t *pv); //player was located, track them now void Cam_SelfTrack(playerview_t *pv); void Cam_Track(playerview_t *pv, usercmd_t *cmd); void Cam_TrackCrosshairedPlayer(playerview_t *pv); -void Cam_SetAutoTrack(int userid); +void Cam_SetModAutoTrack(int userid); void Cam_FinishMove(playerview_t *pv, usercmd_t *cmd); void Cam_Reset(void); void Cam_TrackPlayer(int seat, char *cmdname, char *plrarg); -void Cam_Lock(playerview_t *pv, int playernum); void CL_InitCam(void); -void Cam_AutoTrack_Update(const char *mode); +void Cam_AutoTrack_Update(const char *mode); //reset autotrack setting (because we started a new map or whatever) void QDECL vectoangles(vec3_t fwd, vec3_t ang); diff --git a/engine/client/fragstats.c b/engine/client/fragstats.c index 046039f81..cffb8bc1d 100644 --- a/engine/client/fragstats.c +++ b/engine/client/fragstats.c @@ -190,7 +190,7 @@ void Stats_FragMessage(int p1, int wid, int p2, qboolean teamkill) struct wt_s *w = &fragstats.weapontotals[wid]; const char *p1n = (p1 < 0)?nonplayers[-p1]:cl.players[p1].name; const char *p2n = (p2 < 0)?nonplayers[-p2]:cl.players[p2].name; - int localplayer = (cl.spectator && cl.playerview[0].cam_locked)?cl.playerview[0].cam_spec_track:cl.playerview[0].playernum; + int localplayer = (cl.playerview[0].cam_state == CAM_EYECAM)?cl.playerview[0].cam_spec_track:cl.playerview[0].playernum; #define YOU_GOOD S_COLOR_GREEN #define YOU_BAD S_COLOR_BLUE diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 39a2fcdfb..58dc761e8 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -502,7 +502,7 @@ static void SL_PostDraw (menu_t *menu) int teamplay = atoi(Info_ValueForKey(server->moreinfo->info, "teamplay")); x = lx; Draw_FunStringWidth (x, y, "^mFrgs", 28, true, false); - x += 28+8; + x += 32+8; Draw_FunStringWidth (x, y, "^mPng", 28, true, false); x += 3*8+8; @@ -523,12 +523,17 @@ static void SL_PostDraw (menu_t *menu) for (i = 0; i < server->moreinfo->numplayers; i++) { x = lx; - R2D_ImagePaletteColour (Sbar_ColorForMap(server->moreinfo->players[i].topc), 1.0); - R2D_FillBlock (x, y+1, 28, 3); - R2D_ImagePaletteColour (Sbar_ColorForMap(server->moreinfo->players[i].botc), 1.0); - R2D_FillBlock (x, y+4, 28, 4); - Draw_FunStringWidth (x, y, va("%3i", server->moreinfo->players[i].frags), 28, true, false); - x += 28+8; + if (server->moreinfo->players[i].isspec) + Draw_FunStringWidth (x, y, "spec", 32, true, false); + else + { + R2D_ImagePaletteColour (Sbar_ColorForMap(server->moreinfo->players[i].topc), 1.0); + R2D_FillBlock (x, y+1, 32, 3); + R2D_ImagePaletteColour (Sbar_ColorForMap(server->moreinfo->players[i].botc), 1.0); + R2D_FillBlock (x, y+4, 32, 4); + Draw_FunStringWidth (x, y, va("%3i", server->moreinfo->players[i].frags), 32, true, false); + } + x += 32+8; Draw_FunStringWidth (x, y, va("%3i", server->moreinfo->players[i].ping), 28, true, false); x += 3*8+8; @@ -956,6 +961,7 @@ void M_Menu_ServerList2_f(void) serverpreview = false; //in case it was lingering. + Key_Dest_Remove(kdm_console); Key_Dest_Add(kdm_menu); m_state = m_complex; diff --git a/engine/client/net_master.c b/engine/client/net_master.c index d8f41a691..6f418f13e 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -776,6 +776,8 @@ qboolean Master_ServerIsGreater(serverinfo_t *a, serverinfo_t *b) return Master_CompareInteger(a->players, b->players, SLIST_TEST_LESS); case SLKEY_NUMHUMANS: return Master_CompareInteger(a->numhumans, b->numhumans, SLIST_TEST_LESS); + case SLKEY_NUMSPECTATORS: + return Master_CompareInteger(a->numspectators, b->numspectators, SLIST_TEST_LESS); case SLKEY_NUMBOTS: return Master_CompareInteger(a->numbots, b->numbots, SLIST_TEST_LESS); case SLKEY_PING: @@ -850,6 +852,9 @@ qboolean Master_PassesMasks(serverinfo_t *a) case SLKEY_NUMHUMANS: res = Master_CompareInteger(a->numhumans, visrules[i].operandi, visrules[i].compareop); break; + case SLKEY_NUMSPECTATORS: + res = Master_CompareInteger(a->numspectators, visrules[i].operandi, visrules[i].compareop); + break; case SLKEY_TIMELIMIT: res = Master_CompareInteger(a->tl, visrules[i].operandi, visrules[i].compareop); break; @@ -1052,6 +1057,8 @@ float Master_ReadKeyFloat(serverinfo_t *server, int keynum) return server->numbots; case SLKEY_NUMHUMANS: return server->numhumans; + case SLKEY_NUMSPECTATORS: + return server->numspectators; case SLKEY_ISFAVORITE: return !!(server->special & SS_FAVORITE); case SLKEY_ISLOCAL: @@ -1184,6 +1191,8 @@ int Master_KeyForName(const char *keyname) return SLKEY_NUMBOTS; else if (!strcmp(keyname, "numhumans")) return SLKEY_NUMHUMANS; + else if (!strcmp(keyname, "numspectators")) + return SLKEY_NUMSPECTATORS; else if (!strcmp(keyname, "qcstatus")) return SLKEY_QCSTATUS; else if (!strcmp(keyname, "isfavorite")) @@ -2594,7 +2603,7 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor char *nl; char *name; int ping; - int len; + int len, j, k; serverinfo_t *info; char adr[MAX_ADR_SIZE]; @@ -2748,6 +2757,8 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor break; *nl = '\0'; + details.players[clnum].isspec = false; + token = msg; if (!token) break; @@ -2804,7 +2815,10 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor if (len >= sizeof(details.players[clnum].name)) len = sizeof(details.players[clnum].name); if (!strncmp(token, "\"\\s\\", 4)) + { + details.players[clnum].isspec = true; Q_strncpyz(details.players[clnum].name, token+4, len-3); + } else Q_strncpyz(details.players[clnum].name, token+1, len); details.players[clnum].name[len] = '\0'; @@ -2848,12 +2862,28 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor MasterInfo_AddPlayer(&info->adr, details.players[clnum].name, details.players[clnum].ping, details.players[clnum].frags, details.players[clnum].topc*4 | details.players[clnum].botc, details.players[clnum].skin, details.players[clnum].team); - - ++details.numplayers; if (details.players[clnum].ping == 807 || !strncmp(details.players[clnum].name, "BOT:", 4)) info->numbots++; + else if (details.players[clnum].isspec) + info->numspectators++; else info->numhumans++; + + for (k = clnum, j = clnum-1; j >= 0; j--) + { + if ((details.players[k].isspec != details.players[j].isspec && !details.players[k].isspec) || + details.players[k].frags > details.players[j].frags) + { + struct serverdetailedplayerinfo_s t = details.players[j]; + details.players[j] = details.players[k]; + details.players[k] = t; + k = j; + } + else + break; + } + details.numplayers++; + info->players++; msg = nl; diff --git a/engine/client/r_part.c b/engine/client/r_part.c index 77fbabaac..9004b4fd0 100644 --- a/engine/client/r_part.c +++ b/engine/client/r_part.c @@ -346,7 +346,7 @@ void R_Clutter_Emit(batch_t **batches) { const float cluttersize = r_clutter_distance.value; int vx, vy, vz; - int x, y, z, key, i; + int x, y, z, key, i, j; cluttersector_t *sect; batch_t *b; qboolean rebuildlimit = false; @@ -453,6 +453,8 @@ void R_Clutter_Emit(batch_t **batches) if (!b) return; memset(b, 0, sizeof(*b)); + for (j = 0; j < MAXRLIGHTMAPS; j++) + b->lightmap[j] = -1; b->ent = &r_worldentity; b->meshes = 1; b->mesh = §->soups[i].pmesh; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index f2bb5c6e5..ad2ae93b4 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -358,6 +358,7 @@ cvar_t dpcompat_psa_ungroup = SCVAR ("dpcompat_psa_ungroup", "0"); cvar_t r_noaliasshadows = SCVARF ("r_noaliasshadows", "0", CVAR_ARCHIVE); cvar_t r_shadows = CVARFD ("r_shadows", "0", CVAR_ARCHIVE, "Draw basic blob shadows underneath entities without using realtime lighting."); cvar_t r_showbboxes = CVARD("r_showbboxes", "0", "Debugging. Shows bounding boxes. 1=ssqc, 2=csqc. Red=solid, Green=stepping/toss/bounce, Blue=onground."); +cvar_t r_showfields = CVARD("r_showfields", "0", "Debugging. Shows entity fields boxes (entity closest to crosshair). 1=ssqc, 2=csqc."); cvar_t r_lightprepass = CVARFD("r_lightprepass", "0", CVAR_SHADERSYSTEM, "Experimental. Attempt to use a different lighting mechanism."); cvar_t r_shadow_bumpscale_basetexture = CVARD ("r_shadow_bumpscale_basetexture", "0", "bumpyness scaler for generation of fallback normalmap textures from models"); @@ -748,6 +749,7 @@ void Renderer_Init(void) Cvar_Register (&r_replacemodels, GRAPHICALNICETIES); Cvar_Register (&r_showbboxes, GLRENDEREROPTIONS); + Cvar_Register (&r_showfields, GLRENDEREROPTIONS); Cvar_Register (&r_polygonoffset_submodel_factor, GLRENDEREROPTIONS); Cvar_Register (&r_polygonoffset_submodel_offset, GLRENDEREROPTIONS); Cvar_Register (&r_polygonoffset_shadowmap_factor, GLRENDEREROPTIONS); diff --git a/engine/client/sbar.c b/engine/client/sbar.c index 14e2434c7..fcac1eff9 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -26,16 +26,16 @@ extern cvar_t *hud_tracking_show; #define CON_ALTMASK (CON_2NDCHARSETTEXT|CON_WHITEMASK) -cvar_t scr_scoreboard_drawtitle = SCVAR("scr_scoreboard_drawtitle", "1"); -cvar_t scr_scoreboard_forcecolors = SCVAR("scr_scoreboard_forcecolors", "0"); //damn americans -cvar_t scr_scoreboard_newstyle = SCVAR("scr_scoreboard_newstyle", "1"); // New scoreboard style ported from Electro, by Molgrum +cvar_t scr_scoreboard_drawtitle = CVARD("scr_scoreboard_drawtitle", "1", "Wastes screen space when looking at the scoreboard."); +cvar_t scr_scoreboard_forcecolors = CVARD("scr_scoreboard_forcecolors", "0", "Makes the scoreboard colours obey enemycolor/teamcolor rules."); //damn americans +cvar_t scr_scoreboard_newstyle = CVARD("scr_scoreboard_newstyle", "1", "Display team colours and stuff in a style popularised by Electro. Looks more modern, but might not quite fit classic huds."); // New scoreboard style ported from Electro, by Molgrum cvar_t scr_scoreboard_showfrags = CVARD("scr_scoreboard_showfrags", "0", "Display kills+deaths+teamkills, as determined by fragfile.dat-based conprint parsing. These may be inaccurate if you join mid-game."); cvar_t scr_scoreboard_showflags = CVARD("scr_scoreboard_showflags", "2", "Display flag caps+touches on the scoreboard, where our fragfile.dat supports them.\n0: off\n1: on\n2: on only if someone appears to have interacted with a flag."); -cvar_t scr_scoreboard_fillalpha = CVAR("scr_scoreboard_fillalpha", "0.7"); -cvar_t scr_scoreboard_teamscores = SCVAR("scr_scoreboard_teamscores", "1"); -cvar_t scr_scoreboard_teamsort = SCVAR("scr_scoreboard_teamsort", "1"); +cvar_t scr_scoreboard_fillalpha = CVARD("scr_scoreboard_fillalpha", "0.7", "Transparency amount for newstyle scoreboard."); +cvar_t scr_scoreboard_teamscores = CVARD("scr_scoreboard_teamscores", "1", "Makes +showscores act as +showteamscores. Because reasons."); +cvar_t scr_scoreboard_teamsort = CVARD("scr_scoreboard_teamsort", "0", "On the scoreboard, sort players by their team BEFORE their personal score."); cvar_t scr_scoreboard_titleseperator = SCVAR("scr_scoreboard_titleseperator", "1"); -cvar_t sbar_teamstatus = SCVAR("sbar_teamstatus", "1"); +cvar_t sbar_teamstatus = CVARD("sbar_teamstatus", "1", "Display the last team say from each of your team members just above the sbar area."); //=========================================== //rogue changed and added defines @@ -2123,7 +2123,7 @@ void Sbar_DrawScoreboard (void) #ifndef CLIENTONLY /*no scoreboard in single player (if you want bots, set deathmatch)*/ - if (sv.state && !cls.deathmatch && sv.allocated_client_slots == 1) + if (sv.state && sv.allocated_client_slots == 1) { return; } @@ -2757,7 +2757,7 @@ void Sbar_Draw (playerview_t *pv) // top line if (sb_lines > 24) { - if (!cl.spectator || pv->cam_auto == CAM_TRACK) + if (!cl.spectator || pv->cam_state == CAM_WALLCAM || pv->cam_state == CAM_EYECAM) Sbar_DrawInventory (pv); else if (cl_sbar.ival) Sbar_DrawPic (0, -24, 320, 24, sb_scorebar); //make sure we don't get HoM @@ -2770,13 +2770,14 @@ void Sbar_Draw (playerview_t *pv) { if (cl.spectator) { - if (pv->cam_auto != CAM_TRACK) + if (pv->cam_state == CAM_FREECAM || pv->cam_state == CAM_PENDING) { if (hud_tracking_show->ival || cl_sbar.ival) { //this is annoying. Sbar_DrawPic (0, 0, 320, 24, sb_scorebar); Sbar_DrawString (160-7*8,4, "SPECTATOR MODE"); - Sbar_DrawString(160-14*8+4, 12, "Press [ATTACK] for AutoCamera"); + if (pv->cam_state == CAM_FREECAM) + Sbar_DrawString(160-14*8+4, 12, "Press [ATTACK] for AutoCamera"); } } else @@ -2888,7 +2889,7 @@ void Sbar_IntermissionNumber (float x, float y, int num, int digits, int color, #define COL_TEAM_LOWAVGHIGH COLUMN("low/avg/high", 12*8, {sprintf (num, "%3i/%3i/%3i", plow, pavg, phigh); Draw_FunString ( x, y, num); }) #define COL_TEAM_TEAM COLUMN("team", 4*8, {Draw_FunStringWidth ( x, y, tm->team, 4*8, false, false); \ - if (!strncmp(cl.players[pv->playernum].team, tm->team, 16))\ + if (!strncmp(cl.players[trackplayer].team, tm->team, 16))\ {\ Draw_FunString ( x - 1*8, y, "^Ue010");\ Draw_FunString ( x + 4*8, y, "^Ue011");\ @@ -2920,6 +2921,7 @@ void Sbar_TeamOverlay (void) int rank_width = 320-32*2; int startx; + int trackplayer; if (!pv) pv = &cl.playerview[0]; @@ -2998,6 +3000,11 @@ void Sbar_TeamOverlay (void) // sort the teams Sbar_SortTeams(pv); + if (cl.spectator) + trackplayer = Cam_TrackNum(pv); + else + trackplayer = pv->playernum; + // draw the text for (i=0 ; i < scoreboardteams && y <= vid.height-10 ; i++) { @@ -3060,7 +3067,7 @@ void Sbar_TeamOverlay (void) sprintf (num, "%5i", tm->players); Draw_FunString (x + 104 + 88, y, num); - if (!strncmp(cl.players[cl.playernum[0]].team, tm->team, 16)) + if (!strncmp(cl.players[trackplayer].team, tm->team, 16)) { Draw_FunString ( x + 104 - 8, y, "^Ue010"); Draw_FunString ( x + 104 + 32, y, "^Ue011"); @@ -3132,20 +3139,20 @@ ping time frags name f = s->frags; \ sprintf(num, "%3i",f); \ \ - Font_BeginString(font_default, x+8, y, &cx, &cy); \ - Font_DrawChar(cx, cy, num[0] | 0xe000 | CON_WHITEMASK); \ - Font_BeginString(font_default, x+16, y, &cx, &cy); \ - Font_DrawChar(cx, cy, num[1] | 0xe000 | CON_WHITEMASK); \ - Font_BeginString(font_default, x+24, y, &cx, &cy); \ - Font_DrawChar(cx, cy, num[2] | 0xe000 | CON_WHITEMASK); \ - \ - if ((cl.spectator && k == Cam_TrackNum(pv)) ||\ - (!cl.spectator && k == pv->playernum)) \ - { \ - Font_BeginString(font_default, x, y, &cx, &cy); \ - Font_DrawChar(cx, cy, 16 | 0xe000 | CON_WHITEMASK); \ - Font_BeginString(font_default, x+32, y, &cx, &cy); \ - Font_DrawChar(cx, cy, 17 | 0xe000 | CON_WHITEMASK); \ + Font_BeginString(font_default, x+8, y, &cx, &cy); \ + Font_DrawChar(cx, cy, num[0] | 0xe000 | CON_WHITEMASK); \ + Font_BeginString(font_default, x+16, y, &cx, &cy); \ + Font_DrawChar(cx, cy, num[1] | 0xe000 | CON_WHITEMASK); \ + Font_BeginString(font_default, x+24, y, &cx, &cy); \ + Font_DrawChar(cx, cy, num[2] | 0xe000 | CON_WHITEMASK); \ + \ + if ((pv->cam_state == CAM_FREECAM && k == pv->playernum) || \ + (pv->cam_state != CAM_FREECAM && k == pv->cam_spec_track)) \ + { \ + Font_BeginString(font_default, x, y, &cx, &cy); \ + Font_DrawChar(cx, cy, 16 | 0xe000 | CON_WHITEMASK); \ + Font_BeginString(font_default, x+32, y, &cx, &cy); \ + Font_DrawChar(cx, cy, 17 | 0xe000 | CON_WHITEMASK); \ } \ Font_EndString(font_default); \ } \ @@ -3590,7 +3597,7 @@ static void Sbar_MiniDeathmatchOverlay (playerview_t *pv) Font_BeginString(font_default, x+24, y, &px, &py); Font_DrawChar ( px, py, num[2] | 0xe000 | CON_WHITEMASK); - if ((cl.spectator && k == pv->cam_spec_track && pv->cam_locked) || + if ((cl.spectator && k == pv->cam_spec_track && pv->cam_state != CAM_FREECAM) || (!cl.spectator && k == pv->playernum)) { Font_BeginString(font_default, x, y, &px, &py); diff --git a/engine/client/view.c b/engine/client/view.c index 9437e5cca..79fd271dc 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -1342,7 +1342,7 @@ void V_CalcRefdef (playerview_t *pv) viewheight += pv->crouch; - if (pv->stats[STAT_HEALTH] < 0 && (!cl.spectator || pv->cam_locked) && v_deathtilt.value) // PF_GIB will also set PF_DEAD + if (pv->stats[STAT_HEALTH] < 0 && (!cl.spectator || pv->cam_state == CAM_EYECAM) && v_deathtilt.value) // PF_GIB will also set PF_DEAD { if (!cl.spectator || cl_chasecam.ival) r_refdef.viewangles[ROLL] = 80*v_deathtilt.value; // dead view angle @@ -1527,21 +1527,21 @@ void R_DrawNameTags(void) vec3_t tagcenter; lerpents_t *le; - extern cvar_t r_showbboxes; - if (r_showbboxes.ival && cls.allow_cheats) + extern cvar_t r_showfields; + if (r_showfields.ival && cls.allow_cheats) { world_t *w = NULL; wedict_t *e; vec3_t org; vec3_t screenspace; vec3_t diff; - if (r_showbboxes.ival == 1) + if ((r_showfields.ival & 3) == 1) { #ifndef CLIENTONLY w = &sv.world; #endif } - else if (r_showbboxes.ival == 2) + else if ((r_showfields.ival & 3) == 2) { #ifdef CSQC_DAT extern world_t csqc_world; @@ -1549,34 +1549,55 @@ void R_DrawNameTags(void) #endif } if (w && w->progs) - for (i = 1; i < w->num_edicts; i++) { - e = WEDICT_NUM(w->progs, i); - if (e->isfree) - continue; - VectorInterpolate(e->v->mins, 0.5, e->v->maxs, org); - VectorAdd(org, e->v->origin, org); - VectorSubtract(org, r_refdef.vieworg, diff); - if (DotProduct(diff, diff) > 256*256) - continue; - if (Matrix4x4_CM_Project(org, screenspace, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y)) + int best = 0; + float bestscore = 0, score = 0; + for (i = 1; i < w->num_edicts; i++) { - char asciibuffer[8192]; - char *entstr; - int buflen; - int x, y; - - sprintf(asciibuffer, "entity %i ", e->entnum); - buflen = strlen(asciibuffer); - entstr = w->progs->saveent(w->progs, asciibuffer, &buflen, sizeof(asciibuffer), (edict_t*)e); //will save just one entities vars - if (entstr) + e = WEDICT_NUM(w->progs, i); + if (e->isfree) + continue; + VectorInterpolate(e->v->mins, 0.5, e->v->maxs, org); + VectorAdd(org, e->v->origin, org); + VectorSubtract(org, r_refdef.vieworg, diff); + if (DotProduct(diff, diff) < 16*16) + continue; //ignore stuff too close(like the player themselves) + VectorNormalize(diff); + score = DotProduct(diff, vpn);// r_refdef.viewaxis[0]); + if (score > bestscore) { - vec2_t scale = {8,8}; - x = screenspace[0]*r_refdef.vrect.width+r_refdef.vrect.x; - y = (1-screenspace[1])*r_refdef.vrect.height+r_refdef.vrect.y; - R_DrawTextField(x, y, vid.width - x, vid.height - y, entstr, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, scale); + vec3_t imp; + if (!TraceLineN(r_refdef.vieworg, org, imp, NULL)) + { + best = i; + bestscore = score; + } } + } + if (best) + { + e = WEDICT_NUM(w->progs, best); + VectorInterpolate(e->v->mins, 0.5, e->v->maxs, org); + VectorAdd(org, e->v->origin, org); + if (Matrix4x4_CM_Project(org, screenspace, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y)) + { + char asciibuffer[8192]; + char *entstr; + int buflen; + int x, y; + sprintf(asciibuffer, "entity %i ", e->entnum); + buflen = strlen(asciibuffer); + entstr = w->progs->saveent(w->progs, asciibuffer, &buflen, sizeof(asciibuffer), (edict_t*)e); //will save just one entities vars + if (entstr) + { + vec2_t scale = {8,8}; + x = screenspace[0]*r_refdef.vrect.width+r_refdef.vrect.x; + y = (1-screenspace[1])*r_refdef.vrect.height+r_refdef.vrect.y; + R_DrawTextField(x, y, vid.width - x, vid.height - y, entstr, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, scale); + } + + } } } } diff --git a/engine/common/cmd.c b/engine/common/cmd.c index a11a301d4..e3e7b5ca0 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -2025,14 +2025,9 @@ void Cmd_ForwardToServer_f (void) { playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)]; if (!*Cmd_Argv(2)) - { Cam_Unlock(pv); - } else - { Cam_Lock(pv, atoi(Cmd_Argv(2))); - pv->cam_auto = CAM_TRACK; - } return; } diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index bd250d31c..3cea9cbdf 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -240,7 +240,7 @@ void Netchan_Init (void) port = ((int)(getpid()+getuid()*1000) * time(NULL)) & 0xffff; #endif Q_snprintfz(qportstr, sizeof(qportstr), "%i", port); - qport.string = qportstr; + qport.enginevalue = qportstr; Cvar_Register (&pext_predinfo, "Protocol Extensions"); Cvar_Register (&pext_replacementdeltas, "Protocol Extensions"); diff --git a/engine/droid/fte.cfg b/engine/droid/fte.cfg index a0b9ee6cd..094222d6b 100644 --- a/engine/droid/fte.cfg +++ b/engine/droid/fte.cfg @@ -3,13 +3,15 @@ bind mouse1 +attack //left area of the screen bind mouse2 +jump //right area of the screen bind menu "toggleconsole" -bind volup +showteamscores -// bind voldown +jump //removed because on most android devices power+voldown takes a screenshot +bind volup inv volume 0.1 +bind voldown inv volume -0.1 + // Appearance settings brightness "0.2" contrast "1.2" +vid_conwidth "0" //make something up based upon aspect ratio vid_conheight "300" //not using autoscale as it can make the menu unusable. vid_conautoscale "0" // Text/Menu size. 2 is the default. 4 is bigger diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index cac18b1e3..60bd05374 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -1531,7 +1531,7 @@ void R_GAlias_GenerateBatches(entity_t *e, batch_t **batches) b->texture = NULL; b->shader = shader; for (j = 0; j < MAXRLIGHTMAPS; j++) - b->lightmap[0] = -1; + b->lightmap[j] = -1; b->surf_first = surfnum; b->flags = 0; sort = shader->sort; @@ -1637,7 +1637,8 @@ void R_Sprite_GenerateBatches(entity_t *e, batch_t **batches) b->skin = frame-; b->texture = NULL; b->shader = frame->shader; - b->lightmap = -1; + for (j = 0; j < MAXRLIGHTMAPS; j++) + b->lightmap[j] = -1; b->surf_first = surfnum; b->flags = 0; b->vbo = NULL; diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index ce5cb4947..5c38aab4e 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -4461,6 +4461,7 @@ static void GLBE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist) static void GLBE_SubmitMeshesSortList(batch_t *sortlist) { batch_t *batch; + shader_t *bs; for (batch = sortlist; batch; batch = batch->next) { if (batch->meshes == batch->firstmesh) @@ -4482,31 +4483,33 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) batch->buildmeshes(batch); } - TRACE(("GLBE_SubmitMeshesSortList: shader %s\n", batch->shader->name)); + bs = batch->shader; + + TRACE(("GLBE_SubmitMeshesSortList: shader %s\n", bs->name)); //FIXME:!! - if (!batch->shader) + if (!bs) { Con_Printf("Shader not set...\n"); if (batch->texture) - batch->shader = R_TextureAnimation(0, batch->texture)->shader; + bs = R_TextureAnimation(0, batch->texture)->shader; else continue; } - if (batch->shader->flags & SHADER_NODRAW) + if (bs->flags & SHADER_NODRAW) continue; - if (batch->shader->flags & SHADER_NODLIGHT) + if (bs->flags & SHADER_NODLIGHT) if (shaderstate.mode == BEM_LIGHT) continue; - if (batch->shader->flags & SHADER_NOSHADOWS) + if (bs->flags & SHADER_NOSHADOWS) if (shaderstate.mode == BEM_STENCIL || shaderstate.mode == BEM_DEPTHONLY) //fixme: depthonly is not just shadows. continue; - if (batch->shader->flags & SHADER_SKY) + if (bs->flags & SHADER_SKY) { if (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)// || shaderstate.mode == BEM_WIREFRAME) { - if (!batch->shader->prog) + if (!bs->prog) { R_DrawSkyChain (batch); continue; @@ -4516,7 +4519,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) continue; } - if ((batch->shader->flags & (SHADER_HASREFLECT | SHADER_HASREFRACT | SHADER_HASRIPPLEMAP)) && shaderstate.mode != BEM_WIREFRAME) + if ((bs->flags & (SHADER_HASREFLECT | SHADER_HASREFRACT | SHADER_HASRIPPLEMAP)) && shaderstate.mode != BEM_WIREFRAME) { int oldfbo; float oldil; @@ -4529,7 +4532,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) oldbem = shaderstate.mode; oldil = shaderstate.identitylighting; - if ((batch->shader->flags & SHADER_HASREFLECT) && gl_config.ext_framebuffer_objects) + if ((bs->flags & SHADER_HASREFLECT) && gl_config.ext_framebuffer_objects) { vrect_t orect = r_refdef.vrect; pxrect_t oprect = r_refdef.pxrect; @@ -4571,7 +4574,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) r_refdef.pxrect = oprect; GL_ViewportUpdate(); } - if (batch->shader->flags & (SHADER_HASREFRACT|SHADER_HASREFRACTDEPTH)) + if (bs->flags & (SHADER_HASREFRACT|SHADER_HASREFRACTDEPTH)) { if (r_refract_fboival) { @@ -4603,7 +4606,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } - if (batch->shader->flags & SHADER_HASREFRACTDEPTH) + if (bs->flags & SHADER_HASREFRACTDEPTH) { if (!shaderstate.tex_refractiondepth) { @@ -4634,7 +4637,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) GL_ForceDepthWritable(); qglClearColor(0, 0, 0, 1); qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - GLR_DrawPortal(batch, cl.worldmodel->batches, NULL, ((batch->shader->flags & SHADER_HASREFRACTDEPTH)?3:2)); //fixme + GLR_DrawPortal(batch, cl.worldmodel->batches, NULL, ((bs->flags & SHADER_HASREFRACTDEPTH)?3:2)); //fixme GLBE_FBO_Pop(oldfbo); r_refdef.vrect = ovrect; @@ -4644,7 +4647,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) else GLR_DrawPortal(batch, cl.worldmodel->batches, NULL, 3); } - if ((batch->shader->flags & SHADER_HASRIPPLEMAP) && gl_config.ext_framebuffer_objects) + if ((bs->flags & SHADER_HASRIPPLEMAP) && gl_config.ext_framebuffer_objects) { vrect_t orect = r_refdef.vrect; pxrect_t oprect = r_refdef.pxrect; @@ -5324,19 +5327,19 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) RSpeedRemark(); GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_DECAL); RSpeedEnd(RSPEED_WORLD); - } #ifdef RTLIGHTS - if (drawworld) - { - RSpeedRemark(); - TRACE(("GLBE_DrawWorld: drawing lights\n")); - GLBE_SelectEntity(&r_worldentity); - Sh_DrawLights(vis); - RSpeedEnd(RSPEED_STENCILSHADOWS); - TRACE(("GLBE_DrawWorld: lights drawn\n")); - } + if (drawworld) + { + RSpeedRemark(); + TRACE(("GLBE_DrawWorld: drawing lights\n")); + GLBE_SelectEntity(&r_worldentity); + Sh_DrawLights(vis); + RSpeedEnd(RSPEED_STENCILSHADOWS); + TRACE(("GLBE_DrawWorld: lights drawn\n")); + } #endif + } shaderstate.identitylighting = 1; diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 599e557b4..96ca1f5e7 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -663,11 +663,18 @@ static hmsection_t *Terr_GenerateSection(heightmap_t *hm, int sx, int sy, qboole s->loadstate = TSLS_LOADING1; - if (scheduleload) - COM_AddWork(1, Terr_LoadSectionWorker, s, hm, sx, sy); - } + if (!scheduleload) + return s; + #ifdef LOADERTHREAD - Sys_UnlockMutex(com_resourcemutex); + Sys_UnlockMutex(com_resourcemutex); +#endif + COM_AddWork(1, Terr_LoadSectionWorker, s, hm, sx, sy); + return s; + } + if (scheduleload) +#ifdef LOADERTHREAD + Sys_UnlockMutex(com_resourcemutex); #endif return s; } @@ -1666,13 +1673,24 @@ static void Terr_LoadSectionWorker(void *ctx, void *data, size_t a, size_t b) { //noload avoids recursion. s = Terr_GenerateSection(hm, sx+x, sy+y, false); - if (s->loadstate == TSLS_LOADING1) + if (s) { - offset = block->offset[x + y*SECTIONSPERBLOCK]; - if (!offset) - Terr_ReadSection(hm, s, ver, NULL, 0); //no data in the file for this section + if (s->loadstate == TSLS_LOADING1) + { + s->loadstate = TSLS_LOADING1; +#ifdef LOADERTHREAD + Sys_UnlockMutex(com_resourcemutex); +#endif + offset = block->offset[x + y*SECTIONSPERBLOCK]; + if (!offset) + Terr_ReadSection(hm, s, ver, NULL, 0); //no data in the file for this section + else + Terr_ReadSection(hm, s, ver, (char*)diskimage + offset, len - offset); + } +#ifdef LOADERTHREAD else - Terr_ReadSection(hm, s, ver, (char*)diskimage + offset, len - offset); + Sys_UnlockMutex(com_resourcemutex); +#endif } } } diff --git a/engine/gl/gl_hlmdl.c b/engine/gl/gl_hlmdl.c index e8dad2d7e..966d65220 100644 --- a/engine/gl/gl_hlmdl.c +++ b/engine/gl/gl_hlmdl.c @@ -719,10 +719,8 @@ void R_HalfLife_WalkMeshes(entity_t *rent, batch_t *b, batch_t **batches) b->skin = &shader->defaulttextures; b->texture = NULL; b->shader = shader; - b->lightmap[0] = -1; - b->lightmap[1] = -1; - b->lightmap[2] = -1; - b->lightmap[3] = -1; + for (j = 0; j < MAXRLIGHTMAPS; j++) + b->lightmap[j] = -1; b->surf_first = batchid; b->flags = 0; sort = shader->sort; diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 121e5b905..f06967fa1 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -3004,6 +3004,8 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli state->solid |= i<<5; i = bound(0, ((ent->v->maxs[2]+32)/8), 63); /*up can be negative*/ state->solid |= i<<10; + if (state->solid == 4096) + state->solid = 0; //point sized stuff should just be non-solid. you'll thank me for splitscreens. } } else diff --git a/plugins/ezhud/ezquakeisms.h b/plugins/ezhud/ezquakeisms.h index 97a7328f2..942ffe7bd 100644 --- a/plugins/ezhud/ezquakeisms.h +++ b/plugins/ezhud/ezquakeisms.h @@ -41,6 +41,8 @@ extern cvar_t *cl_multiview; #define Cam_TrackNum() cl.tracknum #define spec_track cl.tracknum #define autocam ((spec_track==-1)?CAM_NONE:CAM_TRACK) +#define CAM_TRACK true +#define CAM_NONE false //#define HAXX #define vid plugvid diff --git a/plugins/ezhud/hud_common.c b/plugins/ezhud/hud_common.c index 50b74348b..78c365d20 100644 --- a/plugins/ezhud/hud_common.c +++ b/plugins/ezhud/hud_common.c @@ -577,11 +577,17 @@ void SCR_HUD_DrawTracking(hud_t *hud) height *= hud_tracking_scale->value; width *= hud_tracking_scale->value; + if (!(cl.spectator && autocam == CAM_TRACK)) + height = 0; + if(!HUD_PrepareDraw(hud, width, height, &x, &y)) { return; } + if (height == 0) + return; + #ifdef HAXX if (cls.mvdplayback && cl_multiview->value && autocam == CAM_TRACK) {