try to fix entertimes.
try to fix gameclock (when joining mid-game on servers that don't support stat_matchstarttime). fix iqm loader issue using the same skin for every surface. modelviewer now allows displaying per-surface info. fix qcc ptr->foo bug. console commands with a leading space are considered to be say messages git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4917 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
0ced7ec194
commit
f35aa1d123
25 changed files with 528 additions and 120 deletions
|
@ -136,6 +136,20 @@ static float CL_TrackScore(player_info_t *pl, char *rule)
|
|||
}
|
||||
return score;
|
||||
}
|
||||
static qboolean CL_MayTrack(int seat, int player)
|
||||
{
|
||||
if (player < 0)
|
||||
return false;
|
||||
if (!cl.players[player].userid || !cl.players[player].name[0] || cl.players[player].spectator)
|
||||
return false;
|
||||
for (seat--; seat >= 0; seat--)
|
||||
{
|
||||
//not valid if an earlier view is tracking it.
|
||||
if (Cam_TrackNum(&cl.playerview[seat]) == player)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//returns the player with the highest frags
|
||||
static int CL_FindHighTrack(int seat, char *rule)
|
||||
{
|
||||
|
@ -146,7 +160,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].userid && cl.players[j].name[0] && !cl.players[j].spectator)
|
||||
if (CL_MayTrack(seat, j))
|
||||
max = CL_TrackScore(&cl.players[j], rule);
|
||||
else
|
||||
{
|
||||
|
@ -158,25 +172,18 @@ static int CL_FindHighTrack(int seat, char *rule)
|
|||
{
|
||||
s = &cl.players[i];
|
||||
score = CL_TrackScore(s, rule);
|
||||
if (s->userid && s->name[0] && !s->spectator && score > max)
|
||||
if (score > max)
|
||||
{
|
||||
if (j == i) //this was our default.
|
||||
continue;
|
||||
//skip it if an earlier seat is watching it already
|
||||
for (k = 0; k < seat; k++)
|
||||
{
|
||||
if (Cam_TrackNum(&cl.playerview[k]) == i)
|
||||
break;
|
||||
}
|
||||
if (!CL_MayTrack(seat, i))
|
||||
continue;
|
||||
if (cl.teamplay && seat && cl.playerview[0].cam_spec_track >= 0 && strcmp(cl.players[cl.playerview[0].cam_spec_track].team, s->team))
|
||||
continue; //when using multiview, keep tracking the team
|
||||
if (k == seat)
|
||||
{
|
||||
max = CL_TrackScore(s, rule);
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
|
@ -224,7 +231,7 @@ qboolean Cam_DrawViewModel(playerview_t *pv)
|
|||
|
||||
int Cam_TrackNum(playerview_t *pv)
|
||||
{
|
||||
if (pv->cam_state == CAM_EYECAM)
|
||||
if (cl.spectator && pv->cam_state == CAM_EYECAM)
|
||||
return pv->cam_spec_track;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -1419,7 +1419,7 @@ void CL_Record_f (void)
|
|||
{
|
||||
MSG_WriteByte (&buf, svc_updateentertime);
|
||||
MSG_WriteByte (&buf, i);
|
||||
MSG_WriteFloat (&buf, player->entertime);
|
||||
MSG_WriteFloat (&buf, realtime - player->realentertime); //seconds since
|
||||
}
|
||||
|
||||
if (*player->userinfo)
|
||||
|
|
|
@ -59,6 +59,14 @@ int CL_TargettedSplit(qboolean nowrap)
|
|||
char *c;
|
||||
int pnum;
|
||||
int mod;
|
||||
|
||||
//explicitly targetted at some seat number from the server
|
||||
if (Cmd_ExecLevel > RESTRICT_SERVER)
|
||||
return Cmd_ExecLevel - RESTRICT_SERVER-1;
|
||||
if (Cmd_ExecLevel == RESTRICT_SERVER)
|
||||
return 0;
|
||||
|
||||
//locally executed command.
|
||||
if (nowrap)
|
||||
mod = MAX_SPLITS;
|
||||
else
|
||||
|
@ -1579,6 +1587,23 @@ qboolean CLQW_SendCmd (sizebuf_t *buf)
|
|||
|
||||
if (cl.sendprespawn)
|
||||
buf->cursize = st; //don't send movement commands while we're still supposedly downloading. mvdsv does not like that.
|
||||
else
|
||||
{
|
||||
//FIXME: user a timer.
|
||||
if (!cls.netchan.message.cursize && cl.allocated_client_slots > 1 && cl.splitclients && (cls.fteprotocolextensions & PEXT_SPLITSCREEN))
|
||||
{
|
||||
int targ = cl_splitscreen.ival+1;
|
||||
if (targ > MAX_SPLITS)
|
||||
targ = MAX_SPLITS;
|
||||
if (cl.splitclients < targ)
|
||||
{
|
||||
char buffer[2048];
|
||||
CL_SendClientCommand(true, "addseat %i %s", cl.splitclients, COM_QuotedString(cls.userinfo[cl.splitclients], buffer, sizeof(buffer), false));
|
||||
}
|
||||
else if (cl.splitclients > targ)
|
||||
CL_SendClientCommand(true, "addseat %i", targ);
|
||||
}
|
||||
}
|
||||
|
||||
return dontdrop;
|
||||
}
|
||||
|
|
|
@ -6099,6 +6099,24 @@ void CLQW_ParseServerMessage (void)
|
|||
Cbuf_Execute (); // make sure any stuffed commands are done
|
||||
CLQW_ParseServerData ();
|
||||
break;
|
||||
case svc_signonnum:
|
||||
cl.splitclients = MSG_ReadByte();
|
||||
for (i = 0; i < cl.splitclients && i < 4; i++)
|
||||
{
|
||||
cl.playerview[i].playernum = MSG_ReadByte();
|
||||
cl.playerview[i].viewentity = cl.playerview[i].playernum+1;
|
||||
}
|
||||
if (i < cl.splitclients)
|
||||
{
|
||||
Con_Printf("Server sent us too many seats!\n");
|
||||
for (; i < cl.splitclients; i++)
|
||||
{ //svcfte_choosesplitclient has a modulo that is also broken, but at least there's no parse errors this way
|
||||
MSG_ReadByte();
|
||||
// CL_SendClientCommand(true, va("%i drop", i+1));
|
||||
}
|
||||
cl.splitclients = MAX_SPLITS;
|
||||
}
|
||||
break;
|
||||
#ifdef PEXT_SETVIEW
|
||||
case svc_setview:
|
||||
if (!(cls.fteprotocolextensions & PEXT_SETVIEW))
|
||||
|
@ -6224,7 +6242,7 @@ void CLQW_ParseServerMessage (void)
|
|||
i = MSG_ReadByte ();
|
||||
if (i >= MAX_CLIENTS)
|
||||
Host_EndGame ("CL_ParseServerMessage: svc_updateentertime > MAX_SCOREBOARD");
|
||||
cl.players[i].entertime = cl.servertime - MSG_ReadFloat ();
|
||||
cl.players[i].realentertime = realtime - MSG_ReadFloat ();
|
||||
break;
|
||||
|
||||
case svc_spawnbaseline:
|
||||
|
|
|
@ -464,7 +464,7 @@ typedef struct {
|
|||
char name[PLUGMAX_SCOREBOARDNAME];
|
||||
int ping;
|
||||
int pl;
|
||||
int starttime;
|
||||
float activetime;
|
||||
int userid;
|
||||
int spectator;
|
||||
char userinfo[1024];
|
||||
|
@ -501,7 +501,7 @@ static qintptr_t VARGS Plug_GetPlayerInfo(void *offset, quintptr_t mask, const q
|
|||
Q_strncpyz(out->name, cl.players[i].name, PLUGMAX_SCOREBOARDNAME);
|
||||
out->ping = cl.players[i].ping;
|
||||
out->pl = cl.players[i].pl;
|
||||
out->starttime = cl.players[i].entertime;
|
||||
out->activetime = realtime - cl.players[i].realentertime;
|
||||
out->userid = cl.players[i].userid;
|
||||
out->spectator = cl.players[i].spectator;
|
||||
Q_strncpyz(out->userinfo, cl.players[i].userinfo, sizeof(out->userinfo));
|
||||
|
|
|
@ -552,7 +552,7 @@ void CL_CalcClientTime(void)
|
|||
}
|
||||
else// if (cls.protocol != CP_QUAKE3)
|
||||
{
|
||||
float oldst = realtime;
|
||||
// float oldst = realtime;
|
||||
|
||||
if (cls.demoplayback && cls.timedemo)
|
||||
{ //more deterministic. one frame is drawn per demo packet parsed. so sync to it as closely as possible.
|
||||
|
@ -574,7 +574,7 @@ void CL_CalcClientTime(void)
|
|||
{
|
||||
float min, max;
|
||||
|
||||
oldst = cl.servertime;
|
||||
// oldst = cl.servertime;
|
||||
|
||||
max = cl.gametime;
|
||||
min = cl.oldgametime;
|
||||
|
@ -624,7 +624,7 @@ void CL_CalcClientTime(void)
|
|||
}
|
||||
}
|
||||
cl.time = cl.servertime;
|
||||
if (oldst == 0)
|
||||
/* if (oldst == 0)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < cl.allocated_client_slots; i++)
|
||||
|
@ -632,9 +632,11 @@ void CL_CalcClientTime(void)
|
|||
cl.players[i].entertime += cl.servertime;
|
||||
}
|
||||
}
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (cls.protocol == CP_NETQUAKE || (cls.demoplayback && cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV))
|
||||
{
|
||||
float want;
|
||||
|
@ -669,6 +671,7 @@ void CL_CalcClientTime(void)
|
|||
if (cl.time > realtime)
|
||||
cl.time = realtime;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void CL_DecodeStateSize(unsigned short solid, int modelindex, vec3_t mins, vec3_t maxs)
|
||||
|
@ -894,7 +897,10 @@ 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;
|
||||
pv->cam_spec_track = -1;
|
||||
}
|
||||
|
||||
#ifdef Q2CLIENT
|
||||
if (cls.protocol == CP_QUAKE2)
|
||||
|
|
|
@ -159,7 +159,7 @@ typedef struct player_info_s
|
|||
// scoreboard information
|
||||
char name[MAX_SCOREBOARDNAME];
|
||||
char team[MAX_INFO_KEY];
|
||||
float entertime;
|
||||
float realentertime; //pegged against realtime, to cope with potentially not knowing the server's time when we first receive this message
|
||||
int frags;
|
||||
int ping;
|
||||
qbyte pl;
|
||||
|
@ -826,7 +826,7 @@ typedef struct
|
|||
|
||||
double matchgametimestart;
|
||||
enum {
|
||||
MATCH_DONTKNOW,
|
||||
MATCH_DONTKNOW, //assumed to be in progress.
|
||||
MATCH_COUNTDOWN,
|
||||
MATCH_STANDBY,
|
||||
MATCH_INPROGRESS
|
||||
|
|
|
@ -419,7 +419,7 @@ int Con_ExecuteLine(console_t *con, char *line)
|
|||
waschat = true;
|
||||
if (keydown[K_CTRL])
|
||||
Cbuf_AddText ("say_team ", RESTRICT_LOCAL);
|
||||
else if (keydown[K_SHIFT])
|
||||
else if (keydown[K_SHIFT] || *line == ' ')
|
||||
Cbuf_AddText ("say ", RESTRICT_LOCAL);
|
||||
else
|
||||
waschat = false;
|
||||
|
|
|
@ -2581,6 +2581,7 @@ typedef struct
|
|||
MV_BONES,
|
||||
MV_SHADER
|
||||
} mode;
|
||||
int surfaceidx;
|
||||
int skingroup;
|
||||
int framegroup;
|
||||
double framechangetime;
|
||||
|
@ -2619,9 +2620,7 @@ static unsigned int genhsv(float h_, float s, float v)
|
|||
((int)(r*255)<<16) |
|
||||
((int)(g*255)<<8) |
|
||||
((int)(b*255)<<0);
|
||||
};
|
||||
const char *Mod_FrameNameForNum(model_t *model, int num);
|
||||
const char *Mod_SkinNameForNum(model_t *model, int num);
|
||||
}
|
||||
|
||||
#include "com_mesh.h"
|
||||
#ifdef SKELETALMODELS
|
||||
|
@ -2650,6 +2649,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
|
|||
entity_t ent;
|
||||
vec3_t fwd, rgt, up;
|
||||
const char *fname;
|
||||
shader_t *shader;
|
||||
vec2_t fs = {8,8};
|
||||
|
||||
modelview_t *mods = c->dptr;
|
||||
|
@ -2713,15 +2713,27 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
|
|||
|
||||
y = 0;
|
||||
|
||||
fname = Mod_FrameNameForNum(ent.model, mods->framegroup);
|
||||
fname = Mod_SurfaceNameForNum(ent.model, mods->surfaceidx);
|
||||
if (!fname)
|
||||
fname = "Unknown Frame";
|
||||
Draw_FunString(0, y, va("%i: %s", mods->framegroup, fname));
|
||||
fname = "Unknown Surface";
|
||||
Draw_FunString(0, y, va("Surf %i: %s", mods->surfaceidx, fname));
|
||||
y+=8;
|
||||
fname = Mod_SkinNameForNum(ent.model, mods->skingroup);
|
||||
|
||||
{
|
||||
char *fname;
|
||||
int numframes = 0;
|
||||
float duration = 0;
|
||||
qboolean loop = false;
|
||||
if (!Mod_FrameInfoForNum(ent.model, mods->surfaceidx, mods->framegroup, &fname, &numframes, &duration, &loop))
|
||||
fname = "Unknown Frame";
|
||||
Draw_FunString(0, y, va("Frame%i: %s (%i poses, %f secs, %s)", mods->framegroup, fname, numframes, duration, loop?"looped":"unlooped"));
|
||||
y+=8;
|
||||
}
|
||||
fname = Mod_SkinNameForNum(ent.model, mods->surfaceidx, mods->skingroup);
|
||||
if (!fname)
|
||||
fname = "Unknown Skin";
|
||||
Draw_FunString(0, y, va("%i: %s", mods->skingroup, fname));
|
||||
shader = Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup);
|
||||
Draw_FunString(0, y, va("Skin %i: (%s) %s", mods->skingroup, fname, shader?shader->name:"NO SHADER"));
|
||||
y+=8;
|
||||
|
||||
switch(mods->mode)
|
||||
|
@ -2759,7 +2771,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_
|
|||
{
|
||||
if (!mods->shadertext)
|
||||
{
|
||||
char *body = Shader_GetShaderBody(Mod_ShaderForSkin(ent.model, mods->skingroup));
|
||||
char *body = Shader_GetShaderBody(Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup));
|
||||
mods->shadertext = Z_StrDup(body);
|
||||
}
|
||||
R_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+16, r_refdef.grect.width, r_refdef.grect.height-16, mods->shadertext, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs);
|
||||
|
@ -2814,17 +2826,33 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k
|
|||
{
|
||||
mods->skingroup += 1;
|
||||
mods->skinchangetime = realtime;
|
||||
Z_Free(mods->shadertext);
|
||||
mods->shadertext = NULL;
|
||||
}
|
||||
else if (key == K_PGDN)
|
||||
{
|
||||
mods->framegroup = max(0, mods->framegroup-1);
|
||||
mods->framechangetime = realtime;
|
||||
Z_Free(mods->shadertext);
|
||||
mods->shadertext = NULL;
|
||||
}
|
||||
else if (key == K_PGUP)
|
||||
{
|
||||
mods->framegroup += 1;
|
||||
mods->framechangetime = realtime;
|
||||
}
|
||||
else if (key == K_INS)
|
||||
{
|
||||
mods->surfaceidx = max(0, mods->surfaceidx-1);
|
||||
Z_Free(mods->shadertext);
|
||||
mods->shadertext = NULL;
|
||||
}
|
||||
else if (key == K_DEL)
|
||||
{
|
||||
mods->surfaceidx += 1;
|
||||
Z_Free(mods->shadertext);
|
||||
mods->shadertext = NULL;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
|
|
|
@ -147,9 +147,9 @@ extern const char *Mod_FixName (const char *modname, const char *worldname);
|
|||
char *Mod_ParseWorldspawnKey (const char *ents, const char *key, char *buffer, size_t sizeofbuffer);
|
||||
|
||||
extern void Mod_Think (void);
|
||||
extern int Mod_SkinNumForName (struct model_s *model, const char *name);
|
||||
extern int Mod_FrameNumForName (struct model_s *model, const char *name);
|
||||
extern float Mod_GetFrameDuration (struct model_s *model, int framenum);
|
||||
extern int Mod_SkinNumForName (struct model_s *model, int surfaceidx, const char *name);
|
||||
extern int Mod_FrameNumForName (struct model_s *model, int surfaceidx, const char *name);
|
||||
extern float Mod_GetFrameDuration (struct model_s *model, int surfaceidx, int framenum);
|
||||
|
||||
#undef FNC
|
||||
|
||||
|
|
|
@ -3090,11 +3090,16 @@ static void QCBUILTIN PF_cs_getplayerkey (pubprogfuncs_t *prinst, struct globalv
|
|||
ret = buffer;
|
||||
sprintf(ret, "%i", cl.players[pnum].pl);
|
||||
}
|
||||
else if (!strcmp(keyname, "entertime")) //packet loss
|
||||
else if (!strcmp(keyname, "activetime")) //packet loss
|
||||
{
|
||||
ret = buffer;
|
||||
sprintf(ret, "%i", (int)cl.players[pnum].entertime);
|
||||
sprintf(ret, "%f", realtime - cl.players[pnum].realentertime);
|
||||
}
|
||||
// else if (!strcmp(keyname, "entertime")) //packet loss
|
||||
// {
|
||||
// ret = buffer;
|
||||
// sprintf(ret, "%i", (int)cl.players[pnum].entertime);
|
||||
// }
|
||||
else if (!strcmp(keyname, "topcolor_rgb")) //packet loss
|
||||
{
|
||||
unsigned int col = cl.players[pnum].ttopcolor;
|
||||
|
|
|
@ -35,9 +35,6 @@ qc must build the skeletal object still, which fills the skeletal object from th
|
|||
|
||||
#include "quakedef.h"
|
||||
|
||||
qboolean Mod_FrameInfoForNum(model_t *model, int num, char **name, int *numframes, float *duration, qboolean *loop);
|
||||
const char *Mod_SkinNameForNum(model_t *model, int num);
|
||||
|
||||
#if defined(RAGDOLL) || defined(SKELETALOBJECTS)
|
||||
|
||||
#include "pr_common.h"
|
||||
|
@ -870,7 +867,7 @@ void skel_generateragdoll_f(void)
|
|||
int numframes;
|
||||
float duration;
|
||||
qboolean loop;
|
||||
if (!Mod_FrameInfoForNum(mod, i, &fname, &numframes, &duration, &loop))
|
||||
if (!Mod_FrameInfoForNum(mod, 0, i, &fname, &numframes, &duration, &loop))
|
||||
break;
|
||||
VFS_PUTS(f, va("//%i %s (%i frames) (%f secs)%s", i, fname, numframes, duration, loop?" (loop)":""));
|
||||
}
|
||||
|
@ -882,7 +879,7 @@ void skel_generateragdoll_f(void)
|
|||
for (i = 0; i < 32768; i++)
|
||||
{
|
||||
const char *sname;
|
||||
sname = Mod_SkinNameForNum(mod, i);
|
||||
sname = Mod_SkinNameForNum(mod, 0, i);
|
||||
if (!sname)
|
||||
break;
|
||||
VFS_PUTS(f, va("//%i %s", i, sname));
|
||||
|
@ -2193,17 +2190,15 @@ void QCBUILTIN PF_gettagindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
|
|||
G_FLOAT(OFS_RETURN) = 0;
|
||||
}
|
||||
|
||||
const char *Mod_FrameNameForNum(model_t *model, int num);
|
||||
const char *Mod_SkinNameForNum(model_t *model, int num);
|
||||
|
||||
//string(float modidx, float framenum) frametoname
|
||||
void QCBUILTIN PF_frametoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
|
||||
{
|
||||
world_t *w = prinst->parms->user;
|
||||
unsigned int modelindex = G_FLOAT(OFS_PARM0);
|
||||
unsigned int skinnum = G_FLOAT(OFS_PARM1);
|
||||
int surfaceidx = 0;
|
||||
model_t *mod = w->Get_CModel(w, modelindex);
|
||||
const char *n = Mod_FrameNameForNum(mod, skinnum);
|
||||
const char *n = Mod_FrameNameForNum(mod, surfaceidx, skinnum);
|
||||
|
||||
if (n)
|
||||
RETURN_TSTRING(n);
|
||||
|
@ -2215,11 +2210,12 @@ void QCBUILTIN PF_frameforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_
|
|||
{
|
||||
world_t *w = prinst->parms->user;
|
||||
unsigned int modelindex = G_FLOAT(OFS_PARM0);
|
||||
int surfaceidx = 0;
|
||||
char *str = PF_VarString(prinst, 1, pr_globals);
|
||||
model_t *mod = w->Get_CModel(w, modelindex);
|
||||
|
||||
if (mod)
|
||||
G_FLOAT(OFS_RETURN) = Mod_FrameNumForName(mod, str);
|
||||
G_FLOAT(OFS_RETURN) = Mod_FrameNumForName(mod, surfaceidx, str);
|
||||
else
|
||||
G_FLOAT(OFS_RETURN) = -1;
|
||||
}
|
||||
|
@ -2228,10 +2224,11 @@ void QCBUILTIN PF_frameduration (pubprogfuncs_t *prinst, struct globalvars_s *pr
|
|||
world_t *w = prinst->parms->user;
|
||||
unsigned int modelindex = G_FLOAT(OFS_PARM0);
|
||||
unsigned int framenum = G_FLOAT(OFS_PARM1);
|
||||
int surfaceidx = 0;
|
||||
model_t *mod = w->Get_CModel(w, modelindex);
|
||||
|
||||
if (mod)
|
||||
G_FLOAT(OFS_RETURN) = Mod_GetFrameDuration(mod, framenum);
|
||||
G_FLOAT(OFS_RETURN) = Mod_GetFrameDuration(mod, surfaceidx, framenum);
|
||||
else
|
||||
G_FLOAT(OFS_RETURN) = 0;
|
||||
}
|
||||
|
@ -2242,8 +2239,9 @@ void QCBUILTIN PF_skintoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
|
|||
world_t *w = prinst->parms->user;
|
||||
unsigned int modelindex = G_FLOAT(OFS_PARM0);
|
||||
unsigned int skinnum = G_FLOAT(OFS_PARM1);
|
||||
int surfaceidx = 0;
|
||||
model_t *mod = w->Get_CModel(w, modelindex);
|
||||
const char *n = Mod_SkinNameForNum(mod, skinnum);
|
||||
const char *n = Mod_SkinNameForNum(mod, surfaceidx, skinnum);
|
||||
|
||||
if (n)
|
||||
RETURN_TSTRING(n);
|
||||
|
@ -2256,10 +2254,11 @@ void QCBUILTIN PF_skinforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
|
|||
world_t *w = prinst->parms->user;
|
||||
unsigned int modelindex = G_FLOAT(OFS_PARM0);
|
||||
char *str = PF_VarString(prinst, 1, pr_globals);
|
||||
int surfaceidx = 0;
|
||||
model_t *mod = w->Get_CModel(w, modelindex);
|
||||
|
||||
if (mod)
|
||||
G_FLOAT(OFS_RETURN) = Mod_SkinNumForName(mod, str);
|
||||
G_FLOAT(OFS_RETURN) = Mod_SkinNumForName(mod, surfaceidx, str);
|
||||
else
|
||||
#endif
|
||||
G_FLOAT(OFS_RETURN) = -1;
|
||||
|
|
|
@ -432,9 +432,9 @@ skinfile_t *Mod_LookupSkin(skinid_t id);
|
|||
void Mod_Init (qboolean initial);
|
||||
void Mod_Shutdown (qboolean final);
|
||||
int Mod_TagNumForName(struct model_s *model, const char *name);
|
||||
int Mod_SkinNumForName(struct model_s *model, const char *name);
|
||||
int Mod_FrameNumForName(struct model_s *model, const char *name);
|
||||
float Mod_GetFrameDuration(struct model_s *model, int frameno);
|
||||
int Mod_SkinNumForName(struct model_s *model, int surfaceidx, const char *name);
|
||||
int Mod_FrameNumForName(struct model_s *model, int surfaceidx, const char *name);
|
||||
float Mod_GetFrameDuration(struct model_s *model, int surfaceidx, int frameno);
|
||||
|
||||
void Mod_ResortShaders(void);
|
||||
void Mod_ClearAll (void);
|
||||
|
|
|
@ -3113,10 +3113,7 @@ ping time frags name
|
|||
})
|
||||
#define COLUMN_TIME COLUMN(time, 4*8, \
|
||||
{ \
|
||||
if (cl.intermission) \
|
||||
total = cl.completed_time - s->entertime; \
|
||||
else \
|
||||
total = cl.servertime - s->entertime; \
|
||||
total = realtime - s->realentertime; \
|
||||
minutes = (int)total/60; \
|
||||
sprintf (num, "%4i", minutes); \
|
||||
Draw_FunStringWidth(x, y, num, 4*8, false, false); \
|
||||
|
|
|
@ -4169,7 +4169,7 @@ int Mod_TagNumForName(model_t *model, const char *name)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int Mod_FrameNumForName(model_t *model, const char *name)
|
||||
int Mod_FrameNumForName(model_t *model, int surfaceidx, const char *name)
|
||||
{
|
||||
galiasanimation_t *group;
|
||||
galiasinfo_t *inf;
|
||||
|
@ -4186,17 +4186,22 @@ int Mod_FrameNumForName(model_t *model, const char *name)
|
|||
|
||||
inf = Mod_Extradata(model);
|
||||
|
||||
while(surfaceidx-->0 && inf)
|
||||
inf = inf->nextsurf;
|
||||
if (inf)
|
||||
{
|
||||
group = inf->ofsanimations;
|
||||
for (i = 0; i < inf->numanimations; i++, group++)
|
||||
{
|
||||
if (!strcmp(group->name, name))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef SERVERONLY
|
||||
int Mod_SkinNumForName(model_t *model, const char *name)
|
||||
int Mod_SkinNumForName(model_t *model, int surfaceidx, const char *name)
|
||||
{
|
||||
int i;
|
||||
galiasinfo_t *inf;
|
||||
|
@ -4206,18 +4211,22 @@ int Mod_SkinNumForName(model_t *model, const char *name)
|
|||
return -1;
|
||||
inf = Mod_Extradata(model);
|
||||
|
||||
while(surfaceidx-->0 && inf)
|
||||
inf = inf->nextsurf;
|
||||
if (inf)
|
||||
{
|
||||
skin = inf->ofsskins;
|
||||
for (i = 0; i < inf->numskins; i++, skin++)
|
||||
{
|
||||
if (!strcmp(skin->name, name))
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *Mod_FrameNameForNum(model_t *model, int num)
|
||||
const char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num)
|
||||
{
|
||||
galiasanimation_t *group;
|
||||
galiasinfo_t *inf;
|
||||
|
@ -4229,13 +4238,16 @@ const char *Mod_FrameNameForNum(model_t *model, int num)
|
|||
|
||||
inf = Mod_Extradata(model);
|
||||
|
||||
if (num >= inf->numanimations)
|
||||
while(surfaceidx-->0 && inf)
|
||||
inf = inf->nextsurf;
|
||||
|
||||
if (!inf || num >= inf->numanimations)
|
||||
return NULL;
|
||||
group = inf->ofsanimations;
|
||||
return group[num].name;
|
||||
}
|
||||
|
||||
qboolean Mod_FrameInfoForNum(model_t *model, int num, char **name, int *numframes, float *duration, qboolean *loop)
|
||||
qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop)
|
||||
{
|
||||
galiasanimation_t *group;
|
||||
galiasinfo_t *inf;
|
||||
|
@ -4247,7 +4259,10 @@ qboolean Mod_FrameInfoForNum(model_t *model, int num, char **name, int *numframe
|
|||
|
||||
inf = Mod_Extradata(model);
|
||||
|
||||
if (num >= inf->numanimations)
|
||||
while(surfaceidx-->0 && inf)
|
||||
inf = inf->nextsurf;
|
||||
|
||||
if (!inf || num >= inf->numanimations)
|
||||
return false;
|
||||
group = inf->ofsanimations;
|
||||
|
||||
|
@ -4259,7 +4274,7 @@ qboolean Mod_FrameInfoForNum(model_t *model, int num, char **name, int *numframe
|
|||
}
|
||||
|
||||
#ifndef SERVERONLY
|
||||
shader_t *Mod_ShaderForSkin(model_t *model, int num)
|
||||
shader_t *Mod_ShaderForSkin(model_t *model, int surfaceidx, int num)
|
||||
{
|
||||
galiasinfo_t *inf;
|
||||
galiasskin_t *skin;
|
||||
|
@ -4268,13 +4283,16 @@ shader_t *Mod_ShaderForSkin(model_t *model, int num)
|
|||
return NULL;
|
||||
inf = Mod_Extradata(model);
|
||||
|
||||
if (num >= inf->numskins)
|
||||
while(surfaceidx-->0 && inf)
|
||||
inf = inf->nextsurf;
|
||||
|
||||
if (!inf || num >= inf->numskins)
|
||||
return NULL;
|
||||
skin = inf->ofsskins;
|
||||
return skin[num].frame[0].shader;
|
||||
}
|
||||
#endif
|
||||
const char *Mod_SkinNameForNum(model_t *model, int num)
|
||||
const char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num)
|
||||
{
|
||||
#ifdef SERVERONLY
|
||||
return NULL;
|
||||
|
@ -4286,17 +4304,39 @@ const char *Mod_SkinNameForNum(model_t *model, int num)
|
|||
return NULL;
|
||||
inf = Mod_Extradata(model);
|
||||
|
||||
if (num >= inf->numskins)
|
||||
while(surfaceidx-->0 && inf)
|
||||
inf = inf->nextsurf;
|
||||
if (!inf || num >= inf->numskins)
|
||||
return NULL;
|
||||
skin = inf->ofsskins;
|
||||
if (!*skin[num].name)
|
||||
return skin[num].frame[0].shadername;
|
||||
else
|
||||
// if (!*skin[num].name)
|
||||
// return skin[num].frame[0].shadername;
|
||||
// else
|
||||
return skin[num].name;
|
||||
#endif
|
||||
}
|
||||
|
||||
float Mod_GetFrameDuration(model_t *model, int frameno)
|
||||
const char *Mod_SurfaceNameForNum(model_t *model, int num)
|
||||
{
|
||||
#ifdef SERVERONLY
|
||||
return NULL;
|
||||
#else
|
||||
galiasinfo_t *inf;
|
||||
|
||||
if (!model || model->type != mod_alias)
|
||||
return NULL;
|
||||
inf = Mod_Extradata(model);
|
||||
|
||||
while(num-->0 && inf)
|
||||
inf = inf->nextsurf;
|
||||
if (inf)
|
||||
return inf->surfacename;
|
||||
else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
float Mod_GetFrameDuration(model_t *model, int surfaceidx, int frameno)
|
||||
{
|
||||
galiasinfo_t *inf;
|
||||
galiasanimation_t *group;
|
||||
|
@ -4305,11 +4345,19 @@ float Mod_GetFrameDuration(model_t *model, int frameno)
|
|||
return 0;
|
||||
inf = Mod_Extradata(model);
|
||||
|
||||
while(surfaceidx-->0 && inf)
|
||||
inf = inf->nextsurf;
|
||||
|
||||
if (inf)
|
||||
{
|
||||
group = inf->ofsanimations;
|
||||
if (frameno < 0 || frameno >= inf->numanimations)
|
||||
return 0;
|
||||
if (frameno >= 0 && frameno < inf->numanimations)
|
||||
{
|
||||
group += frameno;
|
||||
return group->numposes/group->rate;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -6476,6 +6524,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize)
|
|||
skin++;
|
||||
|
||||
Q_strncpyz(skinframe[j].shadername, strings+mesh[i].material, sizeof(skinframe[j].shadername));
|
||||
skinframe++;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -209,8 +209,12 @@ void Mod_DestroyMesh(galiasinfo_t *galias);
|
|||
void Alias_FlushCache(void);
|
||||
void Alias_Shutdown(void);
|
||||
void Alias_Register(void);
|
||||
shader_t *Mod_ShaderForSkin(model_t *model, int num);
|
||||
const char *Mod_SkinNameForNum(model_t *model, int num);
|
||||
shader_t *Mod_ShaderForSkin(model_t *model, int surfaceidx, int num);
|
||||
const char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num);
|
||||
const char *Mod_SurfaceNameForNum(model_t *model, int num);
|
||||
const char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num);
|
||||
const char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num);
|
||||
qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop);
|
||||
|
||||
void Mod_DoCRC(model_t *mod, char *buffer, int buffersize);
|
||||
|
||||
|
|
|
@ -1226,6 +1226,7 @@ qboolean Cvar_Command (int level)
|
|||
char *str;
|
||||
char buffer[65536];
|
||||
int olev;
|
||||
int seat;
|
||||
|
||||
// check variables
|
||||
v = Cvar_FindVar (Cmd_Argv(0));
|
||||
|
@ -1243,6 +1244,7 @@ qboolean Cvar_Command (int level)
|
|||
Con_Printf ("Server tried setting %s cvar\n", v->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
// perform a variable print or set
|
||||
if (Cmd_Argc() == 1)
|
||||
{
|
||||
|
@ -1291,10 +1293,21 @@ qboolean Cvar_Command (int level)
|
|||
Con_Printf ("Cvar %s may not be set via the console\n", v->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef SERVERONLY
|
||||
if (level > RESTRICT_SERVER)
|
||||
{ //directed at a secondary player.
|
||||
CL_SendClientCommand(true, "%i setinfo %s %s", level - RESTRICT_SERVER-1, v->name, COM_QuotedString(str, buffer, sizeof(buffer), false));
|
||||
seat = CL_TargettedSplit(true);
|
||||
if (v->flags & CVAR_USERINFO)
|
||||
{
|
||||
if (Cmd_FromGamecode() && cls.protocol == CP_QUAKEWORLD)
|
||||
{ //don't bother even changing the cvar locally, just update the server's version.
|
||||
//fixme: quake2/quake3 latching.
|
||||
if (seat)
|
||||
CL_SendClientCommand(true, "%i setinfo %s \"%s\"", seat+1, v->name, str);
|
||||
else
|
||||
CL_SendClientCommand(true, "setinfo %s \"%s\"", v->name, str);
|
||||
}
|
||||
else
|
||||
CL_SetInfo(seat, v->name, str);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ struct wedict_s
|
|||
/*the above is shared with qclib*/
|
||||
link_t area;
|
||||
pvscache_t pvsinfo;
|
||||
int lastruntime;
|
||||
|
||||
#ifdef USERBE
|
||||
entityode_t ode;
|
||||
|
|
|
@ -6008,7 +6008,10 @@ QCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *retbuf, QCC_ref_t *r, pbool a
|
|||
char *tname;
|
||||
unsigned int i;
|
||||
if (!idx.cast && t->type == ev_pointer && !arraysize)
|
||||
{
|
||||
t = t->aux_type;
|
||||
dereference = true;
|
||||
}
|
||||
tname = t->name;
|
||||
|
||||
if (t->type == ev_struct || t->type == ev_union)
|
||||
|
@ -10072,6 +10075,10 @@ pbool QCC_CheckUninitialised(int firststatement, int laststatement)
|
|||
QCC_type_t *type = pr_scope->type;
|
||||
int err;
|
||||
|
||||
//assume all, because we don't care for optimisations once we know we're not going to compile anything (removes warning about uninitialised unknown variables/typos).
|
||||
if (pr_error_count)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < type->num_parms; i++)
|
||||
{
|
||||
paramend += type->params[i].type->size;
|
||||
|
|
|
@ -3512,6 +3512,8 @@ static void QCBUILTIN PF_spawnclient (pubprogfuncs_t *prinst, struct globalvars_
|
|||
svs.clients[i].datagram.allowoverflow = true;
|
||||
svs.clients[i].datagram.maxsize = 0;
|
||||
|
||||
svs.clients[i].edict = EDICT_NUM(prinst, i+1);
|
||||
|
||||
SV_SetUpClientEdict (&svs.clients[i], svs.clients[i].edict);
|
||||
|
||||
RETURN_EDICT(prinst, svs.clients[i].edict);
|
||||
|
|
|
@ -1021,6 +1021,7 @@ int SV_ModelIndex (const char *name);
|
|||
void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg);
|
||||
void SVQW_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, unsigned int protext);
|
||||
|
||||
client_t *SV_AddSplit(client_t *controller, char *info, int id);
|
||||
void SV_GetNewSpawnParms(client_t *cl);
|
||||
void SV_SaveSpawnparms (void);
|
||||
void SV_SaveSpawnparmsClient(client_t *client, float *transferparms); //if transferparms, calls SetTransferParms instead, and does not modify the player.
|
||||
|
|
|
@ -94,6 +94,8 @@ cvar_t allow_download_configs = CVARD("allow_download_configs", "0", "1 allows
|
|||
cvar_t allow_download_copyrighted = CVARD("allow_download_copyrighted", "0", "0 blocks download of packages that are considered copyrighted. Specifically, this means packages with a leading 'pak' prefix on the filename.\nIf you take your copyrights seriously, you should also set allow_download_pakmaps 0 and allow_download_pakcontents 0.");
|
||||
cvar_t allow_download_other = CVARD("allow_download_other", "0", "0 blocks downloading of any file that was not covered by any of the directory download blocks.");
|
||||
|
||||
extern cvar_t sv_allow_splitscreen;
|
||||
|
||||
cvar_t sv_serverip = CVARD("sv_serverip", "", "Set this cvar to the server's public ip address if the server is behind a firewall and cannot detect its own public address. Providing a port is required if the firewall/nat remaps it, but is otherwise optional.");
|
||||
cvar_t sv_public = CVAR("sv_public", "0");
|
||||
cvar_t sv_listen_qw = CVARAF("sv_listen_qw", "1", "sv_listen", 0);
|
||||
|
@ -1891,6 +1893,101 @@ void SV_UserDNSResolved(void *ctx, void *data, size_t idx, size_t uid)
|
|||
Z_Free(data);
|
||||
}
|
||||
|
||||
client_t *SV_AddSplit(client_t *controller, char *info, int id)
|
||||
{
|
||||
client_t *cl, *prev;
|
||||
int i;
|
||||
int curclients;
|
||||
|
||||
if (!(controller->fteprotocolextensions & PEXT_SPLITSCREEN))
|
||||
{
|
||||
SV_PrintToClient(controller, PRINT_HIGH, "Your client doesn't support splitscreen\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (curclients = 0, prev = cl = controller; cl; cl = cl->controlled)
|
||||
{
|
||||
prev = cl;
|
||||
curclients++;
|
||||
}
|
||||
|
||||
if (id && curclients != id)
|
||||
return NULL; //this would be weird.
|
||||
|
||||
if (curclients >= 16)
|
||||
return NULL; //protocol limit on stats.
|
||||
if (curclients >= MAX_SPLITS)
|
||||
return NULL;
|
||||
//only allow splitscreen if its explicitly allowed. unless its the local client in which case its always allowed.
|
||||
//wouldn't it be awesome if we could always allow it for spectators? the join command makes that awkward, though I suppose we could just drop the extras in that case.
|
||||
if (!sv_allow_splitscreen.ival && controller->netchan.remote_address.type != NA_LOOPBACK)
|
||||
return NULL;
|
||||
|
||||
for (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)
|
||||
{
|
||||
if (cl->state == cs_free)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == sv.allocated_client_slots)
|
||||
{
|
||||
SV_PrintToClient(controller, PRINT_HIGH, "not enough free player slots\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cl->spectator = controller->spectator;
|
||||
cl->netchan.remote_address = controller->netchan.remote_address;
|
||||
cl->zquake_extensions = controller->zquake_extensions;
|
||||
cl->fteprotocolextensions = controller->fteprotocolextensions;
|
||||
cl->fteprotocolextensions2 = controller->fteprotocolextensions2;
|
||||
cl->penalties = controller->penalties;
|
||||
cl->protocol = controller->protocol;
|
||||
|
||||
|
||||
Q_strncatz(cl->guid, va("%s:%i", controller->guid, curclients), sizeof(cl->guid));
|
||||
cl->name = cl->namebuf;
|
||||
cl->team = cl->teambuf;
|
||||
|
||||
nextuserid++; // so every client gets a unique id
|
||||
cl->userid = nextuserid;
|
||||
|
||||
cl->playerclass = 0;
|
||||
cl->pendingentbits = NULL;
|
||||
cl->edict = EDICT_NUM(svprogfuncs, i+1);
|
||||
|
||||
prev->controlled = cl;
|
||||
prev = cl;
|
||||
cl->controller = prev->controller?prev->controller:host_client;
|
||||
cl->controlled = NULL;
|
||||
|
||||
Q_strncpyS (cl->userinfo, info, sizeof(cl->userinfo)-1);
|
||||
cl->userinfo[sizeof(cl->userinfo)-1] = '\0';
|
||||
|
||||
if (controller->spectator)
|
||||
{
|
||||
Info_RemoveKey (cl->userinfo, "spectator");
|
||||
//this is a hint rather than a game breaker should it fail.
|
||||
Info_SetValueForStarKey (cl->userinfo, "*spectator", "1", sizeof(cl->userinfo));
|
||||
}
|
||||
else
|
||||
Info_RemoveKey (cl->userinfo, "*spectator");
|
||||
|
||||
SV_ExtractFromUserinfo (cl, true);
|
||||
SV_GetNewSpawnParms(cl);
|
||||
|
||||
cl->state = controller->state;
|
||||
|
||||
if (cl->state >= cs_connected)
|
||||
{
|
||||
cl->sendinfo = true;
|
||||
SV_SetUpClientEdict(cl, cl->edict);
|
||||
}
|
||||
if (cl->state >= cs_spawned)
|
||||
SV_Begin_Core(cl);
|
||||
return cl;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SVC_DirectConnect
|
||||
|
@ -2806,7 +2903,13 @@ client_t *SVC_DirectConnect(void)
|
|||
Info_SetValueForStarKey (cl->userinfo, "*spectator", "1", sizeof(cl->userinfo));
|
||||
}
|
||||
|
||||
//only advertise PEXT_SPLITSCREEN when splitscreen is allowed, to avoid spam. this might mean people need to reconnect after its enabled. oh well.
|
||||
if (!sv_allow_splitscreen.ival)
|
||||
newcl->fteprotocolextensions &= ~PEXT_SPLITSCREEN;
|
||||
|
||||
for (clients = 1; clients < numssclients; clients++)
|
||||
SV_AddSplit(newcl, userinfo[clients], clients);
|
||||
#if 0
|
||||
for (clients = 1; clients < numssclients; clients++)
|
||||
{
|
||||
for (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)
|
||||
|
@ -2819,8 +2922,6 @@ client_t *SVC_DirectConnect(void)
|
|||
if (i == sv.allocated_client_slots)
|
||||
break;
|
||||
|
||||
newcl->fteprotocolextensions |= PEXT_SPLITSCREEN;
|
||||
|
||||
temp.frameunion.frames = cl->frameunion.frames; //don't touch these.
|
||||
temp.edict = cl->edict;
|
||||
memcpy(cl, newcl, sizeof(client_t));
|
||||
|
@ -2869,6 +2970,7 @@ client_t *SVC_DirectConnect(void)
|
|||
|
||||
SV_EvaluatePenalties(cl);
|
||||
}
|
||||
#endif
|
||||
newcl->controller = NULL;
|
||||
|
||||
if (!redirect)
|
||||
|
|
|
@ -2083,9 +2083,10 @@ void WPhys_RunEntity (world_t *w, wedict_t *ent)
|
|||
else
|
||||
#endif
|
||||
{
|
||||
if ((unsigned int)ent->v->lastruntime == w->framenum)
|
||||
if (ent->lastruntime == w->framenum)
|
||||
return;
|
||||
ent->v->lastruntime = w->framenum;
|
||||
ent->lastruntime = w->framenum;
|
||||
ent->v->lastruntime = w->physicstime;
|
||||
#ifndef CLIENTONLY
|
||||
svent = NULL;
|
||||
#endif
|
||||
|
@ -2439,7 +2440,7 @@ qboolean SV_Physics (void)
|
|||
old_bot_time = newbottime;
|
||||
for (i = 1; i <= sv.allocated_client_slots; i++)
|
||||
{
|
||||
if (svs.clients[i-1].state && svs.clients[i-1].protocol == SCP_BAD)
|
||||
if (svs.clients[i-1].state > cs_zombie && svs.clients[i-1].protocol == SCP_BAD)
|
||||
{ //then this is a bot
|
||||
oldhost = host_client;
|
||||
oldplayer = sv_player;
|
||||
|
|
|
@ -837,6 +837,9 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
|
|||
}
|
||||
}
|
||||
|
||||
if (client->penalties & BAN_BLIND)
|
||||
continue;
|
||||
|
||||
if (pnum >= 0)
|
||||
{
|
||||
if (pnum != j)
|
||||
|
@ -844,26 +847,26 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
|
|||
}
|
||||
else if (svprogfuncs)
|
||||
{
|
||||
if (!((int)client->edict->xv->dimension_see & dimension_mask))
|
||||
client_t *seat = client;
|
||||
for(seat = client; seat; seat = seat->controlled)
|
||||
{
|
||||
if (!((int)seat->edict->xv->dimension_see & dimension_mask))
|
||||
continue;
|
||||
|
||||
if (!mask) //no pvs? broadcast.
|
||||
goto inrange;
|
||||
|
||||
if (client->penalties & BAN_BLIND)
|
||||
continue;
|
||||
|
||||
if (to == MULTICAST_PHS_R || to == MULTICAST_PHS)
|
||||
{
|
||||
vec3_t delta;
|
||||
VectorSubtract(origin, client->edict->v->origin, delta);
|
||||
VectorSubtract(origin, seat->edict->v->origin, delta);
|
||||
if (DotProduct(delta, delta) <= 1024*1024)
|
||||
goto inrange;
|
||||
}
|
||||
|
||||
{
|
||||
vec3_t pos;
|
||||
VectorAdd(client->edict->v->origin, client->edict->v->view_ofs, pos);
|
||||
VectorAdd(seat->edict->v->origin, seat->edict->v->view_ofs, pos);
|
||||
cluster = sv.world.worldmodel->funcs.ClusterForPoint (sv.world.worldmodel, pos);
|
||||
if (cluster>= 0 && !(mask[cluster>>3] & (1<<(cluster&7)) ) )
|
||||
{
|
||||
|
@ -872,6 +875,7 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inrange:
|
||||
switch (client->protocol)
|
||||
|
|
|
@ -3655,6 +3655,48 @@ void SV_Pause_f (void)
|
|||
|
||||
}
|
||||
|
||||
static void SV_UpdateSeats(client_t *controller)
|
||||
{
|
||||
client_t *cl, *prev;
|
||||
int curclients;
|
||||
|
||||
for (curclients = 0, cl = controller; cl; cl = cl->controlled)
|
||||
{
|
||||
prev = cl;
|
||||
curclients++;
|
||||
}
|
||||
|
||||
ClientReliableWrite_Begin(controller, svc_signonnum, 2+curclients);
|
||||
ClientReliableWrite_Byte(controller, curclients);
|
||||
for (curclients = 0, cl = controller; cl; cl = cl->controlled, curclients++)
|
||||
{
|
||||
ClientReliableWrite_Byte(controller, cl - svs.clients);
|
||||
}
|
||||
|
||||
for (curclients = 0, cl = controller; cl; cl = cl->controlled, curclients++)
|
||||
{
|
||||
// send a fixangle over the reliable channel to make sure it gets there
|
||||
// Never send a roll angle, because savegames can catch the server
|
||||
// in a state where it is expecting the client to correct the angle
|
||||
// and it won't happen if the game was just loaded, so you wind up
|
||||
// with a permanent head tilt
|
||||
ClientReliableWrite_Begin(controller, svcfte_choosesplitclient, 2+curclients);
|
||||
ClientReliableWrite_Byte (controller, curclients);
|
||||
ClientReliableWrite_Byte (controller, svc_setangle);
|
||||
if (cl->edict->v->fixangle)
|
||||
{
|
||||
ClientReliableWrite_Angle(controller, cl->edict->v->angles[0]);
|
||||
ClientReliableWrite_Angle(controller, cl->edict->v->angles[1]);
|
||||
ClientReliableWrite_Angle(controller, 0);//cl->edict->v->angles[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientReliableWrite_Angle(controller, cl->edict->v->v_angle[0]);
|
||||
ClientReliableWrite_Angle(controller, cl->edict->v->v_angle[1]);
|
||||
ClientReliableWrite_Angle(controller, 0);//cl->edict->v->v_angle[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
|
@ -3666,6 +3708,7 @@ The client is going to disconnect, so remove the connection immediately
|
|||
void SV_Drop_f (void)
|
||||
{
|
||||
extern cvar_t sv_fullredirect;
|
||||
client_t *prev;
|
||||
|
||||
SV_EndRedirect ();
|
||||
if (!host_client->drop)
|
||||
|
@ -3681,6 +3724,26 @@ void SV_Drop_f (void)
|
|||
}
|
||||
host_client->drop = true;
|
||||
}
|
||||
|
||||
//if splitscreen, orphan the dropper
|
||||
if (host_client->controller)
|
||||
{
|
||||
for (prev = host_client->controller; prev; prev = prev->controlled)
|
||||
{
|
||||
if (prev->controlled == host_client)
|
||||
{
|
||||
prev->controlled = host_client->controlled;
|
||||
host_client->netchan.remote_address.type = NA_INVALID; //so the remaining client doesn't get the kick too.
|
||||
host_client->protocol = SCP_BAD; //make it a bit like a bot, so we don't try sending any datagrams/reliables at someone that isn't able to receive anything.
|
||||
|
||||
SV_UpdateSeats(host_client->controller);
|
||||
host_client->controller->joinobservelockeduntil = realtime + 3;
|
||||
host_client->controlled = NULL;
|
||||
host_client->controller = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4494,6 +4557,78 @@ void SV_SetUpClientEdict (client_t *cl, edict_t *ent)
|
|||
ent->v->frags = 0;
|
||||
cl->connection_started = realtime;
|
||||
}
|
||||
|
||||
//dynamically add/remove a splitscreen client
|
||||
static void Cmd_AddSeat_f(void)
|
||||
{
|
||||
client_t *cl, *prev;
|
||||
qboolean changed = false;
|
||||
//don't allow an altseat to add. paranoia.
|
||||
if (host_client->controller)
|
||||
return;
|
||||
|
||||
if (host_client->state != cs_spawned)
|
||||
return;
|
||||
|
||||
if (!(host_client->fteprotocolextensions & PEXT_SPLITSCREEN))
|
||||
return;
|
||||
|
||||
if (Cmd_Argc()>1)
|
||||
{
|
||||
int num = atoi(Cmd_Argv(1));
|
||||
int count;
|
||||
|
||||
if (!num || host_client->joinobservelockeduntil > realtime)
|
||||
return;
|
||||
host_client->joinobservelockeduntil = realtime + 2;
|
||||
|
||||
for (count = 1, prev = host_client, cl = host_client->controlled; cl; cl = cl->controlled)
|
||||
{
|
||||
if (count >= num)
|
||||
{
|
||||
for(; cl; cl = prev->controlled)
|
||||
{
|
||||
//unlink it
|
||||
prev->controlled = cl->controlled;
|
||||
cl->controller = NULL;
|
||||
cl->controlled = NULL;
|
||||
|
||||
//make it into a pseudo-bot
|
||||
cl->netchan.remote_address.type = NA_INVALID; //so the remaining client doesn't get the kick too.
|
||||
cl->protocol = SCP_BAD; //make it a bit like a bot, so we don't try sending any datagrams/reliables at someone that isn't able to receive anything.
|
||||
|
||||
//okay, it can get lost now.
|
||||
cl->drop = true;
|
||||
}
|
||||
host_client->controller->joinobservelockeduntil = realtime + 3;
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
prev = cl;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!changed && count <= num)
|
||||
changed = !!SV_AddSplit(host_client, Cmd_Argv(2), num);
|
||||
}
|
||||
else
|
||||
{
|
||||
cl = NULL;
|
||||
/* if (host_client->joinobservelockeduntil > realtime)
|
||||
{
|
||||
SV_TPrintToClient(host_client, PRINT_HIGH, va("Please wait %.1g more seconds\n", host_client->joinobservelockeduntil-realtime));
|
||||
return;
|
||||
}
|
||||
host_client->joinobservelockeduntil = realtime + 2;
|
||||
|
||||
cl = SV_AddSplit(host_client, host_client->userinfo, 0);
|
||||
*/
|
||||
}
|
||||
|
||||
if (cl || changed)
|
||||
SV_UpdateSeats(host_client);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Cmd_Join_f
|
||||
|
@ -4825,6 +4960,9 @@ void Cmd_FPSList_f(void)
|
|||
|
||||
void SV_EnableClientsCSQC(void)
|
||||
{
|
||||
if (host_client->controller)
|
||||
return;
|
||||
|
||||
#ifdef PEXT_CSQC
|
||||
// if ((host_client->fteprotocolextensions & PEXT_CSQC) || atoi(Cmd_Argv(1)))
|
||||
{
|
||||
|
@ -5314,6 +5452,7 @@ ucmd_t ucmds[] =
|
|||
{"nextdl", SV_NextDownload_f, true},
|
||||
|
||||
/*quakeworld specific things*/
|
||||
{"addseat", Cmd_AddSeat_f},
|
||||
{"join", Cmd_Join_f},
|
||||
{"observe", Cmd_Observe_f},
|
||||
{"snap", SV_NoSnap_f},
|
||||
|
|
Loading…
Reference in a new issue