mvd: cl_autotrack_team cvar locks autotrack to a specific team. getting the team name to match can still be problematic when it contains non-ascii chars however.

qw: fix recording mid-map.
q2: add support for recording demos on q2 servers.
q: fix setattachment not using the correct orientations.
q2: now supports splitscreen, as well as increased model and sound limits.
cl: fix crosshair not appearing in splitscreen.
cl: splitscreen clients now get their own colour tints (like the bf command)
snd: tweak audio to be a bit more usable in splitscreen by default.
cl: added con_logcenterprint cvar, for shoving a copy of centerprints onto the console. by default only appears in single player.
qc: add checkbuiltin builtin. for all those #0 builtins without their own extension name.
gl: fix r_dynamic -1 bug that was painfully visible in AD.
mdl: validate per-frame bounds of mdl files (stuff that would crash software renderers).
sv: fix -port or +sv_port commandline args to override the port correctly.
win: attempt to cope with windows symlinks enumerating with the wrong filesizes.
gl: fix skyboxes not appearing properly.
gl: fix sprite alpha/edge issue resulting in some invisible sprites in AD.
gl: fix screenshot_mega, in combination with r_projection. yay for HUGE panoramic screenshots.
q2: fix replacement textures issue.
qw: fix download demonum/X issue, both in client and server.
qw: fix multicast dimensions not always being honoured properly.
nq: fix starting angles sometimes being wrong.
menusys: I'm finally uploading my menusys library, plus example mod, which I'll now be providing like csaddon is.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4997 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2016-01-18 05:22:07 +00:00
parent e996609c73
commit 9360a016c2
136 changed files with 12116 additions and 1703 deletions

View file

@ -1552,11 +1552,11 @@ m-profile:
_qcc-tmp: $(REQDIR)
@$(MAKE) $(TYPE) EXE_NAME="$(EXE_NAME)$(EXEPOSTFIX)" PRECOMPHEADERS="" OUT_DIR="$(OUT_DIR)" WCFLAGS="$(CLIENT_ONLY_CFLAGS) $(WCFLAGS)" LDFLAGS="$(LDFLAGS)" OBJS="QCC_OBJS SOBJS"
qcc-rel:
@$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqcc" OUT_DIR="$(RELEASE_DIR)/qcc" SOBJS="qcctui.o"
@$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqcc" OUT_DIR="$(RELEASE_DIR)/qcc" SOBJS="qcctui.o $(if $(findstring win,$(FTE_TARGET)),fteqcc.o)"
qccgui-rel:
@$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqccgui" OUT_DIR="$(RELEASE_DIR)/qcc" SOBJS="qccgui.o qccguistuff.o fteqcc.o" LDFLAGS="$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows"
qcc-dbg:
@$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME="../fteqcc" OUT_DIR="$(DEBUG_DIR)/qcc" SOBJS="qcctui.o"
@$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME="../fteqcc" OUT_DIR="$(DEBUG_DIR)/qcc" SOBJS="qcctui.o $(if $(findstring win,$(FTE_TARGET)),fteqcc.o)"
qccgui-dbg:
@$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME="../fteqccgui" OUT_DIR="$(DEBUG_DIR)/qcc" SOBJS="qccgui.o qccguistuff.o fteqcc.o" LDFLAGS="$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows"
@ -1577,7 +1577,7 @@ scintilla$(BITS)_static:
@$(MAKE) reldir OUT_DIR=$(RELEASE_DIR)/qcc
@$(MAKE) $(RELEASE_DIR)/scintilla$(BITS).a VPATH="$(SCINTILLA_DIRS)" CFLAGS="$(SCINTILLA_INC) -DDISABLE_D2D -DSTATIC_BUILD -DSCI_LEXER" OUT_DIR=$(RELEASE_DIR)/qcc WCFLAGS="$(WCFLAGS) -Os"
qccgui-scintilla: scintilla$(BITS)_static
@$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqccgui" OUT_DIR="$(RELEASE_DIR)/qcc" SOBJS="qccgui.o qccguistuff.o" WCFLAGS="$(WCFLAGS) -DSCISTATIC" LDFLAGS="$(LDFLAGS) $(RELEASE_DIR)/scintilla$(BITS).a -static -luuid -lole32 -limm32 -lstdc++ -lcomdlg32 -lcomctl32 -lshlwapi -mwindows"
@$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqccgui" OUT_DIR="$(RELEASE_DIR)/qcc" SOBJS="qccgui.o qccguistuff.o fteqcc.o" WCFLAGS="$(WCFLAGS) -DSCISTATIC" LDFLAGS="$(LDFLAGS) $(RELEASE_DIR)/scintilla$(BITS).a -static -luuid -lole32 -limm32 -lstdc++ -lcomdlg32 -lcomctl32 -lshlwapi -mwindows"
ifdef windir
debugdir:

View file

@ -51,6 +51,7 @@ static void QDECL CL_AutoTrackChanged(cvar_t *v, char *oldval)
Cam_AutoTrack_Update(v->string);
}
// track high fragger
cvar_t cl_autotrack_team = CVARD("cl_autotrack_team", "", "Specifies a team name that should be auto-tracked (players on other teams will not be candidates for autotracking). Accepts * and ? wildcards for awkward chars.");
cvar_t cl_autotrack = CVARCD("cl_autotrack", "auto", CL_AutoTrackChanged, "Specifies the default tracking mode at the start of the map. Use the 'autotrack' command to reset/apply an auto-tracking mode without changing the default.\nValid values are: high, ^hkiller^h, mod, user. Other values are treated as weighting scripts for mvd playback, where available.");
cvar_t cl_hightrack = CVARD("cl_hightrack", "0", "Obsolete. If you want hightrack, use '[cl_]autotrack high' instead.");
@ -346,12 +347,14 @@ static float CL_TrackScore(player_info_t *pl, char **rule, float *weights, int p
*rule = s;
return l;
}
static qboolean CL_MayTrack(int seat, int player)
static qboolean CL_MayAutoTrack(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;
if (*cl_autotrack_team.string && !wildcmp(cl_autotrack_team.string, cl.players[player].team))
return false;
for (seat--; seat >= 0; seat--)
{
//not valid if an earlier view is tracking it.
@ -418,7 +421,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 (CL_MayTrack(seat, j))
if (CL_MayAutoTrack(seat, j))
{
p = rule;
max = CL_TrackScore(&cl.players[j], &p, weights, PRI_TOP);
@ -434,7 +437,7 @@ static int CL_FindHighTrack(int seat, char *rule)
s = &cl.players[i];
if (j == i) //this was our default.
continue;
if (!CL_MayTrack(seat, i))
if (!CL_MayAutoTrack(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
@ -449,12 +452,13 @@ static int CL_FindHighTrack(int seat, char *rule)
if (j == -1 && cl.teamplay && seat)
{
//do it again, but with the teamplay check inverted
//fixme: should probably reduce seat count instead, at least in demos.
for (i = 0; i < cl.allocated_client_slots; i++)
{
s = &cl.players[i];
if (j == i) //this was our default.
continue;
if (!CL_MayTrack(seat, i))
if (!CL_MayAutoTrack(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
@ -468,7 +472,7 @@ static int CL_FindHighTrack(int seat, char *rule)
}
}
if (j != -1 && CL_MayTrack(seat, cl.playerview[seat].cam_spec_track) && !instant)
if (j != -1 && CL_MayAutoTrack(seat, cl.playerview[seat].cam_spec_track) && !instant)
{
i = cl.playerview[seat].cam_spec_track;
//extra hacks to prevent 'random' switching mid-game
@ -1323,6 +1327,7 @@ void Cam_Track4_f(void)
void CL_InitCam(void)
{
Cvar_Register (&cl_autotrack, cl_spectatorgroup);
Cvar_Register (&cl_autotrack_team, cl_spectatorgroup);
Cvar_Register (&cl_hightrack, cl_spectatorgroup);
Cvar_Register (&cl_chasecam, cl_spectatorgroup);
// Cvar_Register (&cl_camera_maxpitch, cl_spectatorgroup);

View file

@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
void CL_FinishTimeDemo (void);
float demtime;
float recdemostart; //keyed to Sys_DoubleTime
int demoframe;
int cls_lastto;
@ -102,12 +103,12 @@ void CL_WriteDemoCmd (usercmd_t *pcmd)
q1usercmd_t cmd;
//nq doesn't have this info
if (cls.demorecording != 1)
if (cls.demorecording != DPB_QUAKEWORLD)
return;
//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, demtime);
fl = LittleFloat((float)demtime);
fl = LittleFloat(Sys_DoubleTime()-recdemostart);
VFS_WRITE (cls.demooutfile, &fl, sizeof(fl));
c = dem_cmd;
@ -156,10 +157,10 @@ void CL_WriteDemoMessage (sizebuf_t *msg, int payloadoffset)
switch (cls.demorecording)
{
case 0:
default:
return;
case 1: //QW
fl = LittleFloat((float)demtime);
case DPB_QUAKEWORLD: //QW
fl = LittleFloat(Sys_DoubleTime()-recdemostart);
VFS_WRITE (cls.demooutfile, &fl, sizeof(fl));
c = dem_read;
@ -187,7 +188,15 @@ void CL_WriteDemoMessage (sizebuf_t *msg, int payloadoffset)
VFS_WRITE (cls.demooutfile, msg->data + msg_readcount, msg->cursize - msg_readcount);
}
break;
case 2: //NQ
#ifdef Q2CLIENT
case DPB_QUAKE2:
len = LittleLong (net_message.cursize - payloadoffset);
VFS_WRITE(cls.demooutfile, &len, sizeof(len));
VFS_WRITE(cls.demooutfile, net_message.data + payloadoffset, net_message.cursize - payloadoffset);
break;
#endif
#ifdef NQPROT
case DPB_NETQUAKE: //NQ
len = LittleLong (net_message.cursize - payloadoffset);
VFS_WRITE(cls.demooutfile, &len, sizeof(len));
for (i=0 ; i<3 ; i++)
@ -197,6 +206,7 @@ void CL_WriteDemoMessage (sizebuf_t *msg, int payloadoffset)
}
VFS_WRITE(cls.demooutfile, net_message.data + payloadoffset, net_message.cursize - payloadoffset);
break;
#endif
}
if (record_flush.ival)
VFS_FLUSH (cls.demooutfile);
@ -295,7 +305,7 @@ int readdemobytes(int *readpos, void *data, int len)
}
trybytes = sizeof(demobuffer)-demooffset-demobuffersize;
if (trybytes)
if (trybytes > 4096)
i = VFS_READ(cls.demoinfile, demobuffer+demooffset+demobuffersize, trybytes);
else
i = 0;
@ -940,11 +950,22 @@ void CL_Stop_f (void)
}
// write a disconnect message to the demo file
SZ_Clear (&net_message);
MSG_WriteLong (&net_message, -1); // -1 sequence means out of band
MSG_WriteByte (&net_message, svc_disconnect);
MSG_WriteString (&net_message, "EndOfDemo");
CL_WriteDemoMessage (&net_message, sizeof(int));
#ifdef Q2CLIENT
if (cls.demorecording == DPB_QUAKE2)
{ //q2 demos signify eof with a -1 size
int len = ~0;
VFS_WRITE(cls.demooutfile, &len, sizeof(len));
}
else
#endif
{
SZ_Clear (&net_message);
msg_readcount = 0;
MSG_WriteLong (&net_message, -1); // -1 sequence means out of band
MSG_WriteByte (&net_message, svc_disconnect);
MSG_WriteString (&net_message, "EndOfDemo");
CL_WriteDemoMessage (&net_message, sizeof(int));
}
// finish up
VFS_CLOSE (cls.demooutfile);
@ -955,7 +976,13 @@ void CL_Stop_f (void)
FS_FlushFSHash();
}
void CL_WriteRecordQ2DemoMessage(sizebuf_t *msg)
{ //q2 is really simple, and doesn't have timings in its demo format.
int len = LittleLong (msg->cursize);
VFS_WRITE (cls.demooutfile, &len, 4);
VFS_WRITE (cls.demooutfile, msg->data, msg->cursize);
}
/*
====================
CL_WriteDemoMessage
@ -975,7 +1002,7 @@ void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq)
if (!cls.demorecording)
return;
fl = LittleFloat((float)demtime);
fl = LittleFloat(Sys_DoubleTime()-recdemostart);
VFS_WRITE (cls.demooutfile, &fl, sizeof(fl));
c = dem_read;
@ -1003,10 +1030,10 @@ void CL_WriteSetDemoMessage (void)
//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, demtime);
if (cls.demorecording != 1)
if (cls.demorecording != DPB_QUAKEWORLD)
return;
fl = LittleFloat((float)demtime);
fl = LittleFloat(Sys_DoubleTime()-recdemostart);
VFS_WRITE (cls.demooutfile, &fl, sizeof(fl));
c = dem_set;
@ -1033,6 +1060,12 @@ void CL_RecordMap_f (void)
char demoname[MAX_QPATH];
char mapname[MAX_QPATH];
char demoext[8];
if (Cmd_Argc() < 3)
{
Con_Printf("%s: demoname mapname\n", Cmd_Argv(0));
return;
}
Q_strncpyz(demoname, Cmd_Argv(1), sizeof(demoname));
Q_strncpyz(mapname, Cmd_Argv(2), sizeof(mapname));
CL_Disconnect_f();
@ -1059,16 +1092,18 @@ void CL_RecordMap_f (void)
}
if (!strcmp(demoext, "dem"))
{
cls.demorecording = 2;
cls.demorecording = DPB_NETQUAKE;
VFS_PUTS(cls.demooutfile, "-1\n");
}
else
cls.demorecording = 1;
cls.demorecording = DPB_QUAKEWORLD;
CL_WriteSetDemoMessage();
}
}
#endif
const char *Get_Q2ConfigString(int i);
/*
====================
CL_Record_f
@ -1082,21 +1117,23 @@ void CL_Record_f (void)
char name[MAX_OSPATH];
sizebuf_t buf;
char buf_data[MAX_QWMSGLEN];
int n, i, j;
int n, i, j, seat;
char *s, *p, *fname;
entity_t *ent;
entity_state_t *es;
player_info_t *player;
extern char gamedirfile[];
int seq = 1;
const char *defaultext;
c = Cmd_Argc();
if (c > 2)
{
#ifndef CLIENTONLY
CL_RecordMap_f();
#endif
#else
Con_Printf ("record <demoname>\n");
#endif
return;
}
@ -1106,6 +1143,18 @@ void CL_Record_f (void)
return;
}
if (cls.protocol == CP_QUAKE2)
defaultext = ".dm2";
// else if (cls.protocol == CP_NETQUAKE)
// defaultext = ".dem";
else if (cls.protocol == CP_QUAKEWORLD)
defaultext = ".qwd";
else
{
Con_Printf("Unable to record mid-map - try a different network protocol\n");
return;
}
if (cls.demorecording)
CL_Stop_f();
@ -1177,7 +1226,7 @@ void CL_Record_f (void)
name[sizeof(name)-1-8] = '\0';
//make a unique name (unless the user specified it).
strcat (name, ".qwd"); //we have the space
strcat (name, defaultext); //we have the space
if (c != 2)
{
vfsfile_t *f;
@ -1210,22 +1259,25 @@ void CL_Record_f (void)
Con_Printf ("ERROR: couldn't open.\n");
return;
}
cls.demohadkeyframe = false;
Con_Printf ("recording to %s.\n", name);
cls.demorecording = true;
recdemostart = Sys_DoubleTime();
/*-------------------------------------------------*/
memset(&buf, 0, sizeof(buf));
buf.data = buf_data;
buf.maxsize = sizeof(buf_data);
buf.prim = cls.netchan.netprim;
switch(cls.protocol)
{
case CP_QUAKEWORLD:
cls.demorecording = DPB_QUAKEWORLD;
// serverdata
// send the info about the new client to all connected clients
memset(&buf, 0, sizeof(buf));
buf.data = buf_data;
buf.maxsize = sizeof(buf_data);
buf.prim = cls.netchan.netprim;
// send the serverdata
MSG_WriteByte (&buf, svc_serverdata);
@ -1250,9 +1302,7 @@ void CL_Record_f (void)
MSG_WriteByte (&buf, cl.allocated_client_slots);
MSG_WriteByte (&buf, cl.splitclients | (cl.spectator?128:0));
for (i = 0; i < cl.splitclients; i++)
{
MSG_WriteByte (&buf, cl.playerview[i].playernum);
}
}
else
{
@ -1286,9 +1336,35 @@ void CL_Record_f (void)
MSG_WriteByte (&buf, svc_stufftext);
MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) );
// send music (delayed)
MSG_WriteByte (&buf, svc_cdtrack);
MSG_WriteByte (&buf, 0); // none in demos
// send music
Media_WriteCurrentTrack(&buf);
//paknames
{
char buffer[1024];
FS_GetPackNames(buffer, sizeof(buffer), 2, true); /*retain extensions, or we'd have to assume pk3*/
MSG_WriteByte(&buf, svc_stufftext);
SZ_Write(&buf, "//paknames ", 11);
SZ_Write(&buf, buffer, strlen(buffer));
MSG_WriteString(&buf, "\n");
}
//Paks
{
char buffer[1024];
FS_GetPackHashes(buffer, sizeof(buffer), false);
MSG_WriteByte(&buf, svc_stufftext);
SZ_Write(&buf, "//paks ", 7);
SZ_Write(&buf, buffer, strlen(buffer));
MSG_WriteString(&buf, "\n");
}
//FIXME: //at
//FIXME: //wps
//FIXME: //it
MSG_WriteByte (&buf, svc_setpause);
MSG_WriteByte (&buf, cl.paused);
#ifdef PEXT_SETVIEW
if (cl.playerview[0].viewentity != cl.playerview[0].playernum+1) //tell the player if we have a different view entity
@ -1310,14 +1386,22 @@ void CL_Record_f (void)
while (*s)
{
MSG_WriteString (&buf, s);
if (buf.cursize > MAX_QWMSGLEN/2)
if (buf.cursize > buf.maxsize/2 && (n&0xff))
{
MSG_WriteByte (&buf, 0);
MSG_WriteByte (&buf, n);
CL_WriteRecordDemoMessage (&buf, seq++);
SZ_Clear (&buf);
MSG_WriteByte (&buf, svc_soundlist);
MSG_WriteByte (&buf, n + 1);
if (n + 1 > 0xff)
{
MSG_WriteByte (&buf, svcfte_soundlistshort);
MSG_WriteShort (&buf, n + 1);
}
else
{
MSG_WriteByte (&buf, svc_soundlist);
MSG_WriteByte (&buf, n + 1);
}
}
n++;
s = cl.sound_name[n+1];
@ -1330,6 +1414,8 @@ void CL_Record_f (void)
SZ_Clear (&buf);
}
//FIXME: vweps
// modellist
MSG_WriteByte (&buf, svc_modellist);
MSG_WriteByte (&buf, 0);
@ -1339,14 +1425,22 @@ void CL_Record_f (void)
while (*s)
{
MSG_WriteString (&buf, s);
if (buf.cursize > MAX_QWMSGLEN/2)
if (buf.cursize > buf.maxsize/2 && (n&0xff))
{
MSG_WriteByte (&buf, 0);
MSG_WriteByte (&buf, n);
CL_WriteRecordDemoMessage (&buf, seq++);
SZ_Clear (&buf);
MSG_WriteByte (&buf, svc_modellist);
MSG_WriteByte (&buf, n + 1);
if (n + 1 > 0xff)
{
MSG_WriteByte (&buf, svcfte_modellistshort);
MSG_WriteShort (&buf, n + 1);
}
else
{
MSG_WriteByte (&buf, svc_modellist);
MSG_WriteByte (&buf, n + 1);
}
}
n++;
s = cl.model_name[n+1];
@ -1359,39 +1453,68 @@ void CL_Record_f (void)
SZ_Clear (&buf);
}
// spawnstatic
for (i = 0; i < cl.num_statics; i++)
//particleeffectnum stuff
for (i = 1; i < MAX_SSPARTICLESPRE; i++)
{
ent = &cl_static_entities[i].ent;
if (!cl.particle_ssname[i])
break;
MSG_WriteByte(&buf, svcfte_precache);
MSG_WriteShort(&buf, PC_PARTICLE | i);
MSG_WriteString(&buf, cl.particle_ssname[i]);
MSG_WriteByte (&buf, svc_spawnstatic);
for (j = 1; j < MAX_PRECACHE_MODELS; j++)
if (ent->model == cl.model_precache[j])
break;
if (j == MAX_PRECACHE_MODELS)
MSG_WriteByte (&buf, 0);
else
MSG_WriteByte (&buf, j);
MSG_WriteByte (&buf, ent->framestate.g[FS_REG].frame[0]);
MSG_WriteByte (&buf, 0);
MSG_WriteByte (&buf, ent->skinnum);
for (j=0 ; j<3 ; j++)
{
MSG_WriteCoord (&buf, ent->origin[j]);
MSG_WriteAngle (&buf, ent->angles[j]);
}
if (buf.cursize > MAX_QWMSGLEN/2)
if (buf.cursize > buf.maxsize/2)
{
CL_WriteRecordDemoMessage (&buf, seq++);
SZ_Clear (&buf);
}
}
// spawnstaticsound
//FIXME: custom tents (needed for hexen2)
// spawnstatic
for (i = 0; i < cl.num_statics; i++)
{
ent = &cl_static_entities[i].ent;
#ifndef CLIENTONLY //FIXME
if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
{
MSG_WriteByte(&buf, svcfte_spawnstatic2);
SVFTE_EmitBaseline(&cl_static_entities[i].state, false, &buf, cls.fteprotocolextensions2);
}
//else if (cls.fteprotocolextensions & PEXT_SPAWNSTATIC2) //qw deltas
else
#endif
{
MSG_WriteByte (&buf, svc_spawnstatic);
for (j = 1; j < MAX_PRECACHE_MODELS; j++)
if (ent->model == cl.model_precache[j])
break;
if (j == MAX_PRECACHE_MODELS)
MSG_WriteByte (&buf, 0);
else
MSG_WriteByte (&buf, j);
MSG_WriteByte (&buf, ent->framestate.g[FS_REG].frame[0]);
MSG_WriteByte (&buf, 0);
MSG_WriteByte (&buf, ent->skinnum);
for (j=0 ; j<3 ; j++)
{
MSG_WriteCoord (&buf, ent->origin[j]);
MSG_WriteAngle (&buf, ent->angles[j]);
}
}
if (buf.cursize > buf.maxsize/2)
{
CL_WriteRecordDemoMessage (&buf, seq++);
SZ_Clear (&buf);
}
}
// FIXME: static sounds
// static sounds are skipped in demos, life is hard
// baselines
@ -1402,20 +1525,30 @@ void CL_Record_f (void)
if (memcmp(es, &nullentitystate, sizeof(nullentitystate)))
{
MSG_WriteByte (&buf,svc_spawnbaseline);
MSG_WriteEntity (&buf, i);
MSG_WriteByte (&buf, es->modelindex);
MSG_WriteByte (&buf, es->frame);
MSG_WriteByte (&buf, es->colormap);
MSG_WriteByte (&buf, es->skinnum);
for (j=0 ; j<3 ; j++)
#ifndef CLIENTONLY //FIXME
if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
{
MSG_WriteCoord(&buf, es->origin[j]);
MSG_WriteAngle(&buf, es->angles[j]);
MSG_WriteByte(&buf, svcfte_spawnbaseline2);
SVFTE_EmitBaseline(es, true, &buf, cls.fteprotocolextensions2);
}
//else if (cls.fteprotocolextensions & PEXT_SPAWNSTATIC2) //qw deltas
else
#endif
{
MSG_WriteByte (&buf,svc_spawnbaseline);
MSG_WriteEntity (&buf, i);
if (buf.cursize > MAX_QWMSGLEN/2)
MSG_WriteByte (&buf, es->modelindex);
MSG_WriteByte (&buf, es->frame);
MSG_WriteByte (&buf, es->colormap);
MSG_WriteByte (&buf, es->skinnum);
for (j=0 ; j<3 ; j++)
{
MSG_WriteCoord(&buf, es->origin[j]);
MSG_WriteAngle(&buf, es->angles[j]);
}
}
if (buf.cursize > buf.maxsize/2)
{
CL_WriteRecordDemoMessage (&buf, seq++);
SZ_Clear (&buf);
@ -1474,7 +1607,7 @@ void CL_Record_f (void)
MSG_WriteString (&buf, player->userinfo);
}
if (buf.cursize > MAX_QWMSGLEN/2)
if (buf.cursize > buf.maxsize/2)
{
CL_WriteRecordDemoMessage (&buf, seq++);
SZ_Clear (&buf);
@ -1506,33 +1639,118 @@ void CL_Record_f (void)
MSG_WriteByte (&buf, (unsigned char)i);
MSG_WriteString (&buf, cl_lightstyle[i].map);
}
}
for (i = ((cls.fteprotocolextensions&PEXT_HEXEN2)?MAX_QW_STATS:MAX_CL_STATS); i >= 0; i--)
{
if (!cl.playerview[0].stats[i])
continue;
MSG_WriteByte (&buf, svcqw_updatestatlong);
MSG_WriteByte (&buf, i);
MSG_WriteLong (&buf, cl.playerview[0].stats[i]);
if (buf.cursize > MAX_QWMSGLEN/2)
if (buf.cursize > buf.maxsize/2)
{
CL_WriteRecordDemoMessage (&buf, seq++);
SZ_Clear (&buf);
}
}
for (seat = 0; seat < cl.splitclients; seat++)
{
//higher stats should be 0 and thus not be sent, if not valid.
for (i = 0; i < MAX_CL_STATS; i++)
{
if (cl.playerview[seat].stats[i] || cl.playerview[seat].statsf[i])
{
double fs = cl.playerview[seat].statsf[i];
double is = cl.playerview[seat].stats[i];
if (seat)
{
MSG_WriteByte (&buf, svcfte_choosesplitclient);
MSG_WriteByte (&buf, seat);
}
if ((int)fs == is)
{
MSG_WriteByte (&buf, svcqw_updatestatlong);
MSG_WriteByte (&buf, i);
MSG_WriteLong (&buf, is);
}
else
{
MSG_WriteByte (&buf, svcfte_updatestatfloat);
MSG_WriteByte (&buf, i);
MSG_WriteLong (&buf, fs);
}
}
if (cl.playerview[seat].statsstr[i])
{
if (seat)
{
MSG_WriteByte (&buf, svcfte_choosesplitclient);
MSG_WriteByte (&buf, seat);
}
MSG_WriteByte (&buf, svcfte_updatestatstring);
MSG_WriteByte (&buf, i);
MSG_WriteString (&buf, cl.playerview[seat].statsstr[i]);
}
if (buf.cursize > buf.maxsize/2)
{
CL_WriteRecordDemoMessage (&buf, seq++);
SZ_Clear (&buf);
}
}
}
// get the client to check and download skins
// when that is completed, a begin command will be issued
MSG_WriteByte (&buf, svc_stufftext);
MSG_WriteString (&buf, va("skins\n") );
MSG_WriteString (&buf, "skins\n");
CL_WriteRecordDemoMessage (&buf, seq++);
CL_WriteSetDemoMessage();
//FIXME: make sure the deltas are reset
// done
break;
#ifdef Q2CLIENT
case CP_QUAKE2:
cls.demorecording = DPB_QUAKE2;
MSG_WriteByte (&buf, svcq2_serverdata);
if (cls.fteprotocolextensions) //maintain demo compatability
{
MSG_WriteLong (&buf, PROTOCOL_VERSION_FTE);
MSG_WriteLong (&buf, cls.fteprotocolextensions);
}
if (cls.fteprotocolextensions2) //maintain demo compatability
{
MSG_WriteLong (&buf, PROTOCOL_VERSION_FTE2);
MSG_WriteLong (&buf, cls.fteprotocolextensions2);
}
MSG_WriteLong (&buf, cls.protocol_q2);
MSG_WriteLong (&buf, 0x80000000 + cl.servercount);
MSG_WriteByte (&buf, 1);
MSG_WriteString (&buf, gamedirfile);
MSG_WriteShort (&buf, cl.playerview[0].playernum);
MSG_WriteString (&buf, cl.levelname);
for (i = 0; i < Q2MAX_CONFIGSTRINGS; i++)
{
const char *cs = Get_Q2ConfigString(i);
if (buf.cursize + 4 + strlen(cs) > buf.maxsize)
{
CL_WriteRecordQ2DemoMessage (&buf);
SZ_Clear (&buf);
}
MSG_WriteByte (&buf, svcq2_configstring);
MSG_WriteShort (&buf, i);
MSG_WriteString (&buf, cs);
}
CLQ2_WriteDemoBaselines(&buf);
MSG_WriteByte (&buf, svcq2_stufftext);
MSG_WriteString (&buf, "precache\n");
CL_WriteRecordQ2DemoMessage (&buf);
break;
#endif
case CP_NETQUAKE: //FIXME
default:
Con_Printf("Unable to begin demo recording with this network protocol\n");
CL_Stop_f();
@ -1580,17 +1798,36 @@ void CL_ReRecord_f (void)
//
// open the demo file
//
COM_RequireExtension (name, ".qwd", sizeof(name));
switch (cls.protocol)
{
default:
case CP_QUAKEWORLD:
cls.demorecording = DPB_QUAKEWORLD;
COM_RequireExtension (name, ".qwd", sizeof(name));
break;
#ifdef NQPROT
case CP_NETQUAKE:
cls.demorecording = DPB_NETQUAKE;
COM_RequireExtension (name, ".dem", sizeof(name));
break;
#endif
#ifdef Q2CLIENT
case CP_QUAKE2:
cls.demorecording = DPB_QUAKE2;
COM_RequireExtension (name, ".dm2", sizeof(name));
break;
#endif
}
cls.demooutfile = FS_OpenVFS (name, "wb", FS_GAME);
if (!cls.demooutfile)
{
Con_Printf ("ERROR: couldn't open.\n");
cls.demorecording = DPB_NONE;
return;
}
Con_Printf ("recording to %s.\n", name);
cls.demorecording = true;
CL_Disconnect();
CL_BeginServerReconnect();

View file

@ -1699,7 +1699,7 @@ void CL_RotateAroundTag(entity_t *ent, int entnum, int parenttagent, int parentt
vec3_t axis[3];
float transform[12], parent[12], result[12], old[12], temp[12];
int model;
model_t *model;
framestate_t fstate;
if (parenttagent >= cl.maxlerpents)
@ -1708,6 +1708,20 @@ void CL_RotateAroundTag(entity_t *ent, int entnum, int parenttagent, int parentt
return;
}
//old is the entity's relative transform (relative to the parent entity's tag)
old[0] = ent->axis[0][0];
old[1] = ent->axis[1][0];
old[2] = ent->axis[2][0];
old[3] = ent->origin[0];
old[4] = ent->axis[0][1];
old[5] = ent->axis[1][1];
old[6] = ent->axis[2][1];
old[7] = ent->origin[1];
old[8] = ent->axis[0][2];
old[9] = ent->axis[1][2];
old[10] = ent->axis[2][2];
old[11] = ent->origin[2];
memset(&fstate, 0, sizeof(fstate));
//for visibility checks
@ -1716,12 +1730,44 @@ void CL_RotateAroundTag(entity_t *ent, int entnum, int parenttagent, int parentt
ps = CL_FindPacketEntity(parenttagent);
if (ps)
{
if (ps->tagentity)
CL_RotateAroundTag(ent, entnum, ps->tagentity, ps->tagindex);
if (parenttagent >= cl.maxlerpents)
{
org = ps->origin;
ang = ps->angles;
}
else
{
lerpents_t *le = &cl.lerpents[parenttagent];
org = le->origin;
ang = le->angles;
}
org = ps->origin;
ang = ps->angles;
model = ps->modelindex;
if (ps->modelindex <= countof(cl.model_precache) && cl.model_precache[ps->modelindex] && cl.model_precache[ps->modelindex]->loadstate == MLS_LOADED)
model = cl.model_precache[ps->modelindex];
else
model = NULL;
if (model && model->type == mod_alias)
{
ang[0]*=-1;
AngleVectors(ang, axis[0], axis[1], axis[2]);
ang[0]*=-1;
}
else
AngleVectors(ang, axis[0], axis[1], axis[2]);
VectorInverse(axis[1]);
parent[0] = axis[0][0];
parent[1] = axis[1][0];
parent[2] = axis[2][0];
parent[3] = org[0];
parent[4] = axis[0][1];
parent[5] = axis[1][1];
parent[6] = axis[2][1];
parent[7] = org[1];
parent[8] = axis[0][2];
parent[9] = axis[1][2];
parent[10] = axis[2][2];
parent[11] = org[2];
CL_LerpNetFrameState(FS_REG, &fstate, &cl.lerpents[parenttagent]);
@ -1755,7 +1801,7 @@ void CL_RotateAroundTag(entity_t *ent, int entnum, int parenttagent, int parentt
org = cl.inframes[parsecountmod].playerstate[parenttagent-1].origin;
ang = cl.inframes[parsecountmod].playerstate[parenttagent-1].viewangles;
}
model = cl.inframes[parsecountmod].playerstate[parenttagent-1].modelindex;
model = cl.model_precache[cl.inframes[parsecountmod].playerstate[parenttagent-1].modelindex];
CL_LerpNetFrameState(FS_REG, &fstate, &cl.lerpplayers[parenttagent-1]);
}
@ -1766,47 +1812,17 @@ void CL_RotateAroundTag(entity_t *ent, int entnum, int parenttagent, int parentt
}
}
if (ang)
{
ang[0]*=-1;
AngleVectors(ang, axis[0], axis[1], axis[2]);
ang[0]*=-1;
VectorInverse(axis[1]);
// fstate.g[FS_REG].lerpfrac = CL_EntLerpFactor(tagent);
// fstate.g[FS_REG].frametime[0] = cl.time - cl.lerpents[tagent].framechange;
// fstate.g[FS_REG].frametime[1] = cl.time - cl.lerpents[tagent].oldframechange;
if (Mod_GetTag(cl.model_precache[model], parenttagnum, &fstate, transform))
if (Mod_GetTag(model, parenttagnum, &fstate, transform))
{
old[0] = ent->axis[0][0];
old[1] = ent->axis[1][0];
old[2] = ent->axis[2][0];
old[3] = ent->origin[0];
old[4] = ent->axis[0][1];
old[5] = ent->axis[1][1];
old[6] = ent->axis[2][1];
old[7] = ent->origin[1];
old[8] = ent->axis[0][2];
old[9] = ent->axis[1][2];
old[10] = ent->axis[2][2];
old[11] = ent->origin[2];
// parent -> transform -> old
parent[0] = axis[0][0];
parent[1] = axis[1][0];
parent[2] = axis[2][0];
parent[3] = org[0];
parent[4] = axis[0][1];
parent[5] = axis[1][1];
parent[6] = axis[2][1];
parent[7] = org[1];
parent[8] = axis[0][2];
parent[9] = axis[1][2];
parent[10] = axis[2][2];
parent[11] = org[2];
R_ConcatTransforms((void*)old, (void*)parent, (void*)temp);
R_ConcatTransforms((void*)temp, (void*)transform, (void*)result);
R_ConcatTransforms((void*)parent, (void*)transform, (void*)temp);
R_ConcatTransforms((void*)temp, (void*)old, (void*)result);
ent->axis[0][0] = result[0];
ent->axis[1][0] = result[1];
@ -1823,32 +1839,6 @@ void CL_RotateAroundTag(entity_t *ent, int entnum, int parenttagent, int parentt
}
else //hrm.
{
old[0] = ent->axis[0][0];
old[1] = ent->axis[1][0];
old[2] = ent->axis[2][0];
old[3] = ent->origin[0];
old[4] = ent->axis[0][1];
old[5] = ent->axis[1][1];
old[6] = ent->axis[2][1];
old[7] = ent->origin[1];
old[8] = ent->axis[0][2];
old[9] = ent->axis[1][2];
old[10] = ent->axis[2][2];
old[11] = ent->origin[2];
parent[0] = axis[0][0];
parent[1] = axis[1][0];
parent[2] = axis[2][0];
parent[3] = org[0];
parent[4] = axis[0][1];
parent[5] = axis[1][1];
parent[6] = axis[2][1];
parent[7] = org[1];
parent[8] = axis[0][2];
parent[9] = axis[1][2];
parent[10] = axis[2][2];
parent[11] = org[2];
R_ConcatTransforms((void*)parent, (void*)old, (void*)result);
ent->axis[0][0] = result[0];
@ -1865,6 +1855,9 @@ void CL_RotateAroundTag(entity_t *ent, int entnum, int parenttagent, int parentt
ent->origin[2] = result[11];
}
}
if (ps && ps->tagentity)
CL_RotateAroundTag(ent, entnum, ps->tagentity, ps->tagindex);
}
void V_AddAxisEntity(entity_t *in)
@ -3652,7 +3645,7 @@ void CL_LinkPacketEntities (void)
ent->flags |= RF_ADDITIVE;
if (state->effects & EF_NODEPTHTEST)
ent->flags |= RF_NODEPTHTEST;
if (state->effects & DPEF_NOSHADOW)
if (state->effects & EF_NOSHADOW)
ent->flags |= RF_NOSHADOW;
if (state->trans != 0xff)
ent->flags |= RF_TRANSLUCENT;
@ -4840,7 +4833,22 @@ void CL_LinkViewModel(void)
#ifdef Q2CLIENT
if (cls.protocol == CP_QUAKE2)
{
V_ClearEntity(&ent);
ent.model = pv->vm.oldmodel;
ent.framestate.g[FS_REG].frame[0] = pv->vm.prevframe;
ent.framestate.g[FS_REG].frame[1] = pv->vm.oldframe;
ent.framestate.g[FS_REG].frametime[0] = pv->vm.lerptime;
ent.framestate.g[FS_REG].frametime[1] = pv->vm.oldlerptime;
ent.framestate.g[FS_REG].lerpweight[0] = 1 - cl.lerpfrac;
ent.framestate.g[FS_REG].lerpweight[1] = cl.lerpfrac;
ent.flags |= RF_WEAPONMODEL|RF_DEPTHHACK|RF_NOSHADOW;
V_AddEntity (&ent);
return;
}
#endif
if (!r_drawentities.ival)
@ -4863,7 +4871,7 @@ void CL_LinkViewModel(void)
else
alpha = 1;
if ((r_refdef.playerview->stats[STAT_ITEMS] & IT_INVISIBILITY)
if ((pv->stats[STAT_ITEMS] & IT_INVISIBILITY)
&& r_drawviewmodelinvis.value > 0
&& r_drawviewmodelinvis.value < 1)
alpha *= r_drawviewmodelinvis.value;

View file

@ -52,13 +52,11 @@ usercmd_t independantphysics[MAX_SPLITS];
vec3_t mousemovements[MAX_SPLITS];
/*kinda a hack...*/
static int con_splitmodifier;
int con_splitmodifier;
cvar_t cl_forceseat = CVARAD("in_forceseat", "0", "in_forcesplitclient", "Overrides the device identifiers to control a specific client from any device. This can be used for debugging mods, where you only have one keyboard/mouse.");
extern cvar_t cl_splitscreen;
int CL_TargettedSplit(qboolean nowrap)
{
char *c;
int pnum;
int mod;
//explicitly targetted at some seat number from the server
@ -376,6 +374,8 @@ void IN_WriteButtons(vfsfile_t *f, qboolean all)
VFS_PRINTF(f, "-p%i %s\n", s+1, buttons[b].name);
}
}
//FIXME: save device remappings to config.
}
//is this useful?
@ -465,6 +465,8 @@ void IN_Restart (void)
{
IN_Shutdown();
IN_ReInit();
//FIXME: re-assert explicit device re-mappings
}
/*
@ -855,7 +857,7 @@ void CL_ClampPitch (int pnum)
if (cls.protocol == CP_QUAKE2)
{
float pitch;
pitch = SHORT2ANGLE(cl.q2frame.playerstate.pmove.delta_angles[PITCH]);
pitch = SHORT2ANGLE(cl.q2frame.playerstate[pnum].pmove.delta_angles[PITCH]);
if (pitch > 180)
pitch -= 360;
@ -1387,6 +1389,41 @@ static void QDECL CL_SpareMsec_Callback (struct cvar_s *var, char *oldvalue)
}
}
void CL_UpdateSeats(void)
{
if (!cls.netchan.message.cursize && cl.allocated_client_slots > 1 && cls.state == ca_active && cl.splitclients && (cls.fteprotocolextensions & PEXT_SPLITSCREEN) && cl.worldmodel)
{
int targ = cl_splitscreen.ival+1;
if (targ > MAX_SPLITS)
targ = MAX_SPLITS;
if (cl.splitclients < targ)
{
char buffer[2048];
char newinfo[2048];
Q_strncpyz(newinfo, cls.userinfo[cl.splitclients], sizeof(newinfo));
//some userinfos should always have a value
if (!*Info_ValueForKey(newinfo, "name")) //$name-2
Info_SetValueForKey(newinfo, "name", va("%s-%i\n", Info_ValueForKey(cls.userinfo[0], "name"), cl.splitclients+1), sizeof(newinfo));
if (cls.protocol != CP_QUAKE2)
{
if (!*Info_ValueForKey(newinfo, "team")) //put players on the same team by default. this avoids team damage in coop, and if you're playing on the same computer then you probably want to be on the same team anyway.
Info_SetValueForKey(newinfo, "team", Info_ValueForKey(cls.userinfo[0], "team"), sizeof(newinfo));
if (!*Info_ValueForKey(newinfo, "bottomcolor")) //bottom colour implies team in nq
Info_SetValueForKey(newinfo, "bottomcolor", Info_ValueForKey(cls.userinfo[0], "bottomcolor"), sizeof(newinfo));
if (!*Info_ValueForKey(newinfo, "topcolor")) //should probably pick a random top colour or something
Info_SetValueForKey(newinfo, "topcolor", Info_ValueForKey(cls.userinfo[0], "topcolor"), sizeof(newinfo));
}
if (!*Info_ValueForKey(newinfo, "skin")) //give players the same skin by default, because we can. q2 cares for teams. qw might as well (its not like anyone actually uses them thanks to enemy-skin forcing).
Info_SetValueForKey(newinfo, "skin", Info_ValueForKey(cls.userinfo[0], "skin"), sizeof(newinfo));
CL_SendClientCommand(true, "addseat %i %s", cl.splitclients, COM_QuotedString(newinfo, buffer, sizeof(buffer), false));
}
else if (cl.splitclients > targ)
CL_SendClientCommand(true, "addseat %i", targ);
}
}
/*
=================
@ -1428,76 +1465,91 @@ qboolean CL_WriteDeltas (int plnum, sizebuf_t *buf)
qboolean CLQ2_SendCmd (sizebuf_t *buf)
{
int seq_hash;
qboolean dontdrop;
qboolean dontdrop = false;
usercmd_t *cmd;
int checksumIndex, i;
int lightlev;
int seat;
cl.movesequence = cls.netchan.outgoing_sequence; //make sure its correct even over map changes.
seq_hash = cl.movesequence;
// send this and the previous cmds in the message, so
// if the last packet was dropped, it can be recovered
i = cl.movesequence & UPDATE_MASK;
cmd = &cl.outframes[i].cmd[0];
//q2admin is retarded and kicks you if you get a stall.
if (cmd->msec > 100)
cmd->msec = 100;
if (cls.resendinfo)
for (seat = 0; seat < cl.splitclients; seat++)
{
MSG_WriteByte (&cls.netchan.message, clcq2_userinfo);
MSG_WriteString (&cls.netchan.message, cls.userinfo[0]);
// send this and the previous cmds in the message, so
// if the last packet was dropped, it can be recovered
i = cl.movesequence & UPDATE_MASK;
cmd = &cl.outframes[i].cmd[seat];
cls.resendinfo = false;
}
//q2admin is retarded and kicks you if you get a stall.
if (cmd->msec > 100)
cmd->msec = 100;
MSG_WriteByte (buf, clcq2_move);
if (cls.resendinfo)
{
MSG_WriteByte (&cls.netchan.message, clcq2_userinfo);
MSG_WriteString (&cls.netchan.message, cls.userinfo[seat]);
cls.resendinfo = false;
}
// save the position for a checksum qbyte
if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)
checksumIndex = -1;
else
{
checksumIndex = buf->cursize;
MSG_WriteByte (buf, 0);
}
MSG_WriteByte (buf, clcq2_move);
if (!cl.q2frame.valid || cl_nodelta.ival)
MSG_WriteLong (buf, -1); // no compression
else
MSG_WriteLong (buf, cl.q2frame.serverframe);
if (seat)
{
//multi-seat still has an extra clc_move per seat
//but no checksum (pointless when its opensource anyway)
//no sequence (only seat 0 reports that)
checksumIndex = -1;
}
else
{
// save the position for a checksum qbyte
if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)
checksumIndex = -1;
else
{
checksumIndex = buf->cursize;
MSG_WriteByte (buf, 0);
}
lightlev = R_LightPoint(cl.playerview[0].simorg);
if (!cl.q2frame.valid || cl_nodelta.ival || (cls.demorecording && !cls.demohadkeyframe))
MSG_WriteLong (buf, -1); // no compression
else
MSG_WriteLong (buf, cl.q2frame.serverframe);
}
// msecs = msecs - (double)msecstouse;
lightlev = R_LightPoint(cl.playerview[seat].simorg);
i = cls.netchan.outgoing_sequence & UPDATE_MASK;
cmd = &cl.outframes[i].cmd[0];
*cmd = independantphysics[0];
// msecs = msecs - (double)msecstouse;
cmd->lightlevel = (lightlev>255)?255:lightlev;
i = cls.netchan.outgoing_sequence & UPDATE_MASK;
cmd = &cl.outframes[i].cmd[seat];
*cmd = independantphysics[seat];
cl.outframes[i].senttime = realtime;
cl.outframes[i].latency = -1;
memset(&independantphysics[0], 0, sizeof(independantphysics[0]));
cmd->lightlevel = (lightlev>255)?255:lightlev;
if (cmd->buttons)
cmd->buttons |= 128; //fixme: this isn't really what's meant by the anykey.
cl.outframes[i].senttime = realtime;
cl.outframes[i].latency = -1;
memset(&independantphysics[seat], 0, sizeof(independantphysics[0]));
// calculate a checksum over the move commands
dontdrop = CL_WriteDeltas(0, buf);
if (cmd->buttons)
cmd->buttons |= 128; //fixme: this isn't really what's meant by the anykey.
if (checksumIndex >= 0)
{
buf->data[checksumIndex] = Q2COM_BlockSequenceCRCByte(
buf->data + checksumIndex + 1, buf->cursize - checksumIndex - 1,
seq_hash);
// calculate a checksum over the move commands
dontdrop |= CL_WriteDeltas(seat, buf);
if (checksumIndex >= 0)
{
buf->data[checksumIndex] = Q2COM_BlockSequenceCRCByte(
buf->data + checksumIndex + 1, buf->cursize - checksumIndex - 1,
seq_hash);
}
}
if (cl.sendprespawn || !cls.protocol_q2)
buf->cursize = 0; //tastyspleen.net is alergic.
else
CL_UpdateSeats();
return dontdrop;
}
@ -1608,22 +1660,7 @@ qboolean CLQW_SendCmd (sizebuf_t *buf, qboolean actuallysend)
if (cl.sendprespawn || !actuallysend)
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);
}
}
CL_UpdateSeats();
return dontdrop;
}

View file

@ -35,6 +35,8 @@ void QDECL Name_Callback(struct cvar_s *var, char *oldvalue);
#define Name_Callback NULL
#endif
void CL_ForceStopDownload (qdownload_t *dl, qboolean finish);
// we need to declare some mouse variables here, because the menu system
// references them even when on a unix system.
@ -157,7 +159,7 @@ cvar_t cl_proxyaddr = CVAR("cl_proxyaddr", "");
cvar_t cl_sendguid = CVARD("cl_sendguid", "0", "Send a randomly generated 'globally unique' id to servers, which can be used by servers for score rankings and stuff. Different servers will see different guids. Delete the 'qkey' file in order to appear as a different user.\nIf set to 2, all servers will see the same guid. Be warned that this can show other people the guid that you're using.");
cvar_t cl_downloads = CVARFD("cl_downloads", "1", CVAR_NOTFROMSERVER, "Allows you to block all automatic downloads.");
cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER, "Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages.");
cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER, "Follow download redirection to download packages instead of individual files. 2 allows redirection only to named packages files. Also allows the server to send nearly arbitary download commands.");
cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER, "Follow download redirection to download packages instead of individual files. Also allows the server to send nearly arbitary download commands.\n2: allows redirection only to named packages files (and demos/*.mvd), which is a bit safer.");
cvar_t cl_download_mapsrc = CVARD("cl_download_mapsrc", "", "Specifies an http location prefix for map downloads. EG: \"http://bigfoot.morphos-team.net/misc/quakemaps/\"");
cvar_t cl_download_packages = CVARFD("cl_download_packages", "1", CVAR_NOTFROMSERVER, "0=Do not download packages simply because the server is using them. 1=Download and load packages as needed (does not affect games which do not use this package). 2=Do download and install permanently (use with caution!)");
cvar_t requiredownloads = CVARFD("requiredownloads","1", CVAR_ARCHIVE, "0=join the game before downloads have even finished (might be laggy). 1=wait for all downloads to complete before joining.");
@ -535,15 +537,26 @@ void CL_SendConnectPacket (netadr_t *to, int mtu,
}
#ifdef PROTOCOL_VERSION_FTE
CL_SupportedFTEExtensions(&fteprotextsupported, &fteprotextsupported2);
fteprotextsupported &= ftepext;
fteprotextsupported2 &= ftepext2;
#ifdef Q2CLIENT
if (connectinfo.protocol != CP_QUAKEWORLD)
fteprotextsupported = 0;
if (connectinfo.protocol == CP_QUAKE2)
{
fteprotextsupported = ftepext & (PEXT_MODELDBL|PEXT_SOUNDDBL|PEXT_SPLITSCREEN);
fteprotextsupported2 = 0;
}
else
#endif
{
CL_SupportedFTEExtensions(&fteprotextsupported, &fteprotextsupported2);
fteprotextsupported &= ftepext;
fteprotextsupported2 &= ftepext2;
if (connectinfo.protocol != CP_QUAKEWORLD)
{
fteprotextsupported = 0;
fteprotextsupported2 = 0;
}
}
connectinfo.fteext1 = fteprotextsupported;
connectinfo.fteext2 = fteprotextsupported2;
@ -602,7 +615,7 @@ void CL_SendConnectPacket (netadr_t *to, int mtu,
clients = MAX_SPLITS;
#ifdef Q2CLIENT
if (connectinfo.protocol == CP_QUAKE2) //sorry - too lazy.
if (connectinfo.protocol == CP_QUAKE2) //q2 only supports after-connect seats
clients = 1;
#endif
@ -740,6 +753,8 @@ void CL_CheckForResend (void)
case GT_QUAKE2:
connectinfo.protocol = CP_QUAKE2;
connectinfo.subprotocol = PROTOCOL_VERSION_Q2;
connectinfo.fteext1 = PEXT_MODELDBL|PEXT_SOUNDDBL|PEXT_SPLITSCREEN;
connectinfo.fteext2 = 0;
break;
#endif
default:
@ -838,19 +853,26 @@ void CL_CheckForResend (void)
}
//make sure the protocol within demos is actually correct/sane
if (cls.demorecording == 1 && cls.protocol != CP_QUAKEWORLD)
if (cls.demorecording == DPB_QUAKEWORLD && cls.protocol != CP_QUAKEWORLD)
{
connectinfo.protocol = CP_QUAKEWORLD;
connectinfo.subprotocol = PROTOCOL_VERSION_QW;
connectinfo.fteext1 = Net_PextMask(1, false);
connectinfo.fteext2 = Net_PextMask(2, false);
}
else if (cls.demorecording == 2 && cls.protocol != CP_NETQUAKE)
else if (cls.demorecording == DPB_NETQUAKE && cls.protocol != CP_NETQUAKE)
{
connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_FITZ666;
//FIXME: use pext.
}
else if (cls.demorecording == DPB_QUAKE2 && cls.protocol != CP_NETQUAKE)
{
connectinfo.protocol = CP_QUAKE2;
connectinfo.subprotocol = PROTOCOL_VERSION_Q2;
connectinfo.fteext1 = PEXT_MODELDBL|PEXT_SOUNDDBL|PEXT_SPLITSCREEN;
//FIXME: use pext.
}
break;
}
@ -1485,6 +1507,14 @@ void CL_ClearState (void)
}
}
#ifdef Q2CLIENT
for (i = 0; i < countof(cl.configstring_general); i++)
{
if (cl.configstring_general)
Z_Free(cl.configstring_general[i]);
}
#endif
for (i = 0; i < MAX_SPLITS; i++)
{
for (j = 0; j < MAX_CL_STATS; j++)
@ -1765,7 +1795,7 @@ void CL_Users_f (void)
{
if (cl.players[i].name[0])
{
Con_TPrintf ("%6i %4i %s\n", cl.players[i].userid, cl.players[i].frags, cl.players[i].name);
Con_TPrintf ("%6i %4i ^[%s\\player\\%i^]\n", cl.players[i].userid, cl.players[i].frags, cl.players[i].name, i);
c++;
}
}
@ -1832,7 +1862,7 @@ void CL_Color_f (void)
// else if (server_owns_colour)
// Cvar_LockFromServer(&topcolor, num);
else
Cvar_Set (&topcolor, num);
CL_SetInfo(pnum, "topcolor", num);
Q_snprintfz (num, sizeof(num), (bottom&0xff000000)?"0x%06x":"%i", bottom & 0xffffff);
if (bottom == 0)
*num = '\0';
@ -1841,7 +1871,7 @@ void CL_Color_f (void)
else if (server_owns_colour)
Cvar_LockFromServer(&bottomcolor, num);
else
Cvar_Set (&bottomcolor, num);
CL_SetInfo (pnum, "bottomcolor", num);
#ifdef NQPROT
if (cls.protocol == CP_NETQUAKE)
Cmd_ForwardToServer();
@ -2167,8 +2197,13 @@ void CL_FullInfo_f (void)
while (*s)
{
o = key;
while (*s && *s != '\\')
while (*s && *s != '\\' && o < key + sizeof(key))
*o++ = *s++;
if (o == key + sizeof(key))
{
Con_Printf ("key length too long\n");
return;
}
*o = 0;
if (!*s)
@ -2179,8 +2214,13 @@ void CL_FullInfo_f (void)
o = value;
s++;
while (*s && *s != '\\')
while (*s && *s != '\\' && o < value + sizeof(value))
*o++ = *s++;
if (o == value + sizeof(value))
{
Con_Printf ("value length too long\n");
return;
}
*o = 0;
if (*s)
@ -2209,11 +2249,15 @@ void CL_SetInfo (int pnum, char *key, char *value)
Info_SetValueForStarKey (cls.userinfo[pnum], key, value, sizeof(cls.userinfo[pnum]));
if (cls.state >= ca_connected && !cls.demoplayback)
{
if (pnum >= cl.splitclients)
return;
#ifdef Q2CLIENT
if (cls.protocol == CP_QUAKE2 || cls.protocol == CP_QUAKE3)
if ((cls.protocol == CP_QUAKE2 || cls.protocol == CP_QUAKE3) && !cls.fteprotocolextensions)
cls.resendinfo = true;
else
#endif
if (cls.protocol != CP_NETQUAKE || (cls.fteprotocolextensions & PEXT_BIGUSERINFOS))
{
if (pnum)
CL_SendClientCommand(true, "%i setinfo %s \"%s\"", pnum+1, key, value);
@ -3433,7 +3477,7 @@ qboolean CL_AllowArbitaryDownload(char *localfile)
{
char ext[8];
COM_FileExtension(localfile, ext, sizeof(ext));
if (!strcmp(ext, "pak") || !strcmp(ext, "pk3") || !strcmp(ext, "pk4"))
if (!strcmp(ext, "pak") || !strcmp(ext, "pk3") || !strcmp(ext, "pk4") || (!strncmp(localfile, "demos/", 6) && !strcmp(ext, "mvd")))
return true;
else
{
@ -3508,6 +3552,9 @@ void CL_Download_f (void)
if (Cmd_IsInsecure()) //mark server specified downloads.
{
if (cls.download && cls.download->method == DL_QWPENDING)
DL_Abort(cls.download, QDL_FAILED);
//don't let gamecode order us to download random junk
if (!CL_AllowArbitaryDownload(localname))
return;
@ -3584,8 +3631,13 @@ void CL_ForceStopDownload (qdownload_t *dl, qboolean finish)
if (!dl->file)
{
Con_Printf("No files downloading by QW protocol\n");
return;
if (dl->method == DL_QWPENDING)
finish = false;
else
{
Con_Printf("No files downloading by QW protocol\n");
return;
}
}
if (finish)
@ -4826,7 +4878,10 @@ double Host_Frame (double time)
if (r_blockvidrestart)
{
if (waitingformanifest)
{
COM_MainThreadWork();
return 0.1;
}
Host_FinishLoading();
return 0;
}
@ -4865,6 +4920,10 @@ double Host_Frame (double time)
double idlesec = 1.0 / cl_idlefps.value;
if (idlesec > 0.1)
idlesec = 0.1; // limit to at least 10 fps
#if !defined(NOMEDIA)
if (Media_Capturing())
idlesec = 0;
#endif
if ((realtime - oldrealtime) < idlesec)
{
#ifndef CLIENTONLY
@ -5332,9 +5391,10 @@ void CL_ExecInitialConfigs(char *resetcommand)
Cbuf_AddText("\n", RESTRICT_LOCAL);
COM_ParsePlusSets(true);
def = COM_FDepthFile("default.cfg", true); //q2/q3/tc
#ifdef QUAKETC
Cbuf_AddText ("exec default.cfg\n", RESTRICT_LOCAL);
if (COM_FCheckExists ("config.cfg"))
if (COM_FDepthFile ("config.cfg", true) <= def)
Cbuf_AddText ("exec config.cfg\n", RESTRICT_LOCAL);
if (COM_FCheckExists ("autoexec.cfg"))
Cbuf_AddText ("exec autoexec.cfg\n", RESTRICT_LOCAL);
@ -5342,23 +5402,32 @@ void CL_ExecInitialConfigs(char *resetcommand)
//who should we imitate?
qrc = COM_FDepthFile("quake.rc", true); //q1
hrc = COM_FDepthFile("hexen.rc", true); //h2
def = COM_FDepthFile("default.cfg", true); //q2/q3
if (qrc <= def && qrc <= hrc && qrc!=0x7fffffff)
{
Cbuf_AddText ("exec quake.rc\n", RESTRICT_LOCAL);
def = qrc;
}
else if (hrc <= def && hrc!=0x7fffffff)
{
Cbuf_AddText ("exec hexen.rc\n", RESTRICT_LOCAL);
def = hrc;
}
else
{ //they didn't give us an rc file!
// Cbuf_AddText ("bind ` toggleconsole\n", RESTRICT_LOCAL); //in case default.cfg does not exist. :(
Cbuf_AddText ("exec default.cfg\n", RESTRICT_LOCAL);
if (COM_FCheckExists ("config.cfg"))
if (COM_FDepthFile ("config.cfg", true) <= def)
Cbuf_AddText ("exec config.cfg\n", RESTRICT_LOCAL);
if (COM_FCheckExists ("q3config.cfg"))
if (COM_FDepthFile ("q3config.cfg", true) <= def)
Cbuf_AddText ("exec q3config.cfg\n", RESTRICT_LOCAL);
Cbuf_AddText ("exec autoexec.cfg\n", RESTRICT_LOCAL);
}
Cbuf_AddText ("exec fte.cfg\n", RESTRICT_LOCAL);
qrc = COM_FDepthFile("fte.cfg", true);
if (qrc <= def) //don't use it if we're running a mod with a default.cfg that is in a stronger path than fte.cfg, as this indicates that fte.cfg is from fte/ and not $currentmod/.
Cbuf_AddText ("exec fte.cfg\n", RESTRICT_LOCAL);
else if (qrc != 0x7fffffff)
Cbuf_AddText ("echo skipping fte.cfg from wrong gamedir\n", RESTRICT_LOCAL);
#endif
#ifdef QUAKESPYAPI
if (COM_FCheckExists ("frontend.cfg"))
@ -5380,6 +5449,9 @@ void CL_ExecInitialConfigs(char *resetcommand)
//the configs should be fully loaded.
//so convert the backwards compable commandline parameters in cvar sets.
CL_ArgumentOverrides();
#ifndef CLIENTONLY
SV_ArgumentOverrides();
#endif
//and disable the 'you have unsaved stuff' prompt.
Cvar_Saved();
@ -5403,6 +5475,9 @@ void Host_FinishLoading(void)
Cbuf_Execute ();
CL_ArgumentOverrides();
#ifndef CLIENTONLY
SV_ArgumentOverrides();
#endif
Con_Printf ("\n%s\n", version_string());
@ -5605,7 +5680,7 @@ void Host_Shutdown(void)
Memory_DeInit();
#ifndef CLIENTONLY
memset(&sv, 0, sizeof(sv));
SV_WipeServerState();
memset(&svs, 0, sizeof(svs));
#endif
Sys_Shutdown();

View file

@ -589,10 +589,14 @@ qboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned
Q_strncpyz(dl->rname, filename, sizeof(dl->rname));
Q_strncpyz(dl->localname, localname, sizeof(dl->localname));
dl->next = cl.downloadlist;
cl.downloadlist = dl;
dl->size = 0;
dl->flags = flags | DLLF_SIZEUNKNOWN;
if (!cl.downloadlist)
flags &= ~DLLF_VERBOSE;
cl.downloadlist = dl;
if (!webdl && (cls.fteprotocolextensions & (PEXT_CHUNKEDDOWNLOADS
#ifdef PEXT_PK3DOWNLOADS
| PEXT_PK3DOWNLOADS
@ -1068,7 +1072,6 @@ void Model_CheckDownloads (void)
// Con_TPrintf (TLC_CHECKINGMODELS);
R_SetSky(cl.skyname);
#ifdef Q2CLIENT
if (cls.protocol == CP_QUAKE2)
{
@ -1573,13 +1576,18 @@ void CL_RequestNextDownload (void)
if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING)
COM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING);
#ifdef warningmsg
#pragma warningmsg("timedemo timer should start here")
#endif
if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)
{
Con_Printf("\n\n-------------\nCouldn't download %s - cannot fully connect\n", cl.worldmodel->name);
Con_Printf("\n\n-------------\n" CON_ERROR "Couldn't download \"%s\" - cannot fully connect\n", cl.worldmodel?cl.worldmodel->name:"unknown");
#if !defined(NOMEDIA)
if (cls.demoplayback && Media_Capturing())
{
Con_Printf(CON_ERROR "Aborting capture\n");
CL_StopPlayback();
}
#endif
//else should probably force the demo speed really fast or something
SCR_SetLoadingStage(LS_NONE);
return;
}
@ -3105,6 +3113,9 @@ void CLQ2_ParseServerData (void)
memset(&cls.netchan.netprim, 0, sizeof(cls.netchan.netprim));
cls.netchan.netprim.coordsize = 2;
cls.netchan.netprim.anglesize = 1;
cls.fteprotocolextensions = 0;
cls.fteprotocolextensions2 = 0;
cls.demohadkeyframe = true; //assume that it did, so this stuff all gets recorded.
Con_DPrintf ("Serverdata packet received.\n");
//
@ -3117,6 +3128,25 @@ void CLQ2_ParseServerData (void)
// parse protocol version number
i = MSG_ReadLong ();
if (i == PROTOCOL_VERSION_FTE)
{
cls.fteprotocolextensions = i = MSG_ReadLong();
// if (i & PEXT_FLOATCOORDS)
// i -= PEXT_FLOATCOORDS;
if (i & PEXT_SOUNDDBL)
i -= PEXT_SOUNDDBL;
if (i & PEXT_MODELDBL)
i -= PEXT_MODELDBL;
if (i & PEXT_SPLITSCREEN)
i -= PEXT_SPLITSCREEN;
if (i)
Host_EndGame ("Unsupported q2 protocol extensions: %x", i);
i = MSG_ReadLong ();
if (cls.fteprotocolextensions & PEXT_FLOATCOORDS)
cls.netchan.netprim.coordsize = 4;
}
cls.protocol_q2 = i;
if (i == PROTOCOL_VERSION_R1Q2)
@ -3178,7 +3208,7 @@ void CLQ2_ParseServerData (void)
MSG_ReadByte(); //strafejump hack
if (r1q2ver >= 1905)
cls.netchan.netprim.q2flags |= NPQ2_SIZE32;
cls.netchan.netprim.q2flags |= NPQ2_SOLID32;
}
else if (cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)
{
@ -3189,7 +3219,7 @@ void CLQ2_ParseServerData (void)
MSG_ReadByte(); //strafejump hack
MSG_ReadByte(); //q2pro qw-mode. kinda silly for us tbh.
if (q2prover >= 1014)
cls.netchan.netprim.q2flags |= NPQ2_SIZE32;
cls.netchan.netprim.q2flags |= NPQ2_SOLID32;
if (q2prover >= 1018)
cls.netchan.netprim.q2flags |= NPQ2_ANG16;
if (q2prover >= 1015)
@ -3882,14 +3912,38 @@ void CLQ2_ParseClientinfo(int i, char *s)
void CLQ2_ParseConfigString (void)
{
int i;
unsigned int i;
char *s;
// char olds[MAX_QPATH];
i = MSG_ReadShort ();
i = (unsigned short)MSG_ReadShort ();
s = MSG_ReadString();
if (i >= 0x8000 && i < 0x8000+MAX_PRECACHE_MODELS)
{
Q_strncpyz(cl.model_name[i-0x8000], s, MAX_QPATH);
if (cl.model_name[i-0x8000][0] == '#')
{
if (cl.numq2visibleweapons < Q2MAX_VISIBLE_WEAPONS)
{
cl.q2visibleweapons[cl.numq2visibleweapons] = cl.model_name[i-0x8000]+1;
cl.numq2visibleweapons++;
}
cl.model_precache[i-0x8000] = NULL;
}
else
cl.model_precache[i-0x8000] = Mod_ForName (cl.model_name[i-0x8000], MLV_WARN);
return;
}
else if (i >= 0xc000 && i < 0xc000+MAX_PRECACHE_SOUNDS)
{
Q_strncpyz(cl.sound_name[i-0xc000], s, MAX_QPATH);
cl.sound_precache[i-0xc000] = S_PrecacheSound (s);
return;
}
if (i < 0 || i >= Q2MAX_CONFIGSTRINGS)
Host_EndGame ("configstring > Q2MAX_CONFIGSTRINGS");
s = MSG_ReadString();
// strncpy (olds, cl.configstrings[i], sizeof(olds));
// olds[sizeof(olds) - 1] = 0;
@ -4196,7 +4250,7 @@ void CL_ParseStatic (int version)
ent->flags |= RF_ADDITIVE;
if (es.effects & EF_NODEPTHTEST)
ent->flags |= RF_NODEPTHTEST;
if (es.effects & DPEF_NOSHADOW)
if (es.effects & EF_NOSHADOW)
ent->flags |= RF_NOSHADOW;
if (es.trans != 0xff)
ent->flags |= RF_TRANSLUCENT;
@ -4340,7 +4394,11 @@ void CLQ2_ParseStartSoundPacket(void)
sfx_t *sfx;
flags = MSG_ReadByte ();
sound_num = MSG_ReadByte ();
if ((flags & Q2SND_LARGEIDX) && (cls.fteprotocolextensions & PEXT_SOUNDDBL))
sound_num = MSG_ReadShort();
else
sound_num = MSG_ReadByte ();
if (flags & Q2SND_VOLUME)
volume = MSG_ReadByte () / 255.0;
@ -4374,7 +4432,14 @@ void CLQ2_ParseStartSoundPacket(void)
if (flags & Q2SND_POS)
{ // positioned in space
MSG_ReadPos (pos_v);
if ((flags & Q2SND_LARGEPOS) && (cls.fteprotocolextensions & PEXT_FLOATCOORDS))
{
pos_v[0] = MSG_ReadFloat();
pos_v[1] = MSG_ReadFloat();
pos_v[2] = MSG_ReadFloat();
}
else
MSG_ReadPos (pos_v);
pos = pos_v;
}
@ -4426,6 +4491,9 @@ void CLNQ_ParseStartSoundPacket(void)
field_mask = MSG_ReadByte();
if (field_mask & FTESND_MOREFLAGS)
field_mask |= MSG_ReadByte()<<8;
if (field_mask & NQSND_VOLUME)
volume = MSG_ReadByte ();
else
@ -4446,10 +4514,8 @@ void CLNQ_ParseStartSoundPacket(void)
else
timeofs = 0;
// if (field_mask & FTESND_FLAGS)
// flags = MSG_ReadByte();
// else
flags = 0;
flags = field_mask>>8;
flags &= CF_FORCELOOP;
if (field_mask & DPSND_LARGEENTITY)
{
@ -4469,7 +4535,7 @@ void CLNQ_ParseStartSoundPacket(void)
if (field_mask & DPSND_LARGESOUND)
sound_num = (unsigned short)MSG_ReadShort();
else
sound_num = MSG_ReadByte ();
sound_num = (unsigned char)MSG_ReadByte ();
if (ent > MAX_EDICTS)
Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent);
@ -5262,11 +5328,11 @@ void CLQ2_ParseMuzzleFlash2 (void)
CLQ2_RunMuzzleFlash2(ent, flash_number);
}
void CLQ2_ParseInventory (void)
void CLQ2_ParseInventory (int seat)
{
unsigned int i;
for (i=0 ; i<Q2MAX_ITEMS ; i++)
cl.inventory[i] = MSG_ReadShort ();
cl.inventory[seat][i] = MSG_ReadShort ();
}
#endif
@ -6719,12 +6785,15 @@ void CLQW_ParseServerMessage (void)
}
#ifdef Q2CLIENT
void CL_WriteDemoMessage (sizebuf_t *msg, int payloadoffset);
void CLQ2_ParseServerMessage (void)
{
int cmd;
char *s;
int i;
// int j;
int cmd;
char *s;
int i;
unsigned int seat;
// int j;
int startpos = msg_readcount;
received_framecount = host_framecount;
cl.last_servermessage = realtime;
@ -6754,6 +6823,16 @@ void CLQ2_ParseServerMessage (void)
cmd = MSG_ReadByte ();
seat = 0;
if (cmd == svcq2_playerinfo && (cls.fteprotocolextensions & PEXT_SPLITSCREEN))
{
SHOWNET(va("%i", cmd));
seat = MSG_ReadByte ();
if (seat >= MAX_SPLITS)
Host_EndGame ("CLQ2_ParseServerMessage: Invalid seat", cmd);
cmd = MSG_ReadByte ();
}
if (cmd == -1)
{
msg_readcount++; // so the EOM showner has the right value
@ -6795,13 +6874,13 @@ void CLQ2_ParseServerMessage (void)
break;
case svcq2_layout:
s = MSG_ReadString ();
Q_strncpyz (cl.q2layout, s, sizeof(cl.q2layout));
Q_strncpyz (cl.q2layout[seat], s, sizeof(cl.q2layout[seat]));
#ifdef VM_UI
UI_Q2LayoutChanged();
#endif
break;
case svcq2_inventory:
CLQ2_ParseInventory();
CLQ2_ParseInventory(seat);
break;
// the rest are private to the client and server
@ -6858,9 +6937,9 @@ void CLQ2_ParseServerMessage (void)
s = MSG_ReadString();
#ifdef PLUGINS
if (Plug_CenterPrintMessage(s, 0))
if (Plug_CenterPrintMessage(s, seat))
#endif
SCR_CenterPrint (0, s, false);
SCR_CenterPrint (seat, s, false);
break;
case svcq2_download: //16 // [short] size [size bytes]
CL_ParseDownload();
@ -6880,6 +6959,9 @@ void CLQ2_ParseServerMessage (void)
}
}
CL_SetSolidEntities ();
if (cls.demohadkeyframe)
CL_WriteDemoMessage(&net_message, startpos); //FIXME: incomplete frames might be awkward
}
#endif

View file

@ -47,7 +47,7 @@ void VARGS Q2_Pmove (q2pmove_t *pmove);
#define Q2PMF_NO_PREDICTION 64 // temporarily disables prediction (used for grappling hook)
#endif
vec3_t cl_predicted_origins[UPDATE_BACKUP];
vec3_t cl_predicted_origins[MAX_SPLITS][UPDATE_BACKUP];
/*
@ -62,34 +62,43 @@ void CLQ2_CheckPredictionError (void)
int delta[3];
int i;
int len;
int seat;
q2player_state_t *ps;
playerview_t *pv;
if (cl_nopred.value || (cl.q2frame.playerstate.pmove.pm_flags & Q2PMF_NO_PREDICTION))
return;
// calculate the last usercmd_t we sent that the server has processed
frame = cls.netchan.incoming_acknowledged;
frame &= (UPDATE_MASK);
// compare what the server returned with what we had predicted it to be
VectorSubtract (cl.q2frame.playerstate.pmove.origin, cl_predicted_origins[frame], delta);
// save the prediction error for interpolation
len = abs(delta[0]) + abs(delta[1]) + abs(delta[2]);
if (len > 640) // 80 world units
{ // a teleport or something
VectorClear (cl.prediction_error);
}
else
for (seat = 0; seat < cl.splitclients; seat++)
{
// if (/*cl_showmiss->value && */(delta[0] || delta[1] || delta[2]) )
// Con_Printf ("prediction miss on %i: %i\n", cl.q2frame.serverframe,
// delta[0] + delta[1] + delta[2]);
ps = &cl.q2frame.playerstate[seat];
pv = &cl.playerview[seat];
VectorCopy (cl.q2frame.playerstate.pmove.origin, cl_predicted_origins[frame]);
if (cl_nopred.value || (ps->pmove.pm_flags & Q2PMF_NO_PREDICTION))
continue;
// save for error itnerpolation
for (i=0 ; i<3 ; i++)
cl.prediction_error[i] = delta[i]*0.125;
// calculate the last usercmd_t we sent that the server has processed
frame = cls.netchan.incoming_acknowledged;
frame &= (UPDATE_MASK);
// compare what the server returned with what we had predicted it to be
VectorSubtract (ps->pmove.origin, cl_predicted_origins[seat][frame], delta);
// save the prediction error for interpolation
len = abs(delta[0]) + abs(delta[1]) + abs(delta[2]);
if (len > 640) // 80 world units
{ // a teleport or something
VectorClear (pv->prediction_error);
}
else
{
// if (/*cl_showmiss->value && */(delta[0] || delta[1] || delta[2]) )
// Con_Printf ("prediction miss on %i: %i\n", cl.q2frame.serverframe,
// delta[0] + delta[1] + delta[2]);
VectorCopy (ps->pmove.origin, cl_predicted_origins[seat][frame]);
// save for error itnerpolation
for (i=0 ; i<3 ; i++)
pv->prediction_error[i] = delta[i]*0.125;
}
}
}
@ -100,6 +109,7 @@ CL_ClipMoveToEntities
====================
*/
int predignoreentitynum;
void CLQ2_ClipMoveToEntities ( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, trace_t *tr )
{
int i, x, zd, zu;
@ -118,7 +128,7 @@ void CLQ2_ClipMoveToEntities ( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t en
if (!ent->solid)
continue;
if (ent->number == cl.playerview[0].playernum+1)
if (ent->number == predignoreentitynum)
continue;
if (ent->solid == ES_SOLID_BSP)
@ -130,7 +140,7 @@ void CLQ2_ClipMoveToEntities ( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t en
}
else
{ // encoded bbox
if (cls.netchan.netprim.q2flags & NPQ2_SIZE32)
if (cls.netchan.netprim.q2flags & NPQ2_SOLID32)
{
x = ent->solid & 255;
zd = (ent->solid >> 8) & 255;
@ -232,7 +242,7 @@ CL_PredictMovement
Sets cl.predicted_origin and cl.predicted_angles
=================
*/
void CLQ2_PredictMovement (void) //q2 doesn't support split clients.
static void CLQ2_PredictMovement (int seat) //q2 doesn't support split clients.
{
#ifdef Q2BSPS
int ack, current;
@ -244,7 +254,8 @@ void CLQ2_PredictMovement (void) //q2 doesn't support split clients.
int oldz;
#endif
int i;
int pnum = 0;
q2player_state_t *ps = &cl.q2frame.playerstate[seat];
playerview_t *pv = &cl.playerview[seat];
if (cls.state != ca_active)
return;
@ -253,12 +264,12 @@ void CLQ2_PredictMovement (void) //q2 doesn't support split clients.
// return;
#ifdef Q2BSPS
if (cl_nopred.value || cls.demoplayback || (cl.q2frame.playerstate.pmove.pm_flags & Q2PMF_NO_PREDICTION))
if (cl_nopred.value || cls.demoplayback || (ps->pmove.pm_flags & Q2PMF_NO_PREDICTION))
#endif
{ // just set angles
for (i=0 ; i<3 ; i++)
{
cl.predicted_angles[i] = cl.playerview[pnum].viewangles[i] + SHORT2ANGLE(cl.q2frame.playerstate.pmove.delta_angles[i]);
pv->predicted_angles[i] = pv->viewangles[i] + SHORT2ANGLE(ps->pmove.delta_angles[i]);
}
return;
}
@ -281,53 +292,55 @@ void CLQ2_PredictMovement (void) //q2 doesn't support split clients.
pm_airaccelerate = atof(Get_Q2ConfigString(Q2CS_AIRACCEL));
pm.s = cl.q2frame.playerstate.pmove;
pm.s = ps->pmove;
// SCR_DebugGraph (current - ack - 1, 0);
frame = 0;
predignoreentitynum = cl.q2frame.clientnum[seat]+1;//cl.playerview[seat].playernum+1;
// run frames
while (++ack < current)
{
frame = ack & (UPDATE_MASK);
cmd = (q2usercmd_t*)&cl.outframes[frame].cmd[0];
cmd->msec = cl.outframes[frame].cmd[0].msec;
cmd = (q2usercmd_t*)&cl.outframes[frame].cmd[seat];
cmd->msec = cl.outframes[frame].cmd[seat].msec;
pm.cmd = *cmd;
Q2_Pmove (&pm);
// save for debug checking
VectorCopy (pm.s.origin, cl_predicted_origins[frame]);
VectorCopy (pm.s.origin, cl_predicted_origins[seat][frame]);
}
if (independantphysics[0].msec)
if (independantphysics[seat].msec)
{
cmd = (q2usercmd_t*)&independantphysics[0];
cmd->msec = independantphysics[0].msec;
cmd = (q2usercmd_t*)&independantphysics[seat];
cmd->msec = independantphysics[seat].msec;
pm.cmd = *cmd;
Q2_Pmove (&pm);
}
oldframe = (ack-1) & (UPDATE_MASK);
oldz = cl_predicted_origins[oldframe][2];
oldz = cl_predicted_origins[seat][oldframe][2];
step = pm.s.origin[2] - oldz;
if (step > 63 && step < 160 && (pm.s.pm_flags & Q2PMF_ON_GROUND) )
{
cl.predicted_step = step * 0.125;
cl.predicted_step_time = realtime;// - host_frametime;// * 0.5;
pv->predicted_step = step * 0.125;
pv->predicted_step_time = realtime;// - host_frametime;// * 0.5;
}
cl.playerview[0].onground = !!(pm.s.pm_flags & Q2PMF_ON_GROUND);
pv->onground = !!(pm.s.pm_flags & Q2PMF_ON_GROUND);
// copy results out for rendering
cl.predicted_origin[0] = pm.s.origin[0]*0.125;
cl.predicted_origin[1] = pm.s.origin[1]*0.125;
cl.predicted_origin[2] = pm.s.origin[2]*0.125;
pv->predicted_origin[0] = pm.s.origin[0]*0.125;
pv->predicted_origin[1] = pm.s.origin[1]*0.125;
pv->predicted_origin[2] = pm.s.origin[2]*0.125;
VectorCopy (pm.viewangles, cl.predicted_angles);
VectorCopy (pm.viewangles, pv->predicted_angles);
#endif
}
@ -927,7 +940,7 @@ void CL_PredictMovePNum (int seat)
if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)
return;
pv->crouch = 0;
CLQ2_PredictMovement();
CLQ2_PredictMovement(seat);
return;
}
#endif

View file

@ -184,19 +184,20 @@ float scr_conlines; // lines of console to display
qboolean scr_con_forcedraw;
extern cvar_t scr_viewsize;
extern cvar_t scr_fov;
extern cvar_t scr_conspeed;
extern cvar_t scr_centertime;
extern cvar_t scr_showturtle;
extern cvar_t scr_viewsize;
extern cvar_t scr_fov;
extern cvar_t scr_conspeed;
extern cvar_t scr_centertime;
extern cvar_t scr_logcenterprint;
extern cvar_t scr_showturtle;
extern cvar_t scr_turtlefps;
extern cvar_t scr_showpause;
extern cvar_t scr_printspeed;
extern cvar_t scr_showpause;
extern cvar_t scr_printspeed;
extern cvar_t scr_allowsnap;
extern cvar_t scr_sshot_type;
extern cvar_t scr_sshot_prefix;
extern cvar_t scr_sshot_compression;
extern cvar_t crosshair;
extern cvar_t crosshair;
extern cvar_t scr_consize;
cvar_t scr_neticontimeout = CVAR("scr_neticontimeout", "0.3");
@ -470,6 +471,17 @@ void SCR_CenterPrint (int pnum, char *str, qboolean skipgamecode)
break;
str += 2;
}
if (((scr_logcenterprint.ival && !cl.deathmatch) || scr_logcenterprint.ival == 2) && !(p->flags & CPRINT_PERSIST))
{
//don't spam too much.
if (*str && strncmp(cl.lastcenterprint, str, sizeof(cl.lastcenterprint)-1))
{
Q_strncpyz(cl.lastcenterprint, str, sizeof(cl.lastcenterprint));
Con_CenterPrint(str);
}
}
p->charcount = COM_ParseFunString(CON_WHITEMASK, str, p->string, sizeof(p->string), false) - p->string;
p->time_off = scr_centertime.value;
p->time_start = cl.time;
@ -1384,13 +1396,13 @@ void SCR_DrawTurtle (void)
void SCR_DrawDisk (void)
{
if (!draw_disc)
// if (!draw_disc)
return;
if (!COM_HasWork())
return;
// if (!COM_HasWork())
// return;
R2D_ScalePic (scr_vrect.x + vid.width-24, scr_vrect.y, 24, 24, draw_disc);
// R2D_ScalePic (scr_vrect.x + vid.width-24, scr_vrect.y, 24, 24, draw_disc);
}
/*
@ -1883,7 +1895,7 @@ void SCR_EndLoadingPlaque (void)
scr_drawloading = false;
}
void SCR_ImageName (char *mapname)
void SCR_ImageName (const char *mapname)
{
strcpy(levelshotname, "levelshots/");
COM_FileBase(mapname, levelshotname + strlen(levelshotname), sizeof(levelshotname)-strlen(levelshotname));
@ -2392,6 +2404,11 @@ void SCR_ScreenShot_Mega_f(void)
#ifdef CSQC_DAT
if (!okay && CSQC_DrawView())
okay = true;
// if (!*r_refdef.rt_destcolour[0].texname)
{ //csqc protects its own. lazily.
Q_strncpyz(r_refdef.rt_destcolour[0].texname, "megascreeny", sizeof(r_refdef.rt_destcolour[0].texname));
BE_RenderToTextureUpdate2d(true);
}
#endif
if (!okay && r_worldentity.model)
{
@ -2607,12 +2624,13 @@ void SCR_BringDownConsole (void)
int pnum;
for (pnum = 0; pnum < cl.splitclients; pnum++)
{
scr_centerprint[pnum].charcount = 0;
cl.playerview[pnum].cshifts[CSHIFT_CONTENTS].percent = 0; // no area contents palette on next frame
}
for (i=0 ; i<20 && scr_conlines != scr_con_current ; i++)
SCR_UpdateScreen ();
cl.cshifts[CSHIFT_CONTENTS].percent = 0; // no area contents palette on next frame
// for (i=0 ; i<20 && scr_conlines != scr_con_current ; i++)
// SCR_UpdateScreen ();
}
void SCR_TileClear (int skipbottom)
@ -2683,8 +2701,6 @@ void SCR_DrawTwoDimensional(int uimenu, qboolean nohud)
}
else
{
R2D_DrawCrosshair();
SCR_DrawNet ();
SCR_DrawDisk();
SCR_DrawFPS ();

View file

@ -66,7 +66,7 @@ static const char *q2efnames[] =
"TEQ2_FORCEWALL",
NULL,//"TEQ2_HEATBEAM",
NULL,//"TEQ2_MONSTER_HEATBEAM",
"TEQ2_STEAM",
NULL,//"TEQ2_STEAM",
"TEQ2_BUBBLETRAIL2",
"TEQ2_MOREBLOOD",
"TEQ2_HEATBEAM_SPARKS",
@ -76,8 +76,8 @@ static const char *q2efnames[] =
"TEQ2_TRACKER_EXPLOSION",
"TEQ2_TELEPORT_EFFECT",
"TEQ2_DBALL_GOAL",
"TEQ2_WIDOWBEAMOUT",
"TEQ2_NUKEBLAST",
NULL,//"TEQ2_WIDOWBEAMOUT",
NULL,//"TEQ2_NUKEBLAST",
"TEQ2_WIDOWSPLASH",
"TEQ2_EXPLOSION1_BIG",
"TEQ2_EXPLOSION1_NP",
@ -2408,6 +2408,27 @@ void CL_Laser (vec3_t start, vec3_t end, int colors)
ex->framerate = 100; // smoother fading
}
void CLQ2_ParseSteam(void)
{
vec3_t pos, dir;
qbyte colour;
short magnitude;
unsigned int duration;
signed int id = MSG_ReadShort();
qbyte count = MSG_ReadByte();
MSG_ReadPos(pos);
MSG_ReadPos(dir);
colour = MSG_ReadByte();
magnitude = MSG_ReadShort();
if (id == -1)
duration = MSG_ReadLong();
else
duration = 0;
Con_Printf("FIXME: CLQ2_ParseSteam: stub\n");
}
static struct{
qbyte colour;
char *name;
@ -2579,12 +2600,12 @@ void CLQ2_ParseTEnt (void)
case Q2TE_FORCEWALL:
break;
case Q2TE_STEAM:
break;
case Q2TE_WIDOWBEAMOUT:
break;
*/
case Q2TE_STEAM:
CLQ2_ParseSteam();
break;
default:
@ -3471,9 +3492,11 @@ void CL_UpdateBeams (void)
// vieworg = pl->origin;
// }
// else
#ifdef Q2CLIENT
if (cls.protocol == CP_QUAKE2)
vieworg = cl.predicted_origin;
vieworg = pv->predicted_origin;
else
#endif
vieworg = pv->simorg;
if (cl_truelightning.ival > 1 && cl.movesequence > cl_truelightning.ival)
@ -3557,6 +3580,8 @@ void CL_UpdateBeams (void)
VectorCopy (b->start, org);
}
else
VectorCopy (b->start, org);
VectorAdd(org, b->offset, org);
// calculate pitch and yaw
@ -3663,14 +3688,17 @@ void CL_UpdateExplosions (void)
continue;
lastrunningexplosion = i;
if (ex->model->loadstate == MLS_LOADING)
continue;
if (ex->model->loadstate != MLS_LOADED)
if (ex->model)
{
ex->model = NULL;
ex->flags = 0;
P_DelinkTrailstate(&(ex->trailstate));
continue;
if (ex->model->loadstate == MLS_LOADING)
continue;
if (ex->model->loadstate != MLS_LOADED)
{
ex->model = NULL;
ex->flags = 0;
P_DelinkTrailstate(&(ex->trailstate));
continue;
}
}
f = ex->framerate*(cl.time - ex->start);

View file

@ -113,7 +113,7 @@ typedef struct
} q2pmove_state_t;
typedef struct
{ //shared with q2 dll
{ //shared with q2 dll so cannot be changed
q2pmove_state_t pmove; // for prediction
@ -251,7 +251,8 @@ typedef struct
int servertime; // server time the message is valid for (in msec)
int deltaframe;
qbyte areabits[MAX_Q2MAP_AREAS/8]; // portalarea visibility bits
q2player_state_t playerstate;
q2player_state_t playerstate[MAX_SPLITS];
int clientnum[MAX_SPLITS];
int num_entities;
int parse_entities; // non-masked index into cl_parse_entities array
} q2frame_t;
@ -480,7 +481,6 @@ typedef struct
// demo recording info must be here, because record is started before
// entering a map (and clearing client_state_t)
int demorecording; //1=QW, 2=NQ
vfsfile_t *demooutfile;
enum{DPB_NONE,DPB_QUAKEWORLD,DPB_MVD,DPB_EZTV,
@ -490,7 +490,8 @@ typedef struct
#ifdef Q2CLIENT
DPB_QUAKE2
#endif
} demoplayback;
} demoplayback, demorecording;
qboolean demohadkeyframe; //q2 needs to wait for a packet with a key frame, supposedly.
qboolean demoseeking;
float demoseektime;
qboolean timedemo;
@ -638,6 +639,14 @@ struct playerview_s
float viewheight;
int waterlevel; //for smartjump
#ifdef Q2CLIENT
vec3_t predicted_origin;
vec3_t predicted_angles;
vec3_t prediction_error;
float predicted_step_time;
float predicted_step;
#endif
float punchangle; // temporary view kick from weapon firing
float v_dmg_time; //various view knockbacks.
@ -663,7 +672,10 @@ struct playerview_s
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; //
} cam_state;
cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups and content types
vec4_t screentint;
vec3_t vw_axis[3]; //weapons should be positioned relative to this
vec3_t vw_origin; //weapons should be positioned relative to this
@ -743,8 +755,6 @@ typedef struct
int lerpentssequence;
lerpents_t lerpplayers[MAX_CLIENTS];
cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups and content types
//when running splitscreen, we have multiple viewports all active at once
int splitclients; //we are running this many clients split screen.
playerview_t playerview[MAX_SPLITS];
@ -798,7 +808,7 @@ typedef struct
char *configstring_general[Q2MAX_CLIENTS|Q2MAX_GENERAL];
char *image_name[Q2MAX_IMAGES];
char *item_name[Q2MAX_ITEMS];
short inventory[Q2MAX_ITEMS];
short inventory[MAX_SPLITS][Q2MAX_ITEMS];
#endif
struct model_s *model_precache_vwep[MAX_VWEP_MODELS];
@ -841,15 +851,15 @@ typedef struct
qboolean gamedirchanged;
#ifdef Q2CLIENT
char q2statusbar[1024];
char q2layout[1024];
char q2layout[MAX_SPLITS][1024];
int parse_entities;
float lerpfrac;
vec3_t predicted_origin;
vec3_t predicted_angles;
vec3_t prediction_error;
float predicted_step_time;
float predicted_step;
#endif
char lastcenterprint[1024]; //prevents too much spam with console centerprint logging.
struct itemtimer_s
@ -1139,6 +1149,7 @@ qboolean CL_GetMessage (void);
void CL_WriteDemoCmd (usercmd_t *pcmd);
void CL_Demo_ClientCommand(char *commandtext); //for QTV.
void CL_WriteRecordQ2DemoMessage(sizebuf_t *msg);
void CL_Stop_f (void);
void CL_Record_f (void);
void CL_ReRecord_f (void);
@ -1351,7 +1362,7 @@ void CSQC_CvarChanged(cvar_t *var);
//
void CL_InitPrediction (void);
void CL_PredictMove (void);
void CL_PredictUsercmd (int pnum, int entnum, player_state_t *from, player_state_t *to, usercmd_t *u);
void CL_PredictUsercmd (int seat, int entnum, player_state_t *from, player_state_t *to, usercmd_t *u);
#ifdef Q2CLIENT
void CLQ2_CheckPredictionError (void);
#endif
@ -1470,6 +1481,7 @@ extern qboolean care_f_modified;
//random files (fixme: clean up)
#ifdef Q2CLIENT
unsigned int CLQ2_GatherSounds(vec3_t *positions, unsigned int *entnums, sfx_t **sounds, unsigned int max);
void CLQ2_ParseTEnt (void);
void CLQ2_AddEntities (void);
void CLQ2_ParseBaseline (void);
@ -1477,6 +1489,7 @@ void CLQ2_ClearParticleState(void);
void CLQ2_ParseFrame (int extrabits);
void CLQ2_RunMuzzleFlash2 (int ent, int flash_number);
int CLQ2_RegisterTEntModels (void);
void CLQ2_WriteDemoBaselines(sizebuf_t *buf);
#endif
#ifdef HLCLIENT

View file

@ -210,6 +210,45 @@ entity_state_t clq2_parse_entities[MAX_PARSE_ENTITIES];
void CL_SmokeAndFlash(vec3_t origin);
void CLQ2_WriteDemoBaselines(sizebuf_t *buf)
{
int i;
q2entity_state_t nullstate = {0};
for (i = 0; i < MAX_Q2EDICTS; i++)
{
q2entity_state_t es;
entity_state_t *base = &cl_entities[i].baseline;
if (!base->modelindex)
continue;
//I brought these copies on myself...
es.number = i;
VectorCopy(base->origin, es.origin);
VectorCopy(base->angles, es.angles);
VectorCopy(base->u.q2.old_origin, es.old_origin);
es.modelindex = base->modelindex;
es.modelindex2 = base->modelindex2;
es.modelindex3 = base->u.q2.modelindex3;
es.modelindex4 = base->u.q2.modelindex4;
es.frame = base->frame;
es.skinnum = base->skinnum;
es.effects = base->effects;
es.renderfx = base->u.q2.renderfx;
es.solid = base->solid;
es.sound = base->u.q2.sound;
es.event = base->u.q2.event;
if (buf->cursize > buf->maxsize/2)
{
CL_WriteRecordQ2DemoMessage (buf);
SZ_Clear (buf);
}
MSG_WriteByte (buf, svcq2_spawnbaseline);
MSGQ2_WriteDeltaEntity(&nullstate, &es, buf, true, true);
}
}
void CLQ2_ClearState(void)
{
memset(cl_entities, 0, sizeof(cl_entities));
@ -668,13 +707,33 @@ void CLQ2_ParseDelta (entity_state_t *from, entity_state_t *to, int number, int
to->number = number;
if (bits & Q2U_MODEL)
to->modelindex = MSG_ReadByte ();
{
if (bits & Q2UX_INDEX16)
to->modelindex = MSG_ReadShort();
else
to->modelindex = MSG_ReadByte ();
}
if (bits & Q2U_MODEL2)
to->modelindex2 = MSG_ReadByte ();
{
if (bits & Q2UX_INDEX16)
to->modelindex2 = MSG_ReadShort();
else
to->modelindex2 = MSG_ReadByte ();
}
if (bits & Q2U_MODEL3)
to->u.q2.modelindex3 = MSG_ReadByte ();
{
if (bits & Q2UX_INDEX16)
to->u.q2.modelindex3 = MSG_ReadShort();
else
to->u.q2.modelindex3 = MSG_ReadByte ();
}
if (bits & Q2U_MODEL4)
to->u.q2.modelindex4 = MSG_ReadByte ();
{
if (bits & Q2UX_INDEX16)
to->u.q2.modelindex4 = MSG_ReadShort();
else
to->u.q2.modelindex4 = MSG_ReadByte ();
}
if (bits & Q2U_FRAME8)
to->frame = MSG_ReadByte ();
@ -732,7 +791,12 @@ void CLQ2_ParseDelta (entity_state_t *from, entity_state_t *to, int number, int
MSG_ReadPos (to->u.q2.old_origin);
if (bits & Q2U_SOUND)
to->u.q2.sound = MSG_ReadByte ();
{
if (bits & Q2UX_INDEX16)
to->u.q2.sound = MSG_ReadShort();
else
to->u.q2.sound = MSG_ReadByte ();
}
if (bits & Q2U_EVENT)
to->u.q2.event = MSG_ReadByte ();
@ -741,7 +805,7 @@ void CLQ2_ParseDelta (entity_state_t *from, entity_state_t *to, int number, int
if (bits & Q2U_SOLID)
{
if (net_message.prim.q2flags & NPQ2_SIZE32)
if (net_message.prim.q2flags & NPQ2_SOLID32)
to->solid = MSG_ReadLong();
else
to->solid = MSG_ReadShort ();
@ -973,22 +1037,30 @@ void CLQ2_ParseBaseline (void)
CL_ParsePlayerstate
===================
*/
void CLQ2_ParsePlayerstate (q2frame_t *oldframe, q2frame_t *newframe, int extflags)
void CLQ2_ParsePlayerstate (int seat, q2frame_t *oldframe, q2frame_t *newframe, int extflags)
{
int flags;
q2player_state_t *state;
int i;
int statbits;
state = &newframe->playerstate;
state = &newframe->playerstate[seat];
// clear to old value before delta parsing
if (oldframe)
*state = oldframe->playerstate;
{
*state = oldframe->playerstate[seat];
newframe->clientnum[seat] = oldframe->clientnum[seat];
}
else
{
memset (state, 0, sizeof(*state));
newframe->clientnum[seat] = cl.playerview[seat].playernum;
}
flags = MSG_ReadShort ();
flags = (unsigned short)MSG_ReadShort ();
if (flags & Q2PS_EXTRABITS)
flags |= MSG_ReadByte()<<16;
//
// parse the pmove_state_t
@ -1064,12 +1136,18 @@ void CLQ2_ParsePlayerstate (q2frame_t *oldframe, q2frame_t *newframe, int extfla
if (flags & Q2PS_WEAPONINDEX)
{
state->gunindex = MSG_ReadByte ();
if (flags & Q2PS_INDEX16)
state->gunindex = MSG_ReadShort ();
else
state->gunindex = MSG_ReadByte ();
}
if (flags & Q2PS_WEAPONFRAME)
{
state->gunframe = MSG_ReadByte ();
if (flags & Q2PS_INDEX16)
state->gunframe = MSG_ReadShort ();
else
state->gunframe = MSG_ReadByte ();
if (extflags & Q2PSX_OLD)
{
state->gunoffset[0] = MSG_ReadChar ()*0.25;
@ -1119,8 +1197,8 @@ void CLQ2_ParsePlayerstate (q2frame_t *oldframe, q2frame_t *newframe, int extfla
state->stats[i] = MSG_ReadShort();
}
if (extflags & Q2PSX_CLIENTNUM)
/*state->viewent =*/ MSG_ReadByte();
if ((extflags & Q2PSX_CLIENTNUM) || (flags & Q2PS_CLIENTNUM))
newframe->clientnum[seat] = MSG_ReadByte();
}
@ -1213,7 +1291,7 @@ void CLQ2_ParseFrame (int extrabits)
{
cl.q2frame.valid = true; // uncompressed frame
old = NULL;
// cls.demowaiting = false; // we can start recording now
cls.demohadkeyframe = true; //yay! all is right with the world!
}
else
{
@ -1247,26 +1325,35 @@ void CLQ2_ParseFrame (int extrabits)
len = MSG_ReadByte ();
MSG_ReadData (&cl.q2frame.areabits, len);
// read playerinfo
// normally playerstate then packet entities
//in splitscreen we may have multiple player states, one per player.
if (cls.protocol_q2 != PROTOCOL_VERSION_R1Q2 && cls.protocol_q2 != PROTOCOL_VERSION_Q2PRO)
{
cmd = MSG_ReadByte ();
// SHOWNET(svc_strings[cmd]);
if (cmd != svcq2_playerinfo)
Host_EndGame ("CL_ParseFrame: not playerinfo");
}
CLQ2_ParsePlayerstate (old, &cl.q2frame, extrabits);
// read packet entities
if (cls.protocol_q2 != PROTOCOL_VERSION_R1Q2 && cls.protocol_q2 != PROTOCOL_VERSION_Q2PRO)
{
cmd = MSG_ReadByte ();
// SHOWNET(svc_strings[cmd]);
cl.splitclients = 0;
for (cl.splitclients = 0; ; )
{
cmd = MSG_ReadByte ();
// SHOWNET(svc_strings[cmd]);
if (cmd == svcq2_playerinfo && cl.splitclients < MAX_SPLITS)
CLQ2_ParsePlayerstate (cl.splitclients++, old, &cl.q2frame, extrabits);
else
break;
}
if (!cl.splitclients)
Host_EndGame ("CL_ParseFrame: no playerinfo");
if (cmd != svcq2_packetentities)
Host_EndGame ("CL_ParseFrame: not packetentities");
}
else
{
cl.splitclients = 1;
CLQ2_ParsePlayerstate (0, old, &cl.q2frame, extrabits);
}
CLQ2_ParsePacketEntities (old, &cl.q2frame);
for (cmd = 0; cmd < MAX_SPLITS; cmd++)
cl.playerview[cmd].viewentity = cl.q2frame.clientnum[cmd]+1;
// save the frame off in the backup array for later delta comparisons
cl.q2frames[cl.q2frame.serverframe & Q2UPDATE_MASK] = cl.q2frame;
@ -1279,10 +1366,10 @@ void CLQ2_ParseFrame (int extrabits)
CL_MakeActive("Quake2");
// cl.force_refdef = true;
cl.predicted_origin[0] = cl.q2frame.playerstate.pmove.origin[0]*0.125;
cl.predicted_origin[1] = cl.q2frame.playerstate.pmove.origin[1]*0.125;
cl.predicted_origin[2] = cl.q2frame.playerstate.pmove.origin[2]*0.125;
VectorCopy (cl.q2frame.playerstate.viewangles, cl.predicted_angles);
// cl.predicted_origin[0] = cl.q2frame.playerstate[0].pmove.origin[0]*0.125;
// cl.predicted_origin[1] = cl.q2frame.playerstate[0].pmove.origin[1]*0.125;
// cl.predicted_origin[2] = cl.q2frame.playerstate[0].pmove.origin[2]*0.125;
// VectorCopy (cl.q2frame.playerstate[0].viewangles, cl.predicted_angles);
// if (cls.disable_servercount != cl.servercount
// && cl.refresh_prepped)
SCR_EndLoadingPlaque (); // get rid of loading plaque
@ -1357,13 +1444,45 @@ struct model_s *S_RegisterSexedModel (entity_state_t *ent, char *base)
*/
//returns a list of all the ents currently trying to play a sound.
unsigned int CLQ2_GatherSounds(vec3_t *positions, unsigned int *entnums, sfx_t **sounds, unsigned int max)
{
entity_state_t *s1;
sfx_t *sfx;
unsigned int pnum;
unsigned int count = 0;
q2frame_t *frame = &cl.q2frame;
for (pnum = 0 ; pnum<frame->num_entities ; pnum++)
{
s1 = &clq2_parse_entities[(frame->parse_entities+pnum)&(MAX_PARSE_ENTITIES-1)];
if (s1->u.q2.sound > 0 && s1->u.q2.sound < MAX_PRECACHE_SOUNDS)
{
sfx = cl.sound_precache[s1->u.q2.sound];
if (sfx)
{
if (count == max)
{
Con_DPrintf("Exceeded limit of %d looped sounds\n", max);
break;
}
//fixme: sexed sounds
entnums[count] = s1->number;
VectorCopy(s1->origin, positions[count]);
sounds[count] = sfx;
count++;
}
}
}
return count;
}
/*
===============
CL_AddPacketEntities
===============
*/
void CLQ2_AddPacketEntities (q2frame_t *frame)
static void CLQ2_AddPacketEntities (q2frame_t *frame)
{
entity_t ent;
entity_state_t *s1;
@ -1971,12 +2090,14 @@ void CLQ2_AddPacketEntities (q2frame_t *frame)
CL_AddViewWeapon
==============
*/
void CLQ2_AddViewWeapon (q2player_state_t *ps, q2player_state_t *ops)
static void CLQ2_AddViewWeapon (int seat, q2player_state_t *ps, q2player_state_t *ops)
{
entity_t gun; // view model
extern cvar_t cl_gunx, cl_guny, cl_gunz;
extern cvar_t cl_gunanglex, cl_gunangley, cl_gunanglez;
playerview_t *pv = &cl.playerview[0];
playerview_t *pv = &cl.playerview[seat];
pv->vm.oldmodel = NULL;
// allow the gun to be completely removed
if (!r_drawviewmodel.value)
@ -1990,19 +2111,22 @@ void CLQ2_AddViewWeapon (q2player_state_t *ps, q2player_state_t *ops)
return;
//generate root matrix..
VectorCopy(cl.playerview[0].simorg, pv->vw_origin);
AngleVectors(cl.playerview[0].simangles, pv->vw_axis[0], pv->vw_axis[1], pv->vw_axis[2]);
VectorCopy(pv->simorg, pv->vw_origin);
AngleVectors(pv->simangles, pv->vw_axis[0], pv->vw_axis[1], pv->vw_axis[2]);
VectorInverse(pv->vw_axis[1]);
memset (&gun, 0, sizeof(gun));
// if (gun_model)
// gun.model = gun_model; // development tool
// else
gun.model = cl.model_precache[ps->gunindex];
if (!gun.model)
pv->vm.oldmodel = cl.model_precache[ps->gunindex];
if (!pv->vm.oldmodel)
return;
pv->vm.oldframe = ps->gunframe;
if (ps->gunindex != ops->gunindex)
pv->vm.prevframe = ps->gunframe;
else
pv->vm.prevframe = ops->gunframe;
/*
gun.shaderRGBAf[0] = 1;
gun.shaderRGBAf[1] = 1;
gun.shaderRGBAf[2] = 1;
@ -2037,6 +2161,7 @@ void CLQ2_AddViewWeapon (q2player_state_t *ps, q2player_state_t *ops)
gun.framestate.g[FS_REG].lerpweight[1] = 1-cl.lerpfrac;
VectorCopy (gun.origin, gun.oldorigin); // don't lerp at all
V_AddEntity (&gun);
*/
}
@ -2047,7 +2172,7 @@ CL_CalcViewValues
Sets r_refdef view values
===============
*/
void CLQ2_CalcViewValues (void)
void CLQ2_CalcViewValues (int seat)
{
extern cvar_t v_gunkick_q2;
int i;
@ -2055,6 +2180,7 @@ void CLQ2_CalcViewValues (void)
q2frame_t *oldframe;
q2player_state_t *ps, *ops;
extern cvar_t gl_cshiftenabled;
playerview_t *pv = &cl.playerview[seat];
r_refdef.areabitsknown = true;
memcpy(r_refdef.areabits, cl.q2frame.areabits, sizeof(r_refdef.areabits));
@ -2062,12 +2188,12 @@ void CLQ2_CalcViewValues (void)
r_refdef.useperspective = true;
// find the previous frame to interpolate from
ps = &cl.q2frame.playerstate;
ps = &cl.q2frame.playerstate[seat];
i = (cl.q2frame.serverframe - 1) & Q2UPDATE_MASK;
oldframe = &cl.q2frames[i];
if (oldframe->serverframe != cl.q2frame.serverframe-1 || !oldframe->valid)
oldframe = &cl.q2frame; // previous frame was dropped or involid
ops = &oldframe->playerstate;
ops = &oldframe->playerstate[seat];
// see if the player entity was teleported this frame
if ( fabs(ops->pmove.origin[0] - ps->pmove.origin[0]) > 256*8
@ -2078,49 +2204,47 @@ void CLQ2_CalcViewValues (void)
lerp = cl.lerpfrac;
// calculate the origin
if (cl.worldmodel && (!cl_nopred.value) && !(cl.q2frame.playerstate.pmove.pm_flags & Q2PMF_NO_PREDICTION) && !cls.demoplayback)
if (cl.worldmodel && (!cl_nopred.value) && !(cl.q2frame.playerstate[seat].pmove.pm_flags & Q2PMF_NO_PREDICTION) && !cls.demoplayback)
{ // use predicted values
float delta;
backlerp = 1.0 - lerp;
for (i=0 ; i<3 ; i++)
{
r_refdef.vieworg[i] = cl.predicted_origin[i] + ops->viewoffset[i]
pv->simorg[i] = pv->predicted_origin[i] + ops->viewoffset[i]
+ cl.lerpfrac * (ps->viewoffset[i] - ops->viewoffset[i])
- backlerp * cl.prediction_error[i];
- backlerp * pv->prediction_error[i];
}
// smooth out stair climbing
delta = realtime - cl.predicted_step_time;
delta = realtime - pv->predicted_step_time;
if (delta < 0.1)
r_refdef.vieworg[2] -= cl.predicted_step * (0.1 - delta)*10;
pv->simorg[2] -= pv->predicted_step * (0.1 - delta)*10;
}
else
{ // just use interpolated values
for (i=0 ; i<3 ; i++)
r_refdef.vieworg
pv->simorg
[i] = ops->pmove.origin[i]*0.125 + ops->viewoffset[i]
+ lerp * (ps->pmove.origin[i]*0.125 + ps->viewoffset[i]
- (ops->pmove.origin[i]*0.125 + ops->viewoffset[i]) );
}
// if not running a demo or on a locked frame, add the local angle movement
if (cl.worldmodel && cl.q2frame.playerstate.pmove.pm_type < Q2PM_DEAD && !cls.demoplayback)
if (cl.worldmodel && ps->pmove.pm_type < Q2PM_DEAD && !cls.demoplayback)
{ // use predicted values
for (i=0 ; i<3 ; i++)
r_refdef.viewangles[i] = cl.predicted_angles[i];
pv->simangles[i] = pv->predicted_angles[i];
}
else
{ // just use interpolated values
for (i=0 ; i<3 ; i++)
r_refdef.viewangles[i] = LerpAngle (ops->viewangles[i], ps->viewangles[i], lerp);
pv->simangles[i] = LerpAngle (ops->viewangles[i], ps->viewangles[i], lerp);
}
for (i=0 ; i<3 ; i++)
r_refdef.viewangles[i] += v_gunkick_q2.value * LerpAngle (ops->kick_angles[i], ps->kick_angles[i], lerp);
pv->simangles[i] += v_gunkick_q2.value * LerpAngle (ops->kick_angles[i], ps->kick_angles[i], lerp);
VectorCopy(r_refdef.vieworg, cl.playerview[0].simorg);
VectorCopy(r_refdef.viewangles, cl.playerview[0].simangles);
// VectorCopy(r_refdef.viewangles, cl.viewangles);
// AngleVectors (r_refdef.viewangles, v_forward, v_right, v_up);
@ -2128,13 +2252,17 @@ void CLQ2_CalcViewValues (void)
// interpolate field of view
r_refdef.fov_x = ops->fov + lerp * (ps->fov - ops->fov);
//do interpolate blend alpha, but only if the rgb didn't change
// don't interpolate blend color
for (i=0 ; i<3 ; i++)
sw_blend[i] = ps->blend[i];
sw_blend[3] = ps->blend[3]*gl_cshiftenabled.value;
pv->screentint[i] = ps->blend[i];
if (ps->blend[0] == ops->blend[0] && ps->blend[1] == ops->blend[1] && ps->blend[2] == ops->blend[2] && (!ps->blend[3]) == (!ops->blend[3]))
pv->screentint[3] = (ops->blend[3] + lerp * (ps->blend[3]-ops->blend[3]))*gl_cshiftenabled.value;
else
pv->screentint[3] = ps->blend[3]*gl_cshiftenabled.value;
// add the weapon
CLQ2_AddViewWeapon (ps, ops);
CLQ2_AddViewWeapon (seat, ps, ops);
}
/*
@ -2147,6 +2275,7 @@ Emits all entities, particles, and lights to the refresh
void CLQ2_AddEntities (void)
{
extern cvar_t chase_active, chase_back, chase_up;
int seat;
if (cls.state != ca_active)
return;
@ -2171,7 +2300,8 @@ void CLQ2_AddEntities (void)
else
cl.lerpfrac = 1.0 - (cl.q2frame.servertime - cl.time*1000) * 0.01;
*/
CLQ2_CalcViewValues ();
for (seat = 0; seat < cl.splitclients; seat++)
CLQ2_CalcViewValues (seat);
CLQ2_AddPacketEntities (&cl.q2frame);
#if 0
CLQ2_AddProjectiles ();

View file

@ -538,7 +538,19 @@ void Con_Clear_f (void)
}
void Cmd_ConEchoCenter_f(void)
{
console_t *con;
con = Con_FindConsole(Cmd_Argv(1));
if (!con)
con = Con_Create(Cmd_Argv(1), 0);
if (con)
{
Cmd_ShiftArgs(1, false);
Con_PrintCon(con, Cmd_Args(), con->parseflags|PFS_NONOTIFY|PFS_CENTERED );
Con_PrintCon(con, "\n", con->parseflags|PFS_NONOTIFY|PFS_CENTERED);
}
}
void Cmd_ConEcho_f(void)
{
console_t *con;
@ -651,6 +663,7 @@ void Con_Init (void)
Cmd_AddCommand ("qterm", Con_QTerm_f);
#endif
Cmd_AddCommand ("conecho_center", Cmd_ConEchoCenter_f);
Cmd_AddCommand ("conecho", Cmd_ConEcho_f);
Cmd_AddCommand ("conclear", Cmd_ConClear_f);
Cmd_AddCommand ("conclose", Cmd_ConClose_f);
@ -688,7 +701,7 @@ If no console is visible, the notify window will pop up.
================
*/
void Con_PrintConChars (console_t *con, conchar_t *c, int len)
static void Con_PrintConChars (console_t *con, conchar_t *c, int len)
{
conline_t *oc;
conchar_t *o;
@ -717,7 +730,7 @@ void Con_PrintConChars (console_t *con, conchar_t *c, int len)
con->current->length+=len;
}
void Con_PrintCon (console_t *con, char *txt, unsigned int parseflags)
void Con_PrintCon (console_t *con, const char *txt, unsigned int parseflags)
{
conchar_t expanded[4096];
conchar_t *c;
@ -768,10 +781,11 @@ void Con_PrintCon (console_t *con, char *txt, unsigned int parseflags)
con->linecount--;
}
con->linecount++;
if (con->flags & CONF_NOTIMES)
if ((con->flags & CONF_NOTIMES) || (parseflags & PFS_NONOTIFY))
con->current->time = 0;
else
con->current->time = realtime + con->notif_t;
con->current->flags = (parseflags & PFS_CENTERED)?CONL_CENTERED:0;
#if defined(_WIN32) && !defined(NOMEDIA) && !defined(WINRT)
if (con->current)
@ -803,7 +817,7 @@ void Con_PrintCon (console_t *con, char *txt, unsigned int parseflags)
con->cr = false;
}
if (!con->current->length && con_timestamps.ival)
if (!con->current->length && con_timestamps.ival && !(parseflags & PFS_CENTERED))
{
char timeasc[64];
conchar_t timecon[64], *timeconend;
@ -843,17 +857,25 @@ void Con_PrintCon (console_t *con, char *txt, unsigned int parseflags)
}
if (con->flags & CONF_NOTIMES)
if ((con->flags & CONF_NOTIMES) || (parseflags & PFS_NONOTIFY))
con->current->time = 0;
else
con->current->time = realtime + con->notif_t;
}
void Con_Print (char *txt)
void Con_CenterPrint(const char *txt)
{
int flags = con_main.parseflags|PFS_NONOTIFY|PFS_CENTERED;
Con_PrintCon(&con_main, "^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\n", flags);
Con_PrintCon(&con_main, txt, flags); //client console
Con_PrintCon(&con_main, "\n^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\n", flags);
}
void Con_Print (const char *txt)
{
Con_PrintCon(&con_main, txt, con_main.parseflags); //client console
}
void Con_PrintFlags(char *txt, unsigned int setflags, unsigned int clearflags)
void Con_PrintFlags(const char *txt, unsigned int setflags, unsigned int clearflags)
{
setflags |= con_main.parseflags;
setflags &= ~clearflags;
@ -938,7 +960,7 @@ void VARGS Con_Printf (const char *fmt, ...)
Con_Print (msg);
}
void VARGS Con_SafePrintf (char *fmt, ...)
void VARGS Con_SafePrintf (const char *fmt, ...)
{
va_list argptr;
char msg[MAXPRINTMSG];
@ -1035,7 +1057,7 @@ void VARGS Con_DPrintf (const char *fmt, ...)
}
/*description text at the bottom of the console*/
void Con_Footerf(console_t *con, qboolean append, char *fmt, ...)
void Con_Footerf(console_t *con, qboolean append, const char *fmt, ...)
{
va_list argptr;
char msg[MAXPRINTMSG];
@ -1924,7 +1946,7 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in
}
l->lines = linecount;
l->numlines = linecount;
while(linecount-- > 0)
{
@ -1944,8 +1966,8 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in
{
int sstart;
int send;
sstart = sx+picw;
send = sstart;
int center;
send = sstart = picw;
for (c = s; c < e; )
{
c = Font_Decode(c, &codeflags, &codepoint);
@ -1956,6 +1978,10 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in
if (send == sstart)
send = Font_CharEndCoord(font_console, send, CON_WHITEMASK, ' ');
center = sx;
if (l->flags&CONL_CENTERED)
center += ((ex-sx) - send)/2;
if (y+charh >= seley && y < selsy)
{ //if they're both on the same line, make sure sx is to the left of ex, so our stuff makes sense
if (selex < selsx)
@ -1974,7 +2000,7 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in
c = Font_Decode(c, &codeflags, &codepoint);
send = Font_CharEndCoord(font_console, send, codeflags, codepoint);
if (send > selex)
if (send+center > selex)
break;
}
@ -1990,7 +2016,7 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in
{
Font_Decode(c, &codeflags, &codepoint);
x = Font_CharEndCoord(font_console, sstart, codeflags, codepoint);
if (x > selsx)
if (x+center > selsx)
break;
c = Font_Decode(c, &codeflags, &codepoint);
sstart = x;
@ -2003,6 +2029,9 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in
con->selstartoffset = 0;
}
sstart += center;
send += center;
if (selactive == 1)
{
R2D_ImagePaletteColour(0, 1.0);
@ -2018,6 +2047,19 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in
R2D_ImageColours(1.0, 1.0, 1.0, 1.0);
x = sx + picw;
if (l->flags&CONL_CENTERED)
{
int send = 0;
for (c = s; c < e; )
{
c = Font_Decode(c, &codeflags, &codepoint);
send = Font_CharEndCoord(font_console, send, codeflags, codepoint);
}
x += ((ex-sx) - send)/2;
}
Font_LineDraw(x, y, s, e);
if (y < top)
@ -2402,9 +2444,9 @@ char *Con_CopyConsole(console_t *con, qboolean nomarkup, qboolean onlyiflink)
if (outlen+3 > maxlen)
break;
#ifdef _WIN32
result[outlen++] = '\r';
#endif
//#ifdef _WIN32
// result[outlen++] = '\r';
//#endif
result[outlen++] = '\n';
cur = (conchar_t*)(l+1);
}

View file

@ -215,20 +215,33 @@ struct remapctx
char *type;
char *devicename;
int newdevid;
unsigned int found;
unsigned int failed;
};
static void IN_DeviceIDs_DoRemap(void *vctx, char *type, char *devicename, int *qdevid)
static void IN_DeviceIDs_DoRemap(void *vctx, const char *type, const char *devicename, int *qdevid)
{
struct remapctx *ctx = vctx;
if (!qdevid)
return;
if (!strcmp(ctx->type, type))
if (!strcmp(ctx->devicename, devicename))
*qdevid = ctx->newdevid;
{
if (qdevid)
*qdevid = ctx->newdevid;
else
ctx->failed++;
ctx->found++;
}
}
void IN_DeviceIDs_Enumerate(void *ctx, char *type, char *devicename, int *qdevid)
void IN_DeviceIDs_Enumerate(void *ctx, const char *type, const char *devicename, int *qdevid)
{
char buf[8192];
devicename = COM_QuotedString(devicename, buf, sizeof(buf), false);
if (!qdevid)
Con_Printf("%s (%s): %s\n", type, devicename, "device cannot be remapped");
Con_Printf("%s\t%s\t%s\n", type, "N/A", devicename);
else
Con_Printf("%s (%s): %i\n", type, devicename, *qdevid);
Con_Printf("%s\t%i\t%s\n", type, *qdevid, devicename);
}
void IN_DeviceIDs_f(void)
@ -237,13 +250,24 @@ void IN_DeviceIDs_f(void)
if (Cmd_Argc() > 3)
{
ctx.found = 0;
ctx.type = Cmd_Argv(1);
ctx.devicename = Cmd_Argv(2);
ctx.newdevid = atoi(Cmd_Argv(3));
ctx.newdevid = atoi(Cmd_Argv(2));
ctx.devicename = Cmd_Argv(3);
INS_EnumerateDevices(&ctx, IN_DeviceIDs_DoRemap);
if (ctx.failed)
Con_Printf("device cannot be remapped\n");
else if (!ctx.found)
Con_Printf("%s \"%s\" not known\n", ctx.type, ctx.devicename);
else if (!cl_warncmd.ival)
Con_Printf("device remapped\n");
}
else
{
Con_Printf("Type\tMapping\tName\n");
INS_EnumerateDevices(NULL, IN_DeviceIDs_Enumerate);
}
}
float IN_DetermineMouseRate(void)

View file

@ -118,6 +118,7 @@ typedef struct {
union {
HANDLE rawinputhandle;
} handles;
char sysname[MAX_OSPATH];
int qdeviceid;
} keyboard_t;
@ -126,6 +127,7 @@ typedef struct {
union {
HANDLE rawinputhandle; // raw input
} handles;
char sysname[MAX_OSPATH];
int numbuttons;
int oldbuttons;
@ -280,8 +282,8 @@ static int rawkbdcount;
static RAWINPUT *raw;
static int ribuffersize;
static cvar_t in_rawinput = CVARD("in_rawinput", "0", "Enables rawinput support for mice in XP onwards. Rawinput permits independant device identification (ie: splitscreen clients can each have their own mouse)");
static cvar_t in_rawinput_keyboard = CVARD("in_rawinput_keyboard", "0", "Enables rawinput support for keyboards in XP onwards as well as just mice. Requires in_rawinput to be set.");
static cvar_t in_rawinput_mice = CVARD("in_rawinput", "0", "Enables rawinput support for mice in XP onwards. Rawinput permits independant device identification (ie: splitscreen clients can each have their own mouse)");
static cvar_t in_rawinput_keyboard = CVARD("in_rawinput_keyboard", "0", "Enables rawinput support for keyboards in XP onwards as well as just mice.");
static cvar_t in_rawinput_rdp = CVARD("in_rawinput_rdp", "0", "Activate Remote Desktop Protocol devices too.");
void INS_RawInput_MouseDeRegister(void);
@ -899,25 +901,25 @@ void INS_RawInput_Init(void)
_RRID = (pRegisterRawInputDevices)GetProcAddress(user32,"RegisterRawInputDevices");
if (!_RRID)
{
Con_SafePrintf("Raw input: function RegisterRawInputDevices could not be registered\n");
Con_SafePrintf("Raw input: function RegisterRawInputDevices is not available\n");
return;
}
_GRIDL = (pGetRawInputDeviceList)GetProcAddress(user32,"GetRawInputDeviceList");
if (!_GRIDL)
{
Con_SafePrintf("Raw input: function GetRawInputDeviceList could not be registered\n");
Con_SafePrintf("Raw input: function GetRawInputDeviceList is not available\n");
return;
}
_GRIDIA = (pGetRawInputDeviceInfoA)GetProcAddress(user32,"GetRawInputDeviceInfoA");
if (!_GRIDIA)
{
Con_SafePrintf("Raw input: function GetRawInputDeviceInfoA could not be registered\n");
Con_SafePrintf("Raw input: function GetRawInputDeviceInfoA is not available\n");
return;
}
_GRID = (pGetRawInputData)GetProcAddress(user32,"GetRawInputData");
if (!_GRID)
{
Con_SafePrintf("Raw input: function GetRawInputData could not be registered\n");
Con_SafePrintf("Raw input: function GetRawInputData is not available\n");
return;
}
@ -958,6 +960,9 @@ void INS_RawInput_Init(void)
switch (pRawInputDeviceList[i].dwType)
{
case RIM_TYPEMOUSE:
if (!in_rawinput_mice.ival)
continue;
mtemp++;
break;
case RIM_TYPEKEYBOARD:
@ -995,9 +1000,14 @@ void INS_RawInput_Init(void)
switch (pRawInputDeviceList[i].dwType)
{
case RIM_TYPEMOUSE:
if (!in_rawinput_mice.ival)
continue;
// set handle
rawmice[rawmicecount].handles.rawinputhandle = pRawInputDeviceList[i].hDevice;
rawmice[rawmicecount].numbuttons = 10;
Q_strncpyz(rawmice[rawmicecount].sysname, dname, sizeof(rawmice[rawmicecount].sysname));
rawmice[rawmicecount].numbuttons = 16;
rawmice[rawmicecount].oldbuttons = 0;
rawmice[rawmicecount].qdeviceid = rawmicecount;
rawmicecount++;
break;
@ -1006,6 +1016,7 @@ void INS_RawInput_Init(void)
continue;
rawkbd[rawkbdcount].handles.rawinputhandle = pRawInputDeviceList[i].hDevice;
Q_strncpyz(rawkbd[rawkbdcount].sysname, dname, sizeof(rawkbd[rawkbdcount].sysname));
rawkbd[rawkbdcount].qdeviceid = rawkbdcount;
rawkbdcount++;
break;
@ -1023,7 +1034,7 @@ void INS_RawInput_Init(void)
break;
}
}
Con_SafePrintf("Raw input type %i: [%i] %s\n", (int)pRawInputDeviceList[i].dwType, i, dname);
Con_DPrintf("Raw input type %i: [%i] %s\n", (int)pRawInputDeviceList[i].dwType, i, dname);
}
@ -1108,7 +1119,7 @@ INS_Init
void INS_ReInit (void)
{
#ifdef USINGRAWINPUT
if (in_rawinput.value)
if (in_rawinput_mice.ival || in_rawinput_keyboard.ival)
{
INS_RawInput_Init();
}
@ -1164,7 +1175,7 @@ void INS_Init (void)
uiWheelMessage = RegisterWindowMessageA ( "MSWHEEL_ROLLMSG" );
#ifdef USINGRAWINPUT
Cvar_Register (&in_rawinput, "Input Controls");
Cvar_Register (&in_rawinput_mice, "Input Controls");
Cvar_Register (&in_rawinput_keyboard, "Input Controls");
Cvar_Register (&in_rawinput_rdp, "Input Controls");
#endif
@ -1967,9 +1978,9 @@ void INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, char *type, char
int idx;
for (idx = 0; idx < rawmicecount; idx++)
callback(ctx, "mouse", va("raw%i", idx), &rawmice[idx].qdeviceid);
callback(ctx, "mouse", rawmice[idx].sysname?rawmice[idx].sysname:va("raw%i", idx), &rawmice[idx].qdeviceid);
for (idx = 0; idx < rawkbdcount; idx++)
callback(ctx, "keyboard", va("raw%i", idx), &rawkbd[idx].qdeviceid);
callback(ctx, "keyboard", rawkbd[idx].sysname?rawkbd[idx].sysname:va("rawk%i", idx), &rawkbd[idx].qdeviceid);
#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)
if (dinput >= DINPUT_VERSION_DX7 && g_pMouse7)

View file

@ -752,7 +752,7 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info)
else
{
char cmdprefix[6];
snprintf(cmdprefix, sizeof(cmdprefix), "%i ", i);
snprintf(cmdprefix, sizeof(cmdprefix), "%i ", i+1);
//hey look! its you!
@ -762,7 +762,7 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info)
}
else
{
Con_Footerf(con, true, " ^[Suicide\\cmd\\kill^]");
Con_Footerf(con, true, " ^[Suicide\\cmd\\%skill^]", cmdprefix);
#ifndef CLIENTONLY
if (!sv.state)
Con_Footerf(con, true, " ^[Disconnect\\cmd\\disconnect^]");
@ -772,9 +772,9 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info)
if (cls.allow_cheats)
#endif
{
Con_Footerf(con, true, " ^[Noclip\\cmd\\noclip^]");
Con_Footerf(con, true, " ^[Fly\\cmd\\fly^]");
Con_Footerf(con, true, " ^[God\\cmd\\god^]");
Con_Footerf(con, true, " ^[Noclip\\cmd\\%snoclip^]", cmdprefix);
Con_Footerf(con, true, " ^[Fly\\cmd\\%sfly^]", cmdprefix);
Con_Footerf(con, true, " ^[God\\cmd\\%sgod^]", cmdprefix);
Con_Footerf(con, true, " ^[Give\\impulse\\9^]");
}
}
@ -2213,10 +2213,9 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down)
// Con_Printf ("%i : %i : %i\n", key, unicode, down); //@@@
//bug: my keyboard doesn't fire release events if the other shift is already pressed.
//bug: two of my keyboard doesn't fire release events if the other shift is already pressed (so I assume this is a common thing).
//hack around that by just force-releasing eg left if right is pressed, but only on inital press to avoid potential infinite loops if the state got bad.
//ctrl+alt don't seem to have the problem.
//you can still see a difference in that
if (key == K_LSHIFT && !keydown[K_LSHIFT] && keydown[K_RSHIFT])
Key_Event(devid, K_RSHIFT, 0, false);
if (key == K_RSHIFT && !keydown[K_RSHIFT] && keydown[K_LSHIFT])
@ -2488,7 +2487,9 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down)
return;
//first player is normally assumed anyway.
if (devid)
if (cl_forceseat.ival>0)
Q_snprintfz (p, sizeof(p), "p %i ", cl_forceseat.ival);
else if (devid)
Q_snprintfz (p, sizeof(p), "p %i ", devid+1);
else
*p = 0;

View file

@ -84,6 +84,13 @@ static qboolean Media_Changed (unsigned int mediatype)
return true;
}
void Media_WriteCurrentTrack(sizebuf_t *buf)
{
//fixme: for demo playback
MSG_WriteByte (buf, svc_cdtrack);
MSG_WriteByte (buf, 0);
}
//fake cd tracks.
qboolean Media_NamedTrack(const char *track, const char *looptrack)
{
@ -4628,7 +4635,7 @@ double Media_TweekCaptureFrameTime(double oldtime, double time) { return oldtime
void Media_RecordFrame (void) {}
void Media_CaptureDemoEnd(void) {}
void Media_RecordDemo_f(void) {}
void Media_RecordAudioFrame (short *sample_buffer, int samples) {}
//void Media_RecordAudioFrame (short *sample_buffer, int samples) {}
void Media_StopRecordFilm_f (void) {}
void Media_RecordFilm_f (void){}
void M_Menu_Media_f (void) {}

View file

@ -182,7 +182,7 @@ void M_Options_Remove(menu_t *m)
//options menu.
void M_Menu_Options_f (void)
{
extern cvar_t crosshair, r_projection;
extern cvar_t crosshair, r_projection, sv_autosave;
int y;
menuoption_t *updatecbo;
@ -211,6 +211,25 @@ void M_Menu_Options_f (void)
NULL
};
static const char *autosaveopts[] = {
"Off",
"30 secs",
"60 secs",
"90 secs",
"120 secs",
"5 mins",
NULL
};
static const char *autsavevals[] = {
"0",
"0.5",
"1",
"1.5",
"2",
"5",
NULL
};
menubulk_t bulk[] = {
MB_CONSOLECMD("Customize controls", "menu_keys\n", "Modify keyboard and mouse inputs."),
MB_CONSOLECMD("Go to console", "toggleconsole\nplay misc/menu2.wav\n", "Open up the engine console."),
@ -226,7 +245,10 @@ void M_Menu_Options_f (void)
MB_CHECKBOXCVAR("Lookspring", lookspring, 0),
MB_CHECKBOXCVAR("Lookstrafe", lookstrafe, 0),
MB_CHECKBOXCVAR("Windowed Mouse", _windowed_mouse, 0),
MB_COMBORETURN("Auto Update", autoupopts, Sys_GetAutoUpdateSetting(), updatecbo, "Hello World"),
MB_COMBORETURN("Auto Update", autoupopts, Sys_GetAutoUpdateSetting(), updatecbo, "This downloads engine updates from the internet, when a new build is available."),
#ifndef CLIENTONLY
MB_COMBOCVAR("Auto Save", sv_autosave, autosaveopts, autsavevals, NULL),
#endif
MB_SPACING(4),
// removed hud options (cl_sbar, cl_hudswap, old-style chat, old-style msg)
MB_CONSOLECMD("Video Options", "menu_video\n", "Set video resolution, color depth, refresh rate, and anti-aliasing options."),
@ -244,7 +266,7 @@ void M_Menu_Options_f (void)
#endif
#ifdef TEXTEDITOR
//this option is a bit strange in q2.
MB_CHECKBOXCVAR("QC Debugger", pr_debugger, 0),
// MB_CHECKBOXCVAR("QC Debugger", pr_debugger, 0),
#endif
// removed downloads (is this still appropriate?)
// removed teamplay
@ -1050,9 +1072,11 @@ void M_Menu_Render_f (void)
"1",
NULL
};
static const char *logcenteropts[] = {"Off", "Singleplayer", "Always", NULL};
static const char *logcentervalues[] = {"0", "1", "2", NULL};
menu_t *menu;
extern cvar_t r_novis, cl_item_bobbing, r_waterwarp, r_nolerp, r_noframegrouplerp, r_fastsky, gl_nocolors, gl_lerpimages, r_wateralpha, r_drawviewmodel, gl_cshiftenabled, r_hdr_irisadaptation;
extern cvar_t r_novis, cl_item_bobbing, r_waterwarp, r_nolerp, r_noframegrouplerp, r_fastsky, gl_nocolors, gl_lerpimages, r_wateralpha, r_drawviewmodel, gl_cshiftenabled, r_hdr_irisadaptation, scr_logcenterprint;
#ifdef GLQUAKE
extern cvar_t r_bloom;
#endif
@ -1073,6 +1097,7 @@ void M_Menu_Render_f (void)
MB_SLIDER("Viewmodel Alpha", r_drawviewmodel, 0, 1, 0.1, NULL),
MB_CHECKBOXCVAR("Poly Blending", gl_cshiftenabled, 0),
MB_CHECKBOXCVAR("Disable Colormap", gl_nocolors, 0),
MB_COMBOCVAR("Log Centerprints", scr_logcenterprint, logcenteropts, logcentervalues, "Display centerprints in the console also."),
#ifdef GLQUAKE
MB_CHECKBOXCVAR("Bloom", r_bloom, 0),
#endif

View file

@ -19,67 +19,120 @@ typedef struct {
shader_t *picshader;
} loadsavemenuinfo_t;
#define MAX_SAVEGAMES 20
#define SAVEFIRST_AUTO 1
#define SAVECOUNT_AUTO 3
#define SAVEFIRST_STANDARD (SAVEFIRST_AUTO + SAVECOUNT_AUTO)
#define SAVECOUNT_STANDARD 20
#define MAX_SAVEGAMES (1+SAVECOUNT_AUTO+SAVECOUNT_STANDARD)
struct
{
char map[22+1];
qboolean loadable;
qbyte saveable; //0=autosave, 1=regular, 2=quick
char sname[9];
char desc[22+1];
char kills[39-22+1];
char time[64];
char map[32];
} m_saves[MAX_SAVEGAMES];
int loadable[MAX_SAVEGAMES];
void M_ScanSaves (void)
static void M_ScanSave(unsigned int slot, const char *name, qboolean savable)
{
int i, j;
char *in, *out, *end;
int j;
char line[MAX_OSPATH];
vfsfile_t *f;
int version;
for (i=0 ; i<MAX_SAVEGAMES ; i++)
{
Q_strncpyz (m_saves[i].map, "--- UNUSED SLOT ---", sizeof(m_saves[i].map));
Q_strncpyz (m_saves[i].kills, "", sizeof(m_saves[i].kills));
Q_strncpyz (m_saves[i].time, "", sizeof(m_saves[i].time));
loadable[i] = false;
m_saves[slot].saveable = savable;
m_saves[slot].loadable = false;
Q_strncpyz (m_saves[slot].sname, name, sizeof(m_saves[slot].sname));
Q_strncpyz (m_saves[slot].desc, "--- UNUSED SLOT ---", sizeof(m_saves[slot].desc));
Q_strncpyz (m_saves[slot].kills, "", sizeof(m_saves[slot].kills));
Q_strncpyz (m_saves[slot].time, "", sizeof(m_saves[slot].time));
snprintf (line, sizeof(line), "saves/s%i/info.fsv", i);
snprintf (line, sizeof(line), "saves/%s/info.fsv", m_saves[slot].sname);
f = FS_OpenVFS (line, "rb", FS_GAME);
if (!f)
{ //legacy saved games from some other engine
snprintf (line, sizeof(line), "%s.sav", m_saves[slot].sname);
f = FS_OpenVFS (line, "rb", FS_GAME);
if (!f)
{ //legacy saved games from some other engine
snprintf (line, sizeof(line), "s%i.sav", i);
f = FS_OpenVFS (line, "rb", FS_GAME);
}
if (f)
{
VFS_GETS(f, line, sizeof(line));
version = atoi(line);
if (version != 5 && version != 6 && (version < FTESAVEGAME_VERSION || version >= FTESAVEGAME_VERSION+GT_MAX))
{
Q_strncpyz (m_saves[i].map, "Incompatible version", sizeof(m_saves[i].map));
VFS_CLOSE (f);
continue;
}
// read the desc, change _ back to space, fill the separate fields
VFS_GETS(f, line, sizeof(line));
for (j=0 ; line[j] ; j++)
if (line[j] == '_')
line[j] = ' ';
for (; j < sizeof(line[j]); j++)
line[j] = '\0';
memcpy(m_saves[i].map, line, 22);
m_saves[i].map[22] = 0;
memcpy(m_saves[i].kills, line+22, 39-22);
m_saves[i].kills[39-22] = 0;
Q_strncpyz(m_saves[i].time, line+39, sizeof(m_saves[i].time));
loadable[i] = true;
VFS_CLOSE (f);
continue;
}
}
if (f)
{
VFS_GETS(f, line, sizeof(line));
version = atoi(line);
if (version != 5 && version != 6 && (version < FTESAVEGAME_VERSION || version >= FTESAVEGAME_VERSION+GT_MAX))
{
Q_strncpyz (m_saves[slot].desc, "Incompatible version", sizeof(m_saves[slot].desc));
VFS_CLOSE (f);
return;
}
// read the desc, change _ back to space, fill the separate fields
VFS_GETS(f, line, sizeof(line));
for (j=0 ; line[j] ; j++)
if (line[j] == '_')
line[j] = ' ';
for (; j < sizeof(line[j]); j++)
line[j] = '\0';
memcpy(m_saves[slot].desc, line, 22);
m_saves[slot].desc[22] = 0;
for (in = line+22, out = m_saves[slot].kills, end = line+39; in < end && *in == ' '; )
in++;
for (out = m_saves[slot].kills; in < end; )
*out++ = *in++;
for (end = m_saves[slot].kills; out > end && out[-1] == ' '; )
out--;
*out = 0;
Q_strncpyz(m_saves[slot].time, line+39, sizeof(m_saves[slot].time));
if (version == 5 || version == 6)
{
for (j = 0; j < 16; j++)
VFS_GETS(f, line, sizeof(line)); //16 parms
VFS_GETS(f, line, sizeof(line)); //skill
VFS_GETS(f, m_saves[slot].map, sizeof(m_saves[slot].map));
}
m_saves[slot].loadable = true;
VFS_CLOSE (f);
}
}
const char *M_ChooseAutoSave(void)
{
int i, j;
for (i = SAVEFIRST_AUTO; i < SAVEFIRST_AUTO+SAVECOUNT_AUTO; i++)
{
M_ScanSave(i, va("a%i", i-SAVEFIRST_AUTO), false);
if (!m_saves[i].loadable)
return m_saves[i].sname;
}
for (i = SAVEFIRST_AUTO; i < SAVEFIRST_AUTO+SAVECOUNT_AUTO; i++)
{
for (j = SAVEFIRST_AUTO; j < SAVEFIRST_AUTO+SAVECOUNT_AUTO; j++)
if (strcmp(m_saves[i].time, m_saves[j].time) > 0)
break;
if (j == SAVEFIRST_AUTO+SAVECOUNT_AUTO)
return m_saves[i].sname;
}
return m_saves[SAVEFIRST_AUTO].sname;
}
static void M_ScanSaves (void)
{
int i;
M_ScanSave(0, "quick", 2);
for (i=SAVEFIRST_AUTO ; i<SAVEFIRST_AUTO+SAVECOUNT_AUTO ; i++)
M_ScanSave(i, va("a%i", i-SAVEFIRST_AUTO), false);
for (i=SAVEFIRST_STANDARD ; i<SAVEFIRST_STANDARD+SAVECOUNT_STANDARD ; i++)
M_ScanSave(i, va("s%i", i-SAVEFIRST_STANDARD), true);
}
static void M_Menu_LoadSave_Remove(menu_t *menu)
@ -110,18 +163,57 @@ static void M_Menu_LoadSave_Preview_Draw(int x, int y, menucustom_t *item, menu_
Image_UnloadTexture(info->picshader->defaulttextures->base);
R_UnloadShader(info->picshader);
}
info->picshader = R_RegisterPic(va("saves/s%i/screeny.tga", slot));
info->picshader = R_RegisterPic(va("saves/%s/screeny.tga", m_saves[slot].sname));
}
if (info->picshader)
{
if (R_GetShaderSizes(info->picshader, &width, &height, false) > 0)
shader_t *pic = NULL;
switch(R_GetShaderSizes(info->picshader, &width, &height, false))
{
//FIXME: maintain aspect
R2D_ScalePic (x, y, 160,120/*item->common.width, item->common.height*/, info->picshader);
case 1:
pic = info->picshader;
break;
case 0:
if (*m_saves[slot].map)
pic = R_RegisterPic(va("levelshots/%s", m_saves[slot].map));
break;
}
if (pic)
{
int w = 160;
int h = 120;
if (R_GetShaderSizes(pic, &width, &height, false) <= 0)
{
width = 64;
height = 64;
}
if ((float)width/height > (float)w/h)
{
w = 160;
h = ((float)w*height) / width;
}
else
{
h = 120;
w = ((float)h*width) / height;
}
R2D_ScalePic (x + (160-w)/2, y + (120-h)/2, w, h, pic);
}
}
Draw_FunStringWidth(x, y+120+0, m_saves[slot].time, 160, 2, false);
Draw_FunStringWidth(x, y+120+8, m_saves[slot].kills, 160, 2, false);
switch(m_saves[slot].saveable)
{
case 2:
Draw_FunStringWidth(x, y+120+16, "Quick Save", 160, 2, false);
break;
case 0:
Draw_FunStringWidth(x, y+120+16, "Autosave", 160, 2, false);
break;
}
Draw_FunStringWidth(x, y+120+24, m_saves[slot].sname, 160, 2, false);
}
}
@ -151,7 +243,10 @@ void M_Menu_Save_f (void)
for (i=0 ; i< MAX_SAVEGAMES; i++)
{
op = (menuoption_t *)MC_AddConsoleCommandf(menu, 16, 192, 32+8*i, false, m_saves[i].map, "savegame s%i\nclosemenu\n", i);
if (m_saves[i].saveable)
op = (menuoption_t *)MC_AddConsoleCommandf(menu, 16, 192, 32+8*i, false, m_saves[i].desc, "savegame %s\nclosemenu\n", m_saves[i].sname);
else
MC_AddWhiteText(menu, 16, 170, 32+8*i, m_saves[i].desc, false);
if (!menu->selecteditem)
menu->selecteditem = op;
}
@ -171,21 +266,23 @@ void M_Menu_Load_f (void)
menu->data = menu+1;
MC_AddCenterPicture(menu, 4, 24, "gfx/p_load.lmp");
menu->cursoritem = (menuoption_t *)MC_AddRedText(menu, 8, 0, 32, NULL, false);
menu->remove = M_Menu_LoadSave_Remove;
M_ScanSaves ();
for (i=0 ; i< MAX_SAVEGAMES; i++)
{
if (loadable[i])
op = (menuoption_t *)MC_AddConsoleCommandf(menu, 16, 170, 32+8*i, false, m_saves[i].map, "loadgame s%i\nclosemenu\n", i);
if (m_saves[i].loadable)
op = (menuoption_t *)MC_AddConsoleCommandf(menu, 16, 170, 32+8*i, false, m_saves[i].desc, "loadgame %s\nclosemenu\n", m_saves[i].sname);
else
MC_AddWhiteText(menu, 16, 170, 32+8*i, m_saves[i].map, false);
MC_AddWhiteText(menu, 16, 170, 32+8*i, m_saves[i].desc, false);
if (!menu->selecteditem && op)
menu->selecteditem = op;
}
if (menu->selecteditem)
menu->cursoritem = (menuoption_t *)MC_AddRedText(menu, 8, 0, menu->selecteditem->common.posy, NULL, false);
MC_AddCustom(menu, 192, 60-16, NULL, 0)->draw = M_Menu_LoadSave_Preview_Draw;
}

View file

@ -416,6 +416,7 @@ bindnames_t q2bindnames[] =
{
{"+attack", "attack "},
{"cmd weapnext", "next weapon "},
{"cmd weapprev", "prev weapon "},
{"+forward", "walk forward "},
{"+back", "backpedal "},
{"+left", "turn left "},
@ -506,6 +507,7 @@ void M_Menu_Keys_f (void)
int y;
menu_t *menu;
vfsfile_t *bindslist;
extern cvar_t cl_splitscreen;
Key_Dest_Add(kdm_emenu);
m_state = m_complex;
@ -534,7 +536,7 @@ void M_Menu_Keys_f (void)
break;
}
if (cl_forceseat.ival)
if (cl.splitclients || cl_splitscreen.ival || cl_forceseat.ival)
{
static char *texts[MAX_SPLITS+2] =
{

View file

@ -380,6 +380,7 @@ void M_Complex_Draw(void);
void M_Script_Init(void);
void M_Serverlist_Init(void);
const char *M_ChooseAutoSave(void);
void M_Menu_Main_f (void);
void M_Menu_SinglePlayer_f (void);
void M_Menu_Load_f (void);

View file

@ -279,7 +279,8 @@ typedef struct part_type_s {
SM_LAVASPLASH, //lavasplash = q1-style lavasplash
SM_UNICIRCLE, //unicircle = uniform circle
SM_FIELD, //field = synced field (brightfield, etc)
SM_DISTBALL // uneven distributed ball
SM_DISTBALL, // uneven distributed ball
SM_MESHSURFACE //distributed roughly evenly over the surface of the mesh
} spawnmode;
float gravity;
@ -1186,7 +1187,7 @@ void P_ParticleEffect_f(void)
float tscale;
tscale = atof(Cmd_Argv(5));
if (tscale < 0)
if (tscale <= 0)
tscale = 1;
ptype->s1 = atof(value)/tscale;
@ -1200,6 +1201,51 @@ void P_ParticleEffect_f(void)
if (ptype->randsmax < 1 || ptype->texsstride == 0)
ptype->randsmax = 1;
}
else if (!strcmp(var, "atlas"))
{ //atlas countineachaxis first [last]
int dims;
int i;
int m;
dims = atof(Cmd_Argv(1));
i = atoi(Cmd_Argv(2));
m = atoi(Cmd_Argv(3));
if (dims < 1)
dims = 1;
if (m > (m/dims)*dims+dims-1)
{
m = (m/dims)*dims+dims-1;
Con_Printf("effect %s wraps across an atlased line\n", ptype->name);
}
if (m < i)
m = i;
ptype->s1 = 1.0/dims * (i%dims);
ptype->s2 = 1.0/dims * (1+(i%dims));
ptype->t1 = 1.0/dims * (i/dims);
ptype->t2 = 1.0/dims * (1+(i/dims));
ptype->randsmax = m-i;
ptype->texsstride = ptype->s2-ptype->s1;
//its modulo
ptype->randsmax++;
}
else if (!strcmp(var, "rotation"))
{
ptype->rotationstartmin = atof(value)*M_PI/180;
if (Cmd_Argc()>2)
ptype->rotationstartrand = atof(Cmd_Argv(2))*M_PI/180-ptype->rotationstartmin;
else
ptype->rotationstartrand = 0;
ptype->rotationmin = atof(Cmd_Argv(3))*M_PI/180;
if (Cmd_Argc()>4)
ptype->rotationrand = atof(Cmd_Argv(4))*M_PI/180-ptype->rotationmin;
else
ptype->rotationrand = 0;
}
else if (!strcmp(var, "rotationstart"))
{
ptype->rotationstartmin = atof(value)*M_PI/180;
@ -1517,23 +1563,66 @@ parsefluid:
}
else if (!strcmp(var, "sound"))
{
char *e;
ptype->sounds = BZ_Realloc(ptype->sounds, sizeof(partsounds_t)*(ptype->numsounds+1));
Q_strncpyz(ptype->sounds[ptype->numsounds].name, Cmd_Argv(1), sizeof(ptype->sounds[ptype->numsounds].name));
if (*ptype->sounds[ptype->numsounds].name)
S_PrecacheSound(ptype->sounds[ptype->numsounds].name);
ptype->sounds[ptype->numsounds].vol = atof(Cmd_Argv(2));
if (!ptype->sounds[ptype->numsounds].vol)
ptype->sounds[ptype->numsounds].vol = 1;
ptype->sounds[ptype->numsounds].atten = atof(Cmd_Argv(3));
if (!ptype->sounds[ptype->numsounds].atten)
ptype->sounds[ptype->numsounds].atten = 1;
ptype->sounds[ptype->numsounds].pitch = atof(Cmd_Argv(4));
if (!ptype->sounds[ptype->numsounds].pitch)
ptype->sounds[ptype->numsounds].pitch = 100;
ptype->sounds[ptype->numsounds].delay = atof(Cmd_Argv(5));
if (!ptype->sounds[ptype->numsounds].delay)
ptype->sounds[ptype->numsounds].delay = 0;
ptype->sounds[ptype->numsounds].weight = atof(Cmd_Argv(6));
ptype->sounds[ptype->numsounds].vol = 1;
ptype->sounds[ptype->numsounds].atten = 1;
ptype->sounds[ptype->numsounds].pitch = 100;
ptype->sounds[ptype->numsounds].delay = 0;
ptype->sounds[ptype->numsounds].weight = 0;
strtoul(Cmd_Argv(2), &e, 0);
while(*e == ' ' || *e == '\t')
e++;
if (*e)
{
int p;
for(p = 2; p < Cmd_Argc(); p++)
{
e = Cmd_Argv(p);
if (!Q_strncasecmp(e, "vol=", 4) || !Q_strncasecmp(e, "volume=", 7))
ptype->sounds[ptype->numsounds].vol = atof(strchr(e, '=')+1);
else if (!Q_strncasecmp(e, "attn=", 5) || !Q_strncasecmp(e, "atten=", 6) || !Q_strncasecmp(e, "attenuation=", 12))
{
e = strchr(e, '=')+1;
if (!strcmp(e, "none"))
ptype->sounds[ptype->numsounds].atten = 0;
else if (!strcmp(e, "normal"))
ptype->sounds[ptype->numsounds].atten = 1;
else
ptype->sounds[ptype->numsounds].atten = atof(e);
}
else if (!Q_strncasecmp(e, "pitch=", 6))
ptype->sounds[ptype->numsounds].pitch = atof(strchr(e, '=')+1);
else if (!Q_strncasecmp(e, "delay=", 6))
ptype->sounds[ptype->numsounds].delay = atof(strchr(e, '=')+1);
else if (!Q_strncasecmp(e, "weight=", 7))
ptype->sounds[ptype->numsounds].weight = atof(strchr(e, '=')+1);
else
Con_Printf("Bad named argument: %s\n", e);
}
}
else
{
ptype->sounds[ptype->numsounds].vol = atof(Cmd_Argv(2));
if (!ptype->sounds[ptype->numsounds].vol)
ptype->sounds[ptype->numsounds].vol = 1;
ptype->sounds[ptype->numsounds].atten = atof(Cmd_Argv(3));
if (!ptype->sounds[ptype->numsounds].atten)
ptype->sounds[ptype->numsounds].atten = 1;
ptype->sounds[ptype->numsounds].pitch = atof(Cmd_Argv(4));
if (!ptype->sounds[ptype->numsounds].pitch)
ptype->sounds[ptype->numsounds].pitch = 100;
ptype->sounds[ptype->numsounds].delay = atof(Cmd_Argv(5));
if (!ptype->sounds[ptype->numsounds].delay)
ptype->sounds[ptype->numsounds].delay = 0;
ptype->sounds[ptype->numsounds].weight = atof(Cmd_Argv(6));
}
if (!ptype->sounds[ptype->numsounds].weight)
ptype->sounds[ptype->numsounds].weight = 1;
ptype->numsounds++;
@ -2729,7 +2818,6 @@ static void P_ImportEffectInfo(char *config, char *line)
ptype->colorindex = -1;
ptype->spawnchance = 1;
ptype->randsmax = 1;
ptype->looks.scalefactor = 2;
ptype->looks.invscalefactor = 0;
ptype->looks.type = PT_NORMAL;
@ -2738,6 +2826,14 @@ static void P_ImportEffectInfo(char *config, char *line)
ptype->looks.stretch = 1;
ptype->dl_time = 0;
i = 63; //default texture is 63.
ptype->s1 = teximages[i][0];
ptype->s2 = teximages[i][1];
ptype->t1 = teximages[i][2];
ptype->t2 = teximages[i][3];
ptype->texsstride = 0;
ptype->randsmax = 1;
}
else if (!ptype)
{
@ -2887,7 +2983,8 @@ static void P_ImportEffectInfo(char *config, char *line)
else if (!strcmp(arg[0], "bounce") && args == 2)
{
ptype->clipbounce = atof(arg[1]);
ptype->cliptype = ptype - part_type;
if (ptype->clipbounce < 0)
ptype->cliptype = ptype - part_type;
}
else if (!strcmp(arg[0], "airfriction") && args == 2)
ptype->friction[2] = ptype->friction[1] = ptype->friction[0] = atof(arg[1]);
@ -2974,16 +3071,16 @@ static void P_ImportEffectInfo(char *config, char *line)
ptype->dl_corona_scale = atof(arg[2]);
}
#if 1
else if (!strcmp(arg[0], "staincolor") && args == 3)
Con_Printf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "stainalpha") && args == 3)
Con_Printf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "stainsize") && args == 3)
Con_Printf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "staintex") && args == 3)
Con_Printf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "staincolor") && args == 3) //stainmaps multiplier
Con_DPrintf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "stainalpha") && args == 3) //affects stainmaps AND stain-decals.
Con_DPrintf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "stainsize") && args == 3) //affects stainmaps AND stain-decals.
Con_DPrintf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "staintex") && args == 3) //actually spawns a decal
Con_DPrintf("Particle effect token %s not supported\n", arg[0]);
else if (!strcmp(arg[0], "stainless") && args == 2)
Con_Printf("Particle effect token %s not supported\n", arg[0]);
Con_DPrintf("Particle effect token %s not supported\n", arg[0]);
#endif
else if (!strcmp(arg[0], "rotate") && args == 5)
{
@ -3839,8 +3936,8 @@ static void PScript_ApplyOrgVel(vec3_t oorg, vec3_t ovel, vec3_t eforg, vec3_t a
}
}
j = 0;
m = 0;
j = pno%NUMVERTEXNORMALS;
m = pno/NUMVERTEXNORMALS;
break;
default: //others don't need intitialisation
break;
@ -4035,6 +4132,7 @@ static void PScript_ApplyOrgVel(vec3_t oorg, vec3_t ovel, vec3_t eforg, vec3_t a
ovel[2] += crand() * ptype->velwrand[2];
VectorAdd(ovel, ptype->velbias, ovel);
}
VectorAdd(oorg, ptype->orgbias, oorg);
}
@ -4208,6 +4306,8 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count,
particle_t *p;
beamseg_t *b, *bfirst;
vec3_t ofsvec, arsvec; // offsetspread vec, areaspread vec
float orgadd, veladd;
trailstate_t *ts;
if (typenum >= FALLBACKBIAS && fallback)
@ -4414,6 +4514,13 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count,
j = 0;
m = 0;
break;
// case SM_MESHSURFACE:
// meshsurface = querymesh;
// totalarea = gah;
// density = count / totalarea;
// area = 0;
// tri = -1;
// break;
default: //others don't need intitialisation
break;
}
@ -4518,17 +4625,29 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count,
p->rgba[1] += p->org[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*p->die;
p->rgba[2] += p->org[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*p->die;
#if 1
#if 0
PScript_ApplyOrgVel(p->org, p->vel, org, axis, i, pcount, ptype);
#else
// randomvel
p->vel[0] = crandom()*ptype->randomvel;
p->vel[1] = crandom()*ptype->randomvel;
p->vel[2] = crandom()*ptype->randomvelvert + ptype->randomvelvertbias;
p->vel[0] = 0;
p->vel[1] = 0;
p->vel[2] = 0;
// handle spawn modes (org/vel)
switch (ptype->spawnmode)
{
/* case SM_MESHSURFACE:
if (area <= 0)
{
tri++;
area += calcarea(tri);
arsvec[] = calcnormal(tri);
}
ofsvec[] = randompointintriangle(tri);
area -= density;
break;
*/
case SM_BOX:
ofsvec[0] = crandom();
ofsvec[1] = crandom();
@ -4701,8 +4820,19 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count,
p->org[2] -= orgadd;
}
#endif
if (ptype->flags & PT_WORLDSPACERAND)
{
p->org[0] += crand() * ptype->orgwrand[0];
p->org[1] += crand() * ptype->orgwrand[1];
p->org[2] += crand() * ptype->orgwrand[2];
p->vel[0] += crand() * ptype->velwrand[0];
p->vel[1] += crand() * ptype->velwrand[1];
p->vel[2] += crand() * ptype->velwrand[2];
VectorAdd(p->vel, ptype->velbias, p->vel);
}
VectorAdd(p->org, ptype->orgbias, p->org);
#endif
p->die = particletime + ptype->die - p->die;
}
@ -6759,7 +6889,12 @@ static void PScript_DrawParticleTypes (void)
p->rgba[0]*-10+p->rgba[1]*-10,
30*p->rgba[3]*r_bloodstains.value);
if (part_type + type->cliptype == type)
if (type->clipbounce < 0)
{
p->die = -1;
continue;
}
else if (part_type + type->cliptype == type)
{ //bounce
dist = DotProduct(p->vel, normal);// * (-1-(rand()/(float)0x7fff)/2);
dist *= -type->clipbounce;
@ -6776,6 +6911,7 @@ static void PScript_DrawParticleTypes (void)
{
p->die = -1;
VectorNormalize(p->vel);
if (type->clipbounce)
{
VectorScale(normal, type->clipbounce, normal);

View file

@ -390,7 +390,19 @@ void QCBUILTIN PF_cl_getcursormode (pubprogfuncs_t *prinst, struct globalvars_s
void QCBUILTIN PF_cl_playingdemo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_FLOAT(OFS_RETURN) = !!cls.demoplayback;
switch(cls.demoplayback)
{
case DPB_NONE:
G_FLOAT(OFS_RETURN) = 0;
break;
case DPB_MVD:
case DPB_EZTV:
G_FLOAT(OFS_RETURN) = 2;
break;
default:
G_FLOAT(OFS_RETURN) = 1;
break;
}
}
void QCBUILTIN PF_cl_runningserver (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)

View file

@ -632,6 +632,33 @@ static void QCBUILTIN PF_NoCSQC (pubprogfuncs_t *prinst, struct globalvars_s *pr
prinst->RunError(prinst, "\nBuiltin %i:%s does not make sense in csqc.\nCSQC is not compatible.", binum, fname);
PR_BIError (prinst, "bulitin not implemented");
}
static void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
func_t funcref = G_INT(OFS_PARM0);
char *funcname = NULL;
int args;
int builtinno;
if (prinst->GetFunctionInfo(prinst, funcref, &args, &builtinno, funcname, sizeof(funcname)))
{ //qc defines the function at least. nothing weird there...
if (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins)
{
if (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme || prinst->parms->globalbuiltins[builtinno] == PF_NoCSQC)
G_FLOAT(OFS_RETURN) = false; //the builtin with that number isn't defined.
else
{
G_FLOAT(OFS_RETURN) = true; //its defined, within the sane range, mapped, everything. all looks good.
//we should probably go through the available builtins and validate that the qc's name matches what would be expected
//this is really intended more for builtins defined as #0 though, in such cases, mismatched assumptions are impossible.
}
}
else
G_FLOAT(OFS_RETURN) = false; //not a valid builtin (#0 builtins get remapped according to the function name)
}
else
{ //not valid somehow.
G_FLOAT(OFS_RETURN) = false;
}
}
static void QCBUILTIN PF_cl_cprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -704,7 +731,7 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out)
effects = in->v->effects;
if (effects & NQEF_ADDITIVE)
out->flags |= RF_ADDITIVE;
if (effects & DPEF_NOSHADOW)
if (effects & EF_NOSHADOW)
out->flags |= RF_NOSHADOW;
if (effects & EF_NODEPTHTEST)
out->flags |= RF_NODEPTHTEST;
@ -1885,7 +1912,6 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars
V_ApplyRefdef();
R_RenderView();
R2D_PolyBlend ();
R_DrawNameTags();
if (r_refdef.grect.x || r_refdef.grect.y || r_refdef.grect.width != vid.fbvwidth || r_refdef.grect.height != vid.fbvheight)
{
@ -1903,6 +1929,8 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars
else
scissored = false;
R_DrawNameTags();
if (r_refdef.drawsbar)
{
#ifdef PLUGINS
@ -4516,7 +4544,7 @@ static void QCBUILTIN PF_DeltaListen(pubprogfuncs_t *prinst, struct globalvars_s
func_t func = G_INT(OFS_PARM1);
unsigned int flags = G_FLOAT(OFS_PARM2);
if (prinst->GetFuncArgCount(prinst, func) < 0)
if (!prinst->GetFunctionInfo(prinst, func, NULL, NULL, NULL, 0))
{
Con_Printf("PF_DeltaListen: Bad function index\n");
return;
@ -5215,6 +5243,7 @@ static struct {
{"findfloat", PF_FindFloat, 98}, // #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT)
{"findentity", PF_FindFloat, 98}, // #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT)
{"checkextension", PF_checkextension, 99}, // #99 float(string extname) checkextension (EXT_CSQC)
{"checkbuiltin", PF_checkbuiltin, 0},
{"anglemod", PF_anglemod, 102},
//110
@ -5806,7 +5835,7 @@ void VARGS CSQC_Abort (char *format, ...) //an error occured.
if (pr_csqc_coreonerror.value)
{
int size = 1024*1024*8;
size_t size = 1024*1024*8;
char *buffer = BZ_Malloc(size);
csqcprogs->save_ents(csqcprogs, buffer, &size, size, 3);
COM_WriteFile("csqccore.txt", FS_GAMEONLY, buffer, size);
@ -6503,7 +6532,7 @@ void CSQC_CoreDump(void)
}
{
int size = 1024*1024*8;
size_t size = 1024*1024*8;
char *buffer = BZ_Malloc(size);
csqcprogs->save_ents(csqcprogs, buffer, &size, size, 3);
COM_WriteFile("csqccore.txt", FS_GAMEONLY, buffer, size);

View file

@ -1270,6 +1270,33 @@ static void QCBUILTIN PF_Fixme (pubprogfuncs_t *prinst, struct globalvars_s *pr_
prinst->RunError(prinst, "\nBuiltin %i:%s not implemented.\nMenu is not compatible.", binum, fname);
PR_BIError (prinst, "bulitin not implemented");
}
static void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
func_t funcref = G_INT(OFS_PARM0);
char *funcname = NULL;
int args;
int builtinno;
if (prinst->GetFunctionInfo(prinst, funcref, &args, &builtinno, funcname, sizeof(funcname)))
{ //qc defines the function at least. nothing weird there...
if (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins)
{
if (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme)
G_FLOAT(OFS_RETURN) = false; //the builtin with that number isn't defined.
else
{
G_FLOAT(OFS_RETURN) = true; //its defined, within the sane range, mapped, everything. all looks good.
//we should probably go through the available builtins and validate that the qc's name matches what would be expected
//this is really intended more for builtins defined as #0 though, in such cases, mismatched assumptions are impossible.
}
}
else
G_FLOAT(OFS_RETURN) = false; //not a valid builtin (#0 builtins get remapped according to the function name)
}
else
{ //not valid somehow.
G_FLOAT(OFS_RETURN) = false;
}
}
@ -1875,6 +1902,7 @@ static struct {
int ebfsnum;
} BuiltinList[] = {
{"checkextension", PF_menu_checkextension, 1},
{"checkbuiltin", PF_checkbuiltin, 0},
{"error", PF_error, 2},
{"objerror", PF_nonfatalobjerror, 3},
{"print", PF_print, 4},
@ -2308,7 +2336,7 @@ void VARGS Menu_Abort (char *format, ...)
if (pr_menuqc_coreonerror.value)
{
char *buffer;
int size = 1024*1024*8;
size_t size = 1024*1024*8;
buffer = Z_Malloc(size);
menu_world.progs->save_ents(menu_world.progs, buffer, &size, size, 3);
COM_WriteFile("menucore.txt", FS_GAMEONLY, buffer, size);
@ -2504,7 +2532,7 @@ void MP_CoreDump_f(void)
}
{
int size = 1024*1024*8;
size_t size = 1024*1024*8;
char *buffer = BZ_Malloc(size);
menu_world.progs->save_ents(menu_world.progs, buffer, &size, size, 3);
COM_WriteFile("menucore.txt", FS_GAMEONLY, buffer, size);

View file

@ -1183,13 +1183,13 @@ R_PolyBlend
//bright flashes and stuff, game only, doesn't touch sbar
void R2D_PolyBlend (void)
{
if (!sw_blend[3])
if (!r_refdef.playerview->screentint[3])
return;
if (r_refdef.flags & RDF_NOWORLDMODEL)
return;
R2D_ImageColours (sw_blend[0], sw_blend[1], sw_blend[2], sw_blend[3]);
R2D_ImageColours (r_refdef.playerview->screentint[0], r_refdef.playerview->screentint[1], r_refdef.playerview->screentint[2], r_refdef.playerview->screentint[3]);
R2D_ScalePic(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, shader_polyblend);
R2D_ImageColours (1, 1, 1, 1);
}

View file

@ -766,7 +766,7 @@ entity_t *TraceLineR (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal)
vec3_t delta, ts, te;
entity_t *pe;
entity_t *result=NULL;
vec3_t axis[3];
// vec3_t axis[3];
vec3_t movemins, movemaxs;
memset (&trace, 0, sizeof(trace));

View file

@ -578,7 +578,7 @@ static void Surf_AddDynamicLightsColours (msurface_t *surf)
static void Surf_BuildDeluxMap (msurface_t *surf, qbyte *dest, unsigned int lmwidth, vec3_t *blocknormals)
static void Surf_BuildDeluxMap (model_t *wmodel, msurface_t *surf, qbyte *dest, unsigned int lmwidth, vec3_t *blocknormals)
{
int smax, tmax;
int i, j, size;
@ -601,7 +601,7 @@ static void Surf_BuildDeluxMap (msurface_t *surf, qbyte *dest, unsigned int lmwi
lightmap = surf->samples;
// set to full bright if no light data
if (!currentmodel->deluxdata)
if (!wmodel->deluxdata)
{
for (i=0 ; i<size ; i++)
{
@ -612,10 +612,10 @@ static void Surf_BuildDeluxMap (msurface_t *surf, qbyte *dest, unsigned int lmwi
goto store;
}
if (currentmodel->engineflags & MDLF_RGBLIGHTING)
deluxmap = surf->samples - currentmodel->lightdata + currentmodel->deluxdata;
if (wmodel->engineflags & MDLF_RGBLIGHTING)
deluxmap = surf->samples - wmodel->lightdata + wmodel->deluxdata;
else
deluxmap = (surf->samples - currentmodel->lightdata)*3 + currentmodel->deluxdata;
deluxmap = (surf->samples - wmodel->lightdata)*3 + wmodel->deluxdata;
// clear to no light
@ -629,9 +629,9 @@ static void Surf_BuildDeluxMap (msurface_t *surf, qbyte *dest, unsigned int lmwi
// add all the lightmaps
if (lightmap)
{
if (currentmodel->engineflags & MDLF_RGBLIGHTING)
if (wmodel->engineflags & MDLF_RGBLIGHTING)
{
deluxmap = surf->samples - currentmodel->lightdata + currentmodel->deluxdata;
deluxmap = surf->samples - wmodel->lightdata + wmodel->deluxdata;
for (maps = 0 ; maps < MAXQ1LIGHTMAPS && surf->styles[maps] != 255 ;
maps++)
@ -650,7 +650,7 @@ static void Surf_BuildDeluxMap (msurface_t *surf, qbyte *dest, unsigned int lmwi
}
else
{
deluxmap = (surf->samples - currentmodel->lightdata)*3 + currentmodel->deluxdata;
deluxmap = (surf->samples - wmodel->lightdata)*3 + wmodel->deluxdata;
for (maps = 0 ; maps < MAXQ1LIGHTMAPS && surf->styles[maps] != 255 ;
maps++)
@ -900,7 +900,7 @@ static void Surf_BuildLightMap (msurface_t *surf, qbyte *dest, qbyte *deluxdest,
}
if (currentmodel->deluxdata)
Surf_BuildDeluxMap(surf, deluxdest, lmwidth, blocknormals);
Surf_BuildDeluxMap(currentmodel, surf, deluxdest, lmwidth, blocknormals);
#ifdef PEXT_LIGHTSTYLECOL
if (lightmap_bytes == 4 || lightmap_bytes == 3)
@ -1170,7 +1170,7 @@ static void Surf_BuildLightMap (msurface_t *surf, qbyte *dest, qbyte *deluxdest,
}
static void Surf_BuildLightMap_Worker (msurface_t *surf, qbyte *dest, qbyte *deluxdest, stmap *stainsrc, int shift, int ambient, unsigned int lmwidth)
static void Surf_BuildLightMap_Worker (model_t *wmodel, msurface_t *surf, qbyte *dest, qbyte *deluxdest, stmap *stainsrc, int shift, int ambient, unsigned int lmwidth, int *d_lightstylevalue)
{
int smax, tmax;
int t;
@ -1202,8 +1202,8 @@ static void Surf_BuildLightMap_Worker (msurface_t *surf, qbyte *dest, qbyte *del
blocklights = BZ_Realloc(blocklights, maxblocksize * 3*sizeof(*blocklights));
}
if (currentmodel->deluxdata)
Surf_BuildDeluxMap(surf, deluxdest, lmwidth, blocknormals);
if (wmodel->deluxdata)
Surf_BuildDeluxMap(wmodel, surf, deluxdest, lmwidth, blocknormals);
#ifdef PEXT_LIGHTSTYLECOL
if (lightmap_bytes == 4 || lightmap_bytes == 3)
@ -1230,7 +1230,7 @@ static void Surf_BuildLightMap_Worker (msurface_t *surf, qbyte *dest, qbyte *del
blocklights[i] = r_fullbright.value*255*256;
}
}
else if (!currentmodel->lightdata)
else if (!wmodel->lightdata)
{
/*fullbright if map is not lit. but not overbright*/
for (i=0 ; i<size*3 ; i++)
@ -1265,7 +1265,7 @@ static void Surf_BuildLightMap_Worker (msurface_t *surf, qbyte *dest, qbyte *del
// add all the lightmaps
if (lightmap)
{
if (currentmodel->fromgame == fg_quake3) //rgb
if (wmodel->fromgame == fg_quake3) //rgb
{
/*q3 lightmaps are meant to be pre-built
this code is misguided, and ought never be executed anyway.
@ -1282,7 +1282,7 @@ static void Surf_BuildLightMap_Worker (msurface_t *surf, qbyte *dest, qbyte *del
}
}
}
else if (currentmodel->engineflags & MDLF_RGBLIGHTING) //rgb
else if (wmodel->engineflags & MDLF_RGBLIGHTING) //rgb
{
for (maps = 0 ; maps < MAXQ1LIGHTMAPS && surf->styles[maps] != 255 ;
maps++)
@ -1412,7 +1412,7 @@ static void Surf_BuildLightMap_Worker (msurface_t *surf, qbyte *dest, qbyte *del
#endif
{
// set to full bright if no light data
if (!surf->samples || !currentmodel->lightdata)
if (!surf->samples || !wmodel->lightdata)
{
for (i=0 ; i<size*3 ; i++)
{
@ -1435,7 +1435,7 @@ static void Surf_BuildLightMap_Worker (msurface_t *surf, qbyte *dest, qbyte *del
// add all the lightmaps
if (lightmap)
{
if (currentmodel->engineflags & MDLF_RGBLIGHTING) //rgb
if (wmodel->engineflags & MDLF_RGBLIGHTING) //rgb
for (maps = 0 ; maps < MAXQ1LIGHTMAPS && surf->styles[maps] != 255 ;
maps++)
{
@ -1570,7 +1570,7 @@ dynamic:
}
#ifdef THREADEDWORLD
static void Surf_RenderDynamicLightmaps_Worker (msurface_t *fa)
static void Surf_RenderDynamicLightmaps_Worker (model_t *wmodel, msurface_t *fa, int *d_lightstylevalue)
{
qbyte *base, *luxbase;
stmap *stainbase;
@ -1629,7 +1629,7 @@ dynamic:
base += (fa->light_t[0] * lm->width + fa->light_s[0]) * lightmap_bytes;
stainbase = lm->stainmaps;
stainbase += (fa->light_t[0] * lm->width + fa->light_s[0]) * 3;
Surf_BuildLightMap_Worker (fa, base, luxbase, stainbase, lightmap_shift, r_ambient.value*255, lm->width);
Surf_BuildLightMap_Worker (wmodel, fa, base, luxbase, stainbase, lightmap_shift, r_ambient.value*255, lm->width, d_lightstylevalue);
if (dlm)
{
@ -2496,9 +2496,7 @@ void Surf_SetupFrame(void)
//first scene is the 'main' scene and audio defaults to that (unless overridden later in the frame)
r_refdef.playerview->audio.defaulted = false;
VectorCopy(r_refdef.vieworg, r_refdef.playerview->audio.origin);
VectorCopy(vpn, r_refdef.playerview->audio.forward);
VectorCopy(vright, r_refdef.playerview->audio.right);
VectorCopy(vup, r_refdef.playerview->audio.up);
AngleVectors(r_refdef.viewangles, r_refdef.playerview->audio.forward,r_refdef.playerview->audio.right, r_refdef.playerview->audio.up);
if (r_viewcontents & FTECONTENTS_FLUID)
r_refdef.playerview->audio.inwater = true;
else
@ -2645,7 +2643,7 @@ struct webostate_s
int ebo;
size_t idxcount;
int numbatches;
double goodtime; //time that the webo is no longer 'good', resulting in an update (lightstyles).
int lightstylevalues[MAX_LIGHTSTYLES]; //when using workers that only reprocessing lighting at 10fps, things get too ugly when things go out of sync
batch_t *rbatches[SHADER_SORT_COUNT];
@ -2735,7 +2733,8 @@ static void Surf_SimpleWorld(struct webostate_s *es, qbyte *pvs)
mesh_t *mesh;
int l;
int fc = -r_framecount;
for (leaf = es->wmodel->leafs+es->wmodel->numclusters, l = es->wmodel->numclusters; l-- > 0; leaf--)
model_t *wmodel = es->wmodel;
for (leaf = wmodel->leafs+wmodel->numclusters, l = wmodel->numclusters; l-- > 0; leaf--)
{
if ((pvs[l>>3] & (1u<<(l&7))) && leaf->nummarksurfaces)
{
@ -2749,7 +2748,7 @@ static void Surf_SimpleWorld(struct webostate_s *es, qbyte *pvs)
int i;
struct wesbatch_s *eb;
surf->visframe = fc;
Surf_RenderDynamicLightmaps_Worker (surf);
Surf_RenderDynamicLightmaps_Worker (wmodel, surf, es->lightstylevalues);
mesh = surf->mesh;
eb = &es->batches[surf->sbatch->ebobatch];
@ -2841,20 +2840,29 @@ void Surf_DrawWorld (void)
Surf_LightmapShift(cl.worldmodel);
#ifdef THREADEDWORLD
if (r_dynamic.ival < 0 && !r_refdef.recurse && cl.worldmodel->type == mod_brush && cl.worldmodel->fromgame == fg_quake)
if ((r_dynamic.ival < 0 || cl.worldmodel->numbatches) && !r_refdef.recurse && cl.worldmodel->type == mod_brush && cl.worldmodel->fromgame == fg_quake)
{
int i = MAX_LIGHTSTYLES;
if (webostate && webostate->wmodel != cl.worldmodel)
{
R_DestroyWorldEBO(webostate);
webostate = NULL;
}
if (webostate && webostate->leaf[0] == r_viewleaf && webostate->leaf[1] == r_viewleaf2 && webostate->goodtime > cl.time)
if (webostate && !webogenerating)
for (i = 0; i < MAX_LIGHTSTYLES; i++)
{
if (webostate->lightstylevalues[i] != d_lightstylevalue[i])
break;
}
if (webostate && webostate->leaf[0] == r_viewleaf && webostate->leaf[1] == r_viewleaf2 && i == MAX_LIGHTSTYLES)
{
}
else
{
if (!webogenerating)
{
int i;
if (!cl.worldmodel->numbatches)
{
int sortid;
@ -2872,11 +2880,8 @@ void Surf_DrawWorld (void)
webogenerating->wmodel = cl.worldmodel;
webogenerating->leaf[0] = r_viewleaf;
webogenerating->leaf[1] = r_viewleaf2;
webogenerating->goodtime = cl.time; //stupid lightstyle animations.
if (r_lightstylesmooth.ival)
webogenerating->goodtime += 1.0/60;
else
webogenerating->goodtime += 0.1;
for (i = 0; i < MAX_LIGHTSTYLES; i++)
webogenerating->lightstylevalues[i] = d_lightstylevalue[i];
Q_strncpyz(webogenerating->dbgid, "webostate", sizeof(webogenerating->dbgid));
COM_AddWork(1, R_GenWorldEBO, webogenerating, NULL, 0, 0);
}

View file

@ -277,7 +277,7 @@ void R_GAlias_GenerateBatches(entity_t *e, struct batch_s **batches);
void R_LightArraysByte_BGR(const entity_t *entity, vecV_t *coords, byte_vec4_t *colours, int vertcount, vec3_t *normals);
void R_LightArrays(const entity_t *entity, vecV_t *coords, vec4_t *colours, int vertcount, vec3_t *normals, float scale);
void R_DrawSkyChain (struct batch_s *batch); /*called from the backend, and calls back into it*/
qboolean R_DrawSkyChain (struct batch_s *batch); /*called from the backend, and calls back into it*/
void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int width, unsigned int height); /*generate q1 sky texnums*/
void R_Clutter_Emit(struct batch_s **batches);
@ -485,6 +485,7 @@ void Media_RecordFrame (void);
qboolean Media_PausedDemo (qboolean fortiming);
int Media_Capturing (void);
double Media_TweekCaptureFrameTime(double oldtime, double time);
void Media_WriteCurrentTrack(sizebuf_t *buf);
void MYgluPerspective(double fovx, double fovy, double zNear, double zFar);

View file

@ -186,6 +186,7 @@ cvar_t scr_allowsnap = CVARF ("scr_allowsnap", "1",
CVAR_NOTFROMSERVER);
cvar_t scr_centersbar = CVAR ("scr_centersbar", "2");
cvar_t scr_centertime = CVAR ("scr_centertime", "2");
cvar_t scr_logcenterprint = CVARD ("con_logcenterprint", "1", "");
cvar_t scr_chatmodecvar = CVAR ("scr_chatmode", "0");
cvar_t scr_conalpha = CVARC ("scr_conalpha", "0.7",
Cvar_Limiter_ZeroToOne_Callback);
@ -756,6 +757,7 @@ void Renderer_Init(void)
Cvar_Register (&scr_turtlefps, SCREENOPTIONS);
Cvar_Register (&scr_showpause, SCREENOPTIONS);
Cvar_Register (&scr_centertime, SCREENOPTIONS);
Cvar_Register (&scr_logcenterprint, SCREENOPTIONS);
Cvar_Register (&scr_printspeed, SCREENOPTIONS);
Cvar_Register (&scr_allowsnap, SCREENOPTIONS);
Cvar_Register (&scr_consize, SCREENOPTIONS);

View file

@ -341,7 +341,7 @@ char *Get_Q2ConfigString(int i)
if (i >= Q2CS_MODELS && i < Q2CS_MODELS + Q2MAX_MODELS)
return cl.model_name [i-Q2CS_MODELS];
if (i >= Q2CS_SOUNDS && i < Q2CS_SOUNDS + Q2MAX_SOUNDS)
return cl.model_name [i-Q2CS_SOUNDS];
return cl.sound_name [i-Q2CS_SOUNDS];
if (i == Q2CS_AIRACCEL)
return "4";
if (i >= Q2CS_PLAYERSKINS && i < Q2CS_GENERAL+Q2MAX_GENERAL)
@ -352,7 +352,7 @@ char *Get_Q2ConfigString(int i)
//#define Q2CS_GENERAL (Q2CS_PLAYERSKINS +Q2MAX_CLIENTS)
return "";
}
void Sbar_ExecuteLayoutString (char *s)
void Sbar_ExecuteLayoutString (char *s, int seat)
{
int x, y;
int value;
@ -361,6 +361,7 @@ void Sbar_ExecuteLayoutString (char *s)
int pw, ph;
// q2clientinfo_t *ci;
mpic_t *p;
q2player_state_t *ps = &cl.q2frame.playerstate[seat];
if (cls.state != ca_active)
return;
@ -416,7 +417,7 @@ void Sbar_ExecuteLayoutString (char *s)
if (!strcmp(com_token, "pic"))
{ // draw a pic from a stat number
s = COM_Parse (s);
value = cl.q2frame.playerstate.stats[atoi(com_token)];
value = ps->stats[atoi(com_token)];
if (value >= Q2MAX_IMAGES || value < 0)
Host_EndGame ("Pic >= Q2MAX_IMAGES");
if (*Get_Q2ConfigString(Q2CS_IMAGES+value))
@ -435,9 +436,9 @@ void Sbar_ExecuteLayoutString (char *s)
int score, ping, time;
s = COM_Parse (s);
x = sbar_rect.width/2 - 160 + atoi(com_token);
x = sbar_rect.x + sbar_rect.width/2 - 160 + atoi(com_token);
s = COM_Parse (s);
y = sbar_rect.height/2 - 120 + atoi(com_token);
y = sbar_rect.y + sbar_rect.height/2 - 120 + atoi(com_token);
// SCR_AddDirtyPoint (x, y);
// SCR_AddDirtyPoint (x+159, y+31);
@ -474,9 +475,9 @@ void Sbar_ExecuteLayoutString (char *s)
char block[80];
s = COM_Parse (s);
x = sbar_rect.width/2 - 160 + atoi(com_token);
x = sbar_rect.x + sbar_rect.width/2 - 160 + atoi(com_token);
s = COM_Parse (s);
y = sbar_rect.height/2 - 120 + atoi(com_token);
y = sbar_rect.y + sbar_rect.height/2 - 120 + atoi(com_token);
// SCR_AddDirtyPoint (x, y);
// SCR_AddDirtyPoint (x+159, y+31);
@ -518,7 +519,7 @@ void Sbar_ExecuteLayoutString (char *s)
s = COM_Parse (s);
width = atoi(com_token);
s = COM_Parse (s);
value = cl.q2frame.playerstate.stats[atoi(com_token)];
value = ps->stats[atoi(com_token)];
SCR_DrawField (x, y, 0, width, value);
continue;
}
@ -528,7 +529,7 @@ void Sbar_ExecuteLayoutString (char *s)
int color;
width = 3;
value = cl.q2frame.playerstate.stats[Q2STAT_HEALTH];
value = ps->stats[Q2STAT_HEALTH];
if (value > 25)
color = 0; // green
else if (value > 0)
@ -536,7 +537,7 @@ void Sbar_ExecuteLayoutString (char *s)
else
color = 1;
if (cl.q2frame.playerstate.stats[Q2STAT_FLASHES] & 1)
if (ps->stats[Q2STAT_FLASHES] & 1)
{
p = Sbar_Q2CachePic("field_3");
if (p && R_GetShaderSizes(p, &pw, &ph, false)>0)
@ -552,7 +553,7 @@ void Sbar_ExecuteLayoutString (char *s)
int color;
width = 3;
value = cl.q2frame.playerstate.stats[Q2STAT_AMMO];
value = ps->stats[Q2STAT_AMMO];
if (value > 5)
color = 0; // green
else if (value >= 0)
@ -560,7 +561,7 @@ void Sbar_ExecuteLayoutString (char *s)
else
continue; // negative number = don't show
if (cl.q2frame.playerstate.stats[Q2STAT_FLASHES] & 4)
if (ps->stats[Q2STAT_FLASHES] & 4)
{
p = Sbar_Q2CachePic("field_3");
if (p && R_GetShaderSizes(p, &pw, &ph, false)>0)
@ -576,13 +577,13 @@ void Sbar_ExecuteLayoutString (char *s)
int color;
width = 3;
value = cl.q2frame.playerstate.stats[Q2STAT_ARMOR];
value = ps->stats[Q2STAT_ARMOR];
if (value < 1)
continue;
color = 0; // green
if (cl.q2frame.playerstate.stats[Q2STAT_FLASHES] & 2)
if (ps->stats[Q2STAT_FLASHES] & 2)
R2D_ScalePic (x, y, 64, 64, R2D_SafeCachePic("field_3"));
SCR_DrawField (x, y, color, width, value);
@ -596,7 +597,7 @@ void Sbar_ExecuteLayoutString (char *s)
index = atoi(com_token);
if (index < 0 || index >= Q2MAX_CONFIGSTRINGS)
Host_EndGame ("Bad stat_string index");
index = cl.q2frame.playerstate.stats[index];
index = ps->stats[index];
if (index < 0 || index >= Q2MAX_CONFIGSTRINGS)
Host_EndGame ("Bad stat_string index");
Draw_FunString (x, y, Get_Q2ConfigString(index));
@ -634,7 +635,7 @@ void Sbar_ExecuteLayoutString (char *s)
if (!strcmp(com_token, "if"))
{ // draw a number
s = COM_Parse (s);
value = cl.q2frame.playerstate.stats[atoi(com_token)];
value = ps->stats[atoi(com_token)];
if (!value)
{ // skip to endif
while (s && strcmp(com_token, "endif") )
@ -650,17 +651,20 @@ void Sbar_ExecuteLayoutString (char *s)
}
}
static void Sbar_Q2DrawInventory(void)
static void Sbar_Q2DrawInventory(int seat)
{
int keys[1], keymods[1];
char cmd[1024];
const char *boundkey;
unsigned int validlist[Q2MAX_ITEMS], rows, i, item, selected = cl.q2frame.playerstate.stats[Q2STAT_SELECTED_ITEM];
q2player_state_t *ps = &cl.q2frame.playerstate[seat];
unsigned int validlist[Q2MAX_ITEMS], rows, i, item, selected = ps->stats[Q2STAT_SELECTED_ITEM];
int first;
unsigned int maxrows = ((240-24*2-8*2)/8);
//draw background
float x = (vid.width - 256)/2;
float y = (vid.height - 240)/2;
float x = sbar_rect.x + (sbar_rect.width - 256)/2;
float y = sbar_rect.y + (sbar_rect.height - 240)/2;
if (y < sbar_rect.y)
y = sbar_rect.y; //try to fix small-res 3-way splitscreen slightly
R2D_ScalePic(x, y, 256, 240, Sbar_Q2CachePic("inventory"));
//move into the frame
x += 24;
@ -669,7 +673,7 @@ static void Sbar_Q2DrawInventory(void)
//figure out which items we have
for (i = 0, rows = 0, first = -1; i < Q2MAX_ITEMS; i++)
{
if (!cl.inventory[i])
if (!cl.inventory[seat][i])
continue;
if (i <= selected)
first = rows;
@ -693,7 +697,7 @@ static void Sbar_Q2DrawInventory(void)
else
boundkey = Key_KeynumToString(keys[0], keymods[0]);
Q_snprintfz(cmd, sizeof(cmd), "%6s %3i %s", boundkey, cl.inventory[item], Get_Q2ConfigString(Q2CS_ITEMS+item));
Q_snprintfz(cmd, sizeof(cmd), "%6s %3i %s", boundkey, cl.inventory[seat][item], Get_Q2ConfigString(Q2CS_ITEMS+item));
Draw_FunStringWidth(x, y, cmd, 256-24*2+8, false, item != selected); y+=8;
}
}
@ -2756,14 +2760,19 @@ void Sbar_Draw (playerview_t *pv)
#ifdef Q2CLIENT
if (cls.protocol == CP_QUAKE2)
{
int seat = pv - cl.playerview;
if (seat >= cl.splitclients)
seat = cl.splitclients-1;
if (seat < 0)
seat = 0;
sbar_rect = r_refdef.grect;
R2D_ImageColours(1, 1, 1, 1);
if (*cl.q2statusbar)
Sbar_ExecuteLayoutString(cl.q2statusbar);
if (*cl.q2layout && (cl.q2frame.playerstate.stats[Q2STAT_LAYOUTS] & 1))
Sbar_ExecuteLayoutString(cl.q2layout);
if (cl.q2frame.playerstate.stats[Q2STAT_LAYOUTS] & 2)
Sbar_Q2DrawInventory();
Sbar_ExecuteLayoutString(cl.q2statusbar, seat);
if (*cl.q2layout && (cl.q2frame.playerstate[seat].stats[Q2STAT_LAYOUTS] & 1))
Sbar_ExecuteLayoutString(cl.q2layout[seat], seat);
if (cl.q2frame.playerstate[seat].stats[Q2STAT_LAYOUTS] & 2)
Sbar_Q2DrawInventory(seat);
return;
}
#endif

View file

@ -49,7 +49,7 @@ qboolean SCR_RSShot (void);
void GLSCR_UpdateScreen (void);
#endif
void SCR_ImageName (char *mapname);
void SCR_ImageName (const char *mapname);
//this stuff is internal to the screen systems.
void RSpeedShow(void);

View file

@ -138,6 +138,7 @@ cvar_t snd_voip_micamp = CVARAFDC("cl_voip_micamp", "2", NULL, CVAR_ARCHIVE, "
cvar_t snd_voip_codec = CVARAFDC("cl_voip_codec", "0", NULL, CVAR_ARCHIVE, "0: speex(@11khz). 1: raw. 2: opus. 3: speex(@8khz). 4: speex(@16). 5:speex(@32).", 0);
cvar_t snd_voip_noisefilter = CVARAFDC("cl_voip_noisefilter", "1", NULL, CVAR_ARCHIVE, "Enable the use of the noise cancelation filter.", 0);
cvar_t snd_voip_autogain = CVARAFDC("cl_voip_autogain", "0", NULL, CVAR_ARCHIVE, "Attempts to normalize your voice levels to a standard level. Useful for lazy people, but interferes with voice activation levels.", 0);
cvar_t snd_voip_bitrate = CVARAFDC("cl_voip_bitrate", "0", NULL, CVAR_ARCHIVE, "For codecs with non-specific bitrates, this specifies the target bitrate to use (in kb).", 0);
#endif
extern vfsfile_t *rawwritefile;
@ -297,12 +298,14 @@ static struct
float voiplevel; /*your own voice level*/
unsigned int dumps; /*trigger a new generation thing after a bit*/
unsigned int keeps; /*for vad_delay*/
int curbitrate;
snd_capture_driver_t *cdriver;/*capture driver's functions*/
void *cdriverctx; /*capture driver context*/
} s_voip;
#define OPUS_APPLICATION_VOIP 2048
#define OPUS_SET_BITRATE_REQUEST 4002
#define OPUS_RESET_STATE 4028
#ifdef OPUS_STATIC
#include "opus.h"
@ -1024,18 +1027,20 @@ void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf)
if (!s_voip.encoder)
return;
// opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate_bps));
// opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
// opus_encoder_ctl(enc, OPUS_SET_VBR(use_vbr));
// opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cvbr));
// opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
// opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(use_inbandfec));
// opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(forcechannels));
// opus_encoder_ctl(enc, OPUS_SET_DTX(use_dtx));
// opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
s_voip.curbitrate = 0;
// opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&skip));
// opus_encoder_ctl(enc, OPUS_SET_LSB_DEPTH(16));
// opus_encoder_ctl(s_voip.encoder, OPUS_SET_BITRATE(bitrate_bps));
// opus_encoder_ctl(s_voip.encoder, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
// opus_encoder_ctl(s_voip.encoder, OPUS_SET_VBR(use_vbr));
// opus_encoder_ctl(s_voip.encoder, OPUS_SET_VBR_CONSTRAINT(cvbr));
// opus_encoder_ctl(s_voip.encoder, OPUS_SET_COMPLEXITY(complexity));
// opus_encoder_ctl(s_voip.encoder, OPUS_SET_INBAND_FEC(use_inbandfec));
// opus_encoder_ctl(s_voip.encoder, OPUS_SET_FORCE_CHANNELS(forcechannels));
// opus_encoder_ctl(s_voip.encoder, OPUS_SET_DTX(use_dtx));
// opus_encoder_ctl(s_voip.encoder, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
// opus_encoder_ctl(s_voip.encoder, OPUS_GET_LOOKAHEAD(&skip));
// opus_encoder_ctl(s_voip.encoder, OPUS_SET_LSB_DEPTH(16));
break;
@ -1192,6 +1197,7 @@ void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf)
{
//opus rtp only supports/allows a single chunk in each packet.
int frames;
int nrate;
//densely pack the frames.
start = (short*)(s_voip.capturebuf + encpos);
frames = (s_voip.capturepos-encpos)/2;
@ -1213,6 +1219,16 @@ void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf)
Con_Printf("invalid Opus frame size\n");
frames = 0;
}
nrate = snd_voip_bitrate.value * 1000;
if (nrate != s_voip.curbitrate)
{
s_voip.curbitrate = nrate;
if (nrate == 0)
nrate = -1000;
qopus_encoder_ctl(s_voip.encoder, OPUS_SET_BITRATE_REQUEST, (int)nrate);
}
// Con_Printf("Encoding %i frames", frames);
level += S_Voip_Preprocess(start, frames, micamp);
len = qopus_encode(s_voip.encoder, start, frames, outbuf+outpos, sizeof(outbuf) - outpos);
@ -1660,6 +1676,8 @@ static soundcardinfo_t *SNDDMA_Init(char *driver, char *device, int seat)
else
snd_speed = sc->sn.speed;
if (sc->seat == -1 && sc->ListenerUpdate)
sc->seat = 0; //hardware rendering won't cope with seat=-1
sc->next = sndcardinfo;
sndcardinfo = sc;
return sc;
@ -1757,7 +1775,7 @@ void S_Startup (void)
sep = strchr(com_token, ':');
if (sep)
*sep++ = 0;
SNDDMA_Init(com_token, sep, 0);
SNDDMA_Init(com_token, sep, -1);
}
}
if (!sndcardinfo && !nodefault)
@ -1766,7 +1784,7 @@ void S_Startup (void)
INS_SetupControllerAudioDevices();
#endif
if (!sndcardinfo)
SNDDMA_Init(NULL, NULL, 0);
SNDDMA_Init(NULL, NULL, -1);
}
sound_started = true;
@ -2213,25 +2231,25 @@ SND_PickChannel
*/
channel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel)
{
int ch_idx;
int oldestpos;
int oldest;
int ch_idx;
int oldestpos;
int oldest;
// Check for replacement sound, or find the best one to replace
oldest = -1;
oldestpos = -1;
for (ch_idx=DYNAMIC_FIRST; ch_idx < DYNAMIC_STOP ; ch_idx++)
{
oldest = -1;
oldestpos = -1;
for (ch_idx=DYNAMIC_FIRST; ch_idx < DYNAMIC_STOP ; ch_idx++)
{
if (entchannel != 0 // channel 0 never overrides
&& sc->channel[ch_idx].entnum == entnum
&& (sc->channel[ch_idx].entchannel == entchannel || entchannel == -1))
&& sc->channel[ch_idx].entchannel == entchannel)
{ // always override sound from same entity
oldest = ch_idx;
break;
}
// don't let monster sounds override player sounds
if (sc->channel[ch_idx].entnum == listener[sc->seat].entnum && entnum != listener[sc->seat].entnum && sc->channel[ch_idx].sfx)
if (sc->seat != -1 && sc->channel[ch_idx].entnum == listener[sc->seat].entnum && entnum != listener[sc->seat].entnum && sc->channel[ch_idx].sfx)
continue;
if (!sc->channel[ch_idx].sfx)
@ -2254,15 +2272,10 @@ channel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel)
if (sc->total_chans <= oldest)
sc->total_chans = oldest+1;
return &sc->channel[oldest];
return &sc->channel[oldest];
}
/*
=================
SND_Spatialize
=================
*/
void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch)
static void SND_AccumulateSpacialization(soundcardinfo_t *sc, channel_t *ch, vec3_t origin)
{
vec3_t listener_vec;
vec_t dist;
@ -2270,27 +2283,141 @@ void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch)
vec3_t world_vec;
int i, v;
float volscale;
int seat;
if (ch->flags & CF_ABSVOLUME)
volscale = 1;
else
volscale = volume.value * voicevolumemod;
if (sc->seat == -1)
{
seat = 0;
VectorSubtract(origin, listener[seat].origin, world_vec);
dist = DotProduct(world_vec,world_vec);
for (i = 1; i < cl.splitclients; i++)
{
VectorSubtract(origin, listener[i].origin, world_vec);
scale = DotProduct(world_vec,world_vec);
if (scale < dist)
{
dist = scale;
seat = i;
}
}
}
else
{
seat = sc->seat;
}
// anything coming from the view entity will always be full volume
if (ch->entnum == listener[seat].entnum)
{
v = ch->master_vol * (ruleset_allow_localvolume.value ? snd_playersoundvolume.value : 1) * volscale;
v = bound(0, v, 255);
for (i = 0; i < sc->sn.numchannels; i++)
ch->vol[i] = v;
return;
}
// calculate stereo seperation and distance attenuation
VectorSubtract(origin, listener[seat].origin, world_vec);
dist = VectorNormalize(world_vec) * ch->dist_mult;
if (ch->flags & CF_NOSPACIALISE)
{
scale = 1;
scale = (1.0 - dist) * scale;
v = ch->master_vol * scale * volscale;
for (i = 0; i < sc->sn.numchannels; i++)
ch->vol[i] += bound(0, v, 255);
return;
}
//rotate the world_vec into listener space, so that the audio direction stored in the speakerdir array can be used directly.
listener_vec[0] = DotProduct(listener[seat].forward, world_vec);
listener_vec[1] = DotProduct(listener[seat].right, world_vec);
listener_vec[2] = DotProduct(listener[seat].up, world_vec);
if (snd_leftisright.ival)
listener_vec[1] = -listener_vec[1];
for (i = 0; i < sc->sn.numchannels; i++)
{
scale = 1 + DotProduct(listener_vec, sc->speakerdir[i]);
scale = (1.0 - dist) * scale * sc->dist[i];
v = ch->master_vol * scale * volscale;
ch->vol[i] += bound(0, v, 255);
}
}
/*
=================
SND_Spatialize
=================
*/
static void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch)
{
vec3_t listener_vec;
vec_t dist;
vec_t scale;
vec3_t world_vec;
int i, v;
float volscale;
int seat;
/*
if ((ch->flags & CF_FOLLOW) && ch->entnum > 0 && ch->entnum < cl.maxlerpents)
{ //sounds following ents should update their position to match that ent's position.
//its important that they do not snap back to where they were if the entity vanishes, so we just overwrite the channel origin for that. its simpler.
lerpents_t *le = cl.lerpents+ch->entnum;
if (le->sequence == cl.lerpentssequence)
VectorCopy(le->origin, ch->origin); //fixme: bmodels should use their center rather than their origin. check le->state->solid?
//FIXME: update rate to provide doppler
}
*/
//sounds with absvolume ignore all volume etc cvars+settings
if (ch->flags & CF_ABSVOLUME)
volscale = 1;
else
volscale = volume.value * voicevolumemod;
if (sc->seat == -1)
{
seat = 0;
VectorSubtract(ch->origin, listener[seat].origin, world_vec);
dist = DotProduct(world_vec,world_vec);
for (i = 1; i < cl.splitclients; i++)
{
VectorSubtract(ch->origin, listener[i].origin, world_vec);
scale = DotProduct(world_vec,world_vec);
if (scale < dist)
{
dist = scale;
seat = i;
}
}
}
else
{
seat = sc->seat;
}
// anything coming from the view entity will always be full volume
if (ch->entnum == listener[sc->seat].entnum)
// (no, I don't like this hack)
if (ch->entnum == listener[seat].entnum)
{
v = ch->master_vol * (ruleset_allow_localvolume.value ? snd_playersoundvolume.value : 1) * volscale;
v = bound(0, v, 255);
for (i = 0; i < sc->sn.numchannels; i++)
{
ch->vol[i] = v;
}
return;
}
// calculate stereo seperation and distance attenuation
VectorSubtract(ch->origin, listener[sc->seat].origin, world_vec);
VectorSubtract(ch->origin, listener[seat].origin, world_vec);
dist = VectorNormalize(world_vec) * ch->dist_mult;
@ -2305,9 +2432,9 @@ void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch)
}
//rotate the world_vec into listener space, so that the audio direction stored in the speakerdir array can be used directly.
listener_vec[0] = DotProduct(listener[sc->seat].forward, world_vec);
listener_vec[1] = DotProduct(listener[sc->seat].right, world_vec);
listener_vec[2] = DotProduct(listener[sc->seat].up, world_vec);
listener_vec[0] = DotProduct(listener[seat].forward, world_vec);
listener_vec[1] = DotProduct(listener[seat].right, world_vec);
listener_vec[2] = DotProduct(listener[seat].up, world_vec);
if (snd_leftisright.ival)
listener_vec[1] = -listener_vec[1];
@ -2352,7 +2479,14 @@ static void S_UpdateSoundCard(soundcardinfo_t *sc, qboolean updateonly, channel_
memset (target_chan, 0, sizeof(*target_chan));
if (!origin)
{
VectorCopy(listener[sc->seat].origin, target_chan->origin);
if (sc->seat == -1)
{
VectorClear(target_chan->origin);
attenuation = 0;
flags |= CF_NOSPACIALISE;
}
else
VectorCopy(listener[sc->seat].origin, target_chan->origin);
}
else
{
@ -2763,9 +2897,9 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc)
{
mleaf_t *l;
float vol, oldvol;
int ambient_channel;
channel_t *chan;
int i;
int ambientlevel[NUM_AMBIENTS];
if (!snd_ambient)
return;
@ -2835,55 +2969,67 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc)
if (!cl.worldmodel || cl.worldmodel->type != mod_brush || cl.worldmodel->fromgame != fg_quake || cl.worldmodel->loadstate != MLS_LOADED)
return;
l = Q1BSP_LeafForPoint(cl.worldmodel, listener[sc->seat].origin);
if (!l || ambient_level.value <= 0)
for (i = 0; i < NUM_AMBIENTS; i++)
ambientlevel[i] = 0;
if (ambient_level.value)
{
for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
if (sc->seat < 0)
{
chan = &sc->channel[AMBIENT_FIRST+ambient_channel];
chan->sfx = NULL;
if (sc->ChannelUpdate)
sc->ChannelUpdate(sc, chan, true);
int seat = max(1,cl.splitclients);
while(seat --> 0)
{
l = Q1BSP_LeafForPoint(cl.worldmodel, listener[seat].origin);
if (!l)
continue;
for (i = 0; i < NUM_AMBIENTS; i++)
ambientlevel[i] = max(ambientlevel[i], l->ambient_sound_level[i]);
}
}
else
{
l = Q1BSP_LeafForPoint(cl.worldmodel, listener[sc->seat].origin);
if (l)
for (i = 0; i < NUM_AMBIENTS; i++)
ambientlevel[i] = l->ambient_sound_level[i];
}
return;
}
for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
for (i = 0 ; i< NUM_AMBIENTS ; i++)
{
static float level[NUM_AMBIENTS];
chan = &sc->channel[AMBIENT_FIRST+ambient_channel];
chan->sfx = ambient_sfx[AMBIENT_FIRST+ambient_channel];
chan->entnum = -1;
chan = &sc->channel[AMBIENT_FIRST+i];
chan->sfx = ambient_sfx[AMBIENT_FIRST+i];
chan->entnum = 0;
chan->flags = CF_FORCELOOP | CF_NOSPACIALISE;
chan->rate = 1<<PITCHSHIFT;
VectorCopy(listener[sc->seat].origin, chan->origin);
VectorClear(chan->origin);
vol = ambient_level.value * l->ambient_sound_level[ambient_channel];
vol = ambient_level.value * ambientlevel[i];
if (vol < 8)
vol = 0;
oldvol = level[ambient_channel];
oldvol = sc->ambientlevels[i];
// don't adjust volume too fast
if (level[ambient_channel] < vol)
if (sc->ambientlevels[i] < vol)
{
level[ambient_channel] += host_frametime * ambient_fade.value;
if (level[ambient_channel] > vol)
level[ambient_channel] = vol;
sc->ambientlevels[i] += host_frametime * ambient_fade.value;
if (sc->ambientlevels[i] > vol)
sc->ambientlevels[i] = vol;
}
else if (chan->master_vol > vol)
{
level[ambient_channel] -= host_frametime * ambient_fade.value;
if (level[ambient_channel] < vol)
level[ambient_channel] = vol;
sc->ambientlevels[i] -= host_frametime * ambient_fade.value;
if (sc->ambientlevels[i] < vol)
sc->ambientlevels[i] = vol;
}
chan->master_vol = level[ambient_channel];
chan->master_vol = sc->ambientlevels[i];
chan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = bound(0, chan->master_vol * (volume.value*voicevolumemod), 255);
if (sc->ChannelUpdate)
sc->ChannelUpdate(sc, chan, (oldvol == 0) ^ (level[ambient_channel] == 0));
sc->ChannelUpdate(sc, chan, (oldvol == 0) ^ (sc->ambientlevels[i] == 0));
}
}
@ -2897,14 +3043,14 @@ Called once each time through the main loop
void S_UpdateListener(int seat, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, qboolean underwater)
{
soundcardinfo_t *sc;
listener[seat].entnum = cl.playerview[seat].playernum+1;
listener[seat].entnum = cl.playerview[seat].viewentity;
VectorCopy(origin, listener[seat].origin);
VectorCopy(forward, listener[seat].forward);
VectorCopy(right, listener[seat].right);
VectorCopy(up, listener[seat].up);
for (sc = sndcardinfo; sc; sc=sc->next)
if (sc->SetWaterDistortion && sc->seat == seat)
if (sc->SetWaterDistortion && (sc->seat == seat || (sc->seat == -1 && seat == 0)))
sc->SetWaterDistortion(sc, underwater);
}
@ -2916,6 +3062,102 @@ void S_GetListenerInfo(int seat, float *origin, float *forward, float *right, fl
VectorCopy(listener[seat].up, up);
}
static void S_Q2_AddEntitySounds(soundcardinfo_t *sc)
{
vec3_t positions[2048];
int entnums[countof(positions)];
sfx_t *sounds[countof(positions)];
unsigned int count;
unsigned int j;
channel_t *c;
#ifdef Q2CLIENT
if (cls.protocol == CP_QUAKE2)
count = CLQ2_GatherSounds(positions, entnums, sounds, countof(sounds));
else
#endif
return;
while(count --> 0)
{
sfx_t *sfx = sounds[count];
if (!sfx)
continue;
if (sfx->loadstate == SLS_NOTLOADED)
S_LoadSound(sfx);
if (sfx->loadstate != SLS_LOADED)
continue; //not ready yet
if (sc->ChannelUpdate)
{
for (c = NULL, j=DYNAMIC_FIRST; j < DYNAMIC_STOP ; j++)
{
if (sc->channel[j].entnum == entnums[count] && !sc->channel[j].entchannel && (sc->channel[j].flags & CF_AUTOSOUND))
{
c = &sc->channel[j];
break;
}
}
}
else
{
for (c = NULL, j=DYNAMIC_FIRST; j < DYNAMIC_STOP ; j++)
{
if (sc->channel[j].sfx == sfx && (sc->channel[j].flags & CF_AUTOSOUND))
{
c = &sc->channel[j];
break;
}
}
}
if (!c)
{
c = SND_PickChannel(sc, 0, 0);
if (!c)
continue;
c->flags = CF_AUTOSOUND|CF_FORCELOOP;
c->entnum = sc->ChannelUpdate?entnums[count]:0;
c->entchannel = 0;
c->dist_mult = 3 / sound_nominal_clip_dist;
c->master_vol = 255 * 1;
c->pos = 0<<PITCHSHIFT; //q2 does weird stuff with the pos. we just forceloop and detect when it became irrelevant. this is required for stream decoding or openal
c->rate = 1<<PITCHSHIFT;
for (j = 0; j < countof(c->vol); j++)
c->vol[j] = 0;
c->sfx = NULL;
}
if (sc->ChannelUpdate)
{ //hardware mixing doesn't support merging
SND_Spatialize(sc, c);
}
else
{ //merge with any other ents, if we can
for (j = 0; j <= count; j++)
{
if (sounds[j] == sfx)
{
sounds[j] = NULL;
SND_AccumulateSpacialization(sc, c, positions[j]);
}
}
}
if (!c->sfx)
{
for (j = 0; j < countof(c->vol); j++)
if (c->vol[j])
break;
if (j == countof(c->vol))
c->sfx = NULL; //err, never mind
else
{
c->sfx = sfx;
if (sc->ChannelUpdate)
sc->ChannelUpdate(sc, c, true);
}
}
}
}
static void S_UpdateCard(soundcardinfo_t *sc)
{
int i, j;
@ -2949,6 +3191,17 @@ static void S_UpdateCard(soundcardinfo_t *sc)
{
if (!ch->sfx)
continue;
if (ch->flags & CF_AUTOSOUND)
{
if (!ch->vol[0] && !ch->vol[1] && !ch->vol[2] && !ch->vol[3] && !ch->vol[4] && !ch->vol[5])
{
ch->sfx = NULL;
if (sc->ChannelUpdate)
sc->ChannelUpdate(sc, ch, true);
}
ch->vol[0] = ch->vol[1] = ch->vol[2] = ch->vol[3] = ch->vol[4] = ch->vol[5] = 0;
continue;
}
if (sc->ChannelUpdate)
{
@ -3004,6 +3257,8 @@ static void S_UpdateCard(soundcardinfo_t *sc)
}
}
S_Q2_AddEntitySounds(sc);
//
// debugging output
//
@ -3186,7 +3441,7 @@ console functions
*/
void S_Play(void)
{
{ //plays a sound located around the player
int i;
char name[256];
sfx_t *sfx;
@ -3202,7 +3457,7 @@ void S_Play(void)
else
Q_strncpyz(name, Cmd_Argv(i), sizeof(name));
sfx = S_PrecacheSound(name);
S_StartSound(listener[0].entnum, -1, sfx, vec3_origin, 1.0, 0.0, 0, 0, CF_NOSPACIALISE);
S_StartSound(0, -1, sfx, NULL, 1.0, 0.0, 0, 0, CF_NOSPACIALISE);
i++;
}
}
@ -3226,7 +3481,7 @@ void S_PlayVol(void)
Q_strncpy(name, Cmd_Argv(i), sizeof(name));
sfx = S_PrecacheSound(name);
vol = Q_atof(Cmd_Argv(i+1));
S_StartSound(listener[0].entnum, -1, sfx, vec3_origin, vol, 0.0, 0, 0, CF_NOSPACIALISE);
S_StartSound(0, -1, sfx, NULL, vol, 0.0, 0, 0, CF_NOSPACIALISE);
i+=2;
}
}
@ -3297,7 +3552,7 @@ void S_LocalSound (const char *sound)
Con_Printf ("S_LocalSound: can't cache %s\n", sound);
return;
}
S_StartSound (0, -1, sfx, vec3_origin, 1, 1, 0, 0, CF_NOSPACIALISE);
S_StartSound (0, -1, sfx, NULL, 1, 0, 0, 0, CF_NOSPACIALISE);
}
@ -3505,7 +3760,7 @@ void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels,
if (c)
{
c->flags = CF_ABSVOLUME|CF_NOSPACIALISE;
c->entnum = -1;
c->entnum = 0;
c->entchannel = 0;
c->dist_mult = 0;
c->master_vol = 255 * volume;

View file

@ -91,10 +91,13 @@ typedef struct
#define CF_RELIABLE 1 // serverside only. yeah, evil. screw you.
#define CF_FORCELOOP 2 // forces looping. set on static sounds.
#define CF_NOSPACIALISE 4 // these sounds are played at a fixed volume
#define CF_NOSPACIALISE 4 // these sounds are played at a fixed volume in both speakers, but still gets quieter with distance.
//#define CF_PAUSED 8 // rate = 0. or something.
#define CF_ABSVOLUME 16 // ignores volume cvar.
#define CF_UNICAST 256 // serverside only. the sound is sent to msg_entity only.
#define CF_AUTOSOUND 512 // generated from q2 entities, which avoids breaking regular sounds, using it outside the sound system will probably break things.
typedef struct
{
sfx_t *sfx; // sfx number
@ -188,9 +191,6 @@ qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth, int i
// picks a channel based on priorities, empty slots, number of channels
channel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel);
// spatializes a channel
void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch);
void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int insamps, void *out, int outrate, int outwidth, int outchannels, int resampstyle);
// restart entire sound subsystem (doesn't flush old sounds, so make sure that happens)
@ -296,6 +296,8 @@ struct soundcardinfo_s { //windows has one defined AFTER directsound
channel_t channel[MAX_CHANNELS];
int total_chans;
float ambientlevels[NUM_AMBIENTS]; //we use a float instead of the channel's int volume value to avoid framerate dependancies with slow transitions.
//mixer
volatile dma_t sn; //why is this volatile?
qboolean inactive_sound; //continue mixing for this card even when the window isn't active.

View file

@ -1553,14 +1553,25 @@ void VARGS Sys_Printf (char *fmt, ...)
out = wide;
in = msg;
wlen = 0;
for (in = msg; in < end; )
for (in = msg; in < end && wlen+3 < countof(wide); )
{
unsigned int flags, cp;
in = Font_Decode(in, &flags, &cp);
if (!(flags & CON_HIDDEN))
{
*out++ = COM_DeQuake(cp);
wlen++;
cp = COM_DeQuake(cp);
if (cp > 0xffff)
{
cp -= 0x10000;
*out++ = 0xD800 | (cp>>10);
*out++ = 0xDC00 | (cp&0x3ff);
wlen += 2;
}
else
{
*out++ = cp;
wlen++;
}
}
}
*out = 0;
@ -1788,7 +1799,7 @@ void Sys_SaveClipboard(char *text)
return;
EmptyClipboard();
if (com_parseutf8.ival > 0)
if (WinNT)
{
glob = GlobalAlloc(GMEM_MOVEABLE, (strlen(text) + 1)*4);
if (glob)
@ -1799,7 +1810,7 @@ void Sys_SaveClipboard(char *text)
int error;
while(*text)
{
codepoint = utf8_decode(&error, text, &text);
codepoint = unicode_decode(&error, text, &text, false);
if (codepoint == '\n')
{ //windows is stupid and annoying.
*tempw++ = '\r';
@ -1824,7 +1835,7 @@ void Sys_SaveClipboard(char *text)
}
else
{
glob = GlobalAlloc(GMEM_MOVEABLE, strlen(text) + 1);
glob = GlobalAlloc(GMEM_MOVEABLE, strlen(text)*2 + 1);
if (glob)
{
//yes, quake chars will get mangled horribly.
@ -1834,8 +1845,10 @@ void Sys_SaveClipboard(char *text)
int error;
while (*text)
{
//NOTE: should be \r\n and not just \n
*temp++ = utf8_decode(&error, text, &text) & 0xff;
codepoint = unicode_decode(&error, text, &text, false);
if (codepoint == '\n')
*temp++ = '\r';
*temp++ = codepoint;
}
*temp = 0;
strcpy(temp, text);

View file

@ -324,7 +324,6 @@ qbyte gammatable[256]; // palette is sent through this
unsigned short ramps[3][256];
//extern qboolean gammaworks;
float sw_blend[4]; // rgba 0.0 - 1.0
float hw_blend[4]; // rgba 0.0 - 1.0
/*
void BuildGammaTable (float g)
@ -459,29 +458,29 @@ void V_ParseDamage (playerview_t *pv)
if (v_damagecshift.value >= 0)
count *= v_damagecshift.value;
cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
cl.cshifts[CSHIFT_DAMAGE].percent = 0;
if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
cl.cshifts[CSHIFT_DAMAGE].percent = 150;
pv->cshifts[CSHIFT_DAMAGE].percent += 3*count;
if (pv->cshifts[CSHIFT_DAMAGE].percent < 0)
pv->cshifts[CSHIFT_DAMAGE].percent = 0;
if (pv->cshifts[CSHIFT_DAMAGE].percent > 150)
pv->cshifts[CSHIFT_DAMAGE].percent = 150;
if (armor > blood)
{
cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
pv->cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
pv->cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
pv->cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
}
else if (armor)
{
cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
pv->cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
pv->cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
pv->cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
}
else
{
cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
pv->cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
pv->cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
pv->cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
}
//
@ -510,6 +509,7 @@ V_cshift_f
void V_cshift_f (void)
{
int r, g, b, p;
playerview_t *pv = &cl.playerview[CL_TargettedSplit(true)];
r = g = b = p = 0;
@ -551,10 +551,10 @@ void V_cshift_f (void)
Con_DPrintf("broken v_cshift from gamecode\n");
// ensure we always clear out or set for nehahra
cl.cshifts[CSHIFT_SERVER].destcolor[0] = r;
cl.cshifts[CSHIFT_SERVER].destcolor[1] = g;
cl.cshifts[CSHIFT_SERVER].destcolor[2] = b;
cl.cshifts[CSHIFT_SERVER].percent = p;
pv->cshifts[CSHIFT_SERVER].destcolor[0] = r;
pv->cshifts[CSHIFT_SERVER].destcolor[1] = g;
pv->cshifts[CSHIFT_SERVER].destcolor[2] = b;
pv->cshifts[CSHIFT_SERVER].percent = p;
return;
}
@ -580,6 +580,7 @@ When you run over an item, the server sends this command
*/
void V_BonusFlash_f (void)
{
playerview_t *pv = &cl.playerview[CL_TargettedSplit(true)];
float frac;
if (!gl_cshiftenabled.ival)
frac = 0;
@ -601,22 +602,23 @@ void V_BonusFlash_f (void)
{
if (Cmd_Argc() > 1)
{ //this is how I understand DP expects them.
cl.cshifts[CSHIFT_BONUS].destcolor[0] = atof(Cmd_Argv(1))*255;
cl.cshifts[CSHIFT_BONUS].destcolor[1] = atof(Cmd_Argv(2))*255;
cl.cshifts[CSHIFT_BONUS].destcolor[2] = atof(Cmd_Argv(3))*255;
cl.cshifts[CSHIFT_BONUS].percent = atof(Cmd_Argv(4))*255*frac;
pv->cshifts[CSHIFT_BONUS].destcolor[0] = atof(Cmd_Argv(1))*255;
pv->cshifts[CSHIFT_BONUS].destcolor[1] = atof(Cmd_Argv(2))*255;
pv->cshifts[CSHIFT_BONUS].destcolor[2] = atof(Cmd_Argv(3))*255;
pv->cshifts[CSHIFT_BONUS].percent = atof(Cmd_Argv(4))*255*frac;
}
else
{
cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
cl.cshifts[CSHIFT_BONUS].percent = 50*frac;
pv->cshifts[CSHIFT_BONUS].destcolor[0] = 215;
pv->cshifts[CSHIFT_BONUS].destcolor[1] = 186;
pv->cshifts[CSHIFT_BONUS].destcolor[2] = 69;
pv->cshifts[CSHIFT_BONUS].percent = 50*frac;
}
}
}
void V_DarkFlash_f (void)
{
playerview_t *pv = &cl.playerview[CL_TargettedSplit(true)];
float frac;
if (!gl_cshiftenabled.ival)
frac = 0;
@ -624,13 +626,14 @@ void V_DarkFlash_f (void)
frac = 1;
frac *= gl_cshiftpercent.value / 100.0;
cl.cshifts[CSHIFT_BONUS].destcolor[0] = 0;
cl.cshifts[CSHIFT_BONUS].destcolor[1] = 0;
cl.cshifts[CSHIFT_BONUS].destcolor[2] = 0;
cl.cshifts[CSHIFT_BONUS].percent = 255*frac;
pv->cshifts[CSHIFT_BONUS].destcolor[0] = 0;
pv->cshifts[CSHIFT_BONUS].destcolor[1] = 0;
pv->cshifts[CSHIFT_BONUS].destcolor[2] = 0;
pv->cshifts[CSHIFT_BONUS].percent = 255*frac;
}
void V_WhiteFlash_f (void)
{
playerview_t *pv = &cl.playerview[CL_TargettedSplit(true)];
float frac;
if (!gl_cshiftenabled.ival)
frac = 0;
@ -638,10 +641,10 @@ void V_WhiteFlash_f (void)
frac = 1;
frac *= gl_cshiftpercent.value / 100.0;
cl.cshifts[CSHIFT_BONUS].destcolor[0] = 255;
cl.cshifts[CSHIFT_BONUS].destcolor[1] = 255;
cl.cshifts[CSHIFT_BONUS].destcolor[2] = 255;
cl.cshifts[CSHIFT_BONUS].percent = 255*frac;
pv->cshifts[CSHIFT_BONUS].destcolor[0] = 255;
pv->cshifts[CSHIFT_BONUS].destcolor[1] = 255;
pv->cshifts[CSHIFT_BONUS].destcolor[2] = 255;
pv->cshifts[CSHIFT_BONUS].percent = 255*frac;
}
/*
@ -656,25 +659,27 @@ FIXME: Uses Q1 contents
void V_SetContentsColor (int contents)
{
int i;
playerview_t *pv = r_refdef.playerview;
if (contents & FTECONTENTS_LAVA)
cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
pv->cshifts[CSHIFT_CONTENTS] = cshift_lava;
else if (contents & (FTECONTENTS_SLIME | FTECONTENTS_SOLID))
cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
pv->cshifts[CSHIFT_CONTENTS] = cshift_slime;
else if (contents & FTECONTENTS_WATER)
cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
pv->cshifts[CSHIFT_CONTENTS] = cshift_water;
else
cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
pv->cshifts[CSHIFT_CONTENTS] = cshift_empty;
cl.cshifts[CSHIFT_CONTENTS].percent *= v_contentblend.value;
pv->cshifts[CSHIFT_CONTENTS].percent *= v_contentblend.value;
if (cl.cshifts[CSHIFT_CONTENTS].percent)
if (pv->cshifts[CSHIFT_CONTENTS].percent)
{ //bound contents so it can't go negative
if (cl.cshifts[CSHIFT_CONTENTS].percent < 0)
cl.cshifts[CSHIFT_CONTENTS].percent = 0;
if (pv->cshifts[CSHIFT_CONTENTS].percent < 0)
pv->cshifts[CSHIFT_CONTENTS].percent = 0;
for (i = 0; i < 3; i++)
if (cl.cshifts[CSHIFT_CONTENTS].destcolor[0] < 0)
cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
if (pv->cshifts[CSHIFT_CONTENTS].destcolor[0] < 0)
pv->cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
}
}
@ -683,50 +688,44 @@ void V_SetContentsColor (int contents)
V_CalcPowerupCshift
=============
*/
void V_CalcPowerupCshift (void)
void V_CalcPowerupCshift (playerview_t *pv)
{
#ifdef QUAKESTATS
int im = 0;
int s;
//we only have one palette, so combine the mask
for (s = 0; s < cl.splitclients; s++)
im |= cl.playerview[s].stats[STAT_ITEMS];
int im = pv->stats[STAT_ITEMS];
if (im & IT_QUAD)
{
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
cl.cshifts[CSHIFT_POWERUP].percent = 30*v_quadcshift.value;
pv->cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
pv->cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
pv->cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
pv->cshifts[CSHIFT_POWERUP].percent = 30*v_quadcshift.value;
}
else if (im & IT_SUIT)
{
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
cl.cshifts[CSHIFT_POWERUP].percent = 20*v_suitcshift.value;
pv->cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
pv->cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
pv->cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
pv->cshifts[CSHIFT_POWERUP].percent = 20*v_suitcshift.value;
}
else if (im & IT_INVISIBILITY)
{
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
cl.cshifts[CSHIFT_POWERUP].percent = 100*v_ringcshift.value;
pv->cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
pv->cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
pv->cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
pv->cshifts[CSHIFT_POWERUP].percent = 100*v_ringcshift.value;
}
else if (im & IT_INVULNERABILITY)
{
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
cl.cshifts[CSHIFT_POWERUP].percent = 30*v_pentcshift.value;
pv->cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
pv->cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
pv->cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
pv->cshifts[CSHIFT_POWERUP].percent = 30*v_pentcshift.value;
}
else
cl.cshifts[CSHIFT_POWERUP].percent = 0;
pv->cshifts[CSHIFT_POWERUP].percent = 0;
if (cl.cshifts[CSHIFT_POWERUP].percent<0)
cl.cshifts[CSHIFT_POWERUP].percent=0;
if (pv->cshifts[CSHIFT_POWERUP].percent<0)
pv->cshifts[CSHIFT_POWERUP].percent=0;
#endif
}
@ -740,58 +739,71 @@ void V_CalcBlend (float *hw_blend)
{
extern qboolean r2d_canhwgamma;
float a2;
int j;
int j, seat;
float *blend;
playerview_t *pv;
memset(hw_blend, 0, sizeof(float)*4);
memset(sw_blend, 0, sizeof(float)*4);
//don't apply it to the server, we'll blend the two later if the user has no hardware gamma (if they do have it, we use just the server specified value) This way we avoid winnt users having a cheat with flashbangs and stuff.
for (j=0 ; j<NUM_CSHIFTS ; j++)
for (seat = 0; seat < cl.splitclients; seat++)
{
if (j != CSHIFT_SERVER && j != CSHIFT_BONUS)
pv = &cl.playerview[seat];
memset(pv->screentint, 0, sizeof(pv->screentint));
//don't apply it to the server, we'll blend the two later if the user has no hardware gamma (if they do have it, we use just the server specified value) This way we avoid winnt users having a cheat with flashbangs and stuff.
for (j=0 ; j<NUM_CSHIFTS ; j++)
{
if (!gl_cshiftpercent.value || !gl_cshiftenabled.ival)
if (j != CSHIFT_SERVER && j != CSHIFT_BONUS)
{
if (!gl_cshiftpercent.value || !gl_cshiftenabled.ival)
continue;
a2 = ((pv->cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
}
else
{
a2 = pv->cshifts[j].percent / 255.0; //don't allow modification of this one.
}
if (!a2)
continue;
a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
}
else
{
a2 = cl.cshifts[j].percent / 255.0; //don't allow modification of this one.
if (j == CSHIFT_SERVER)
{
/*server blend always goes into sw, ALWAYS*/
blend = pv->screentint;
}
else
{
/*flashing things should not change hardware gamma ramps - windows is too slow*/
/*splitscreen should only use hw gamma ramps if they're all equal, and they're probably not*/
/*hw blends may also not be supported or may be disabled*/
if (j == CSHIFT_BONUS || j == CSHIFT_DAMAGE || gl_nohwblend.ival || !r2d_canhwgamma || cl.splitclients > 1)
blend = pv->screentint;
else //powerup or contents?
blend = hw_blend;
}
blend[3] = blend[3] + a2*(1-blend[3]);
a2 = a2/blend[3];
blend[0] = blend[0]*(1-a2) + pv->cshifts[j].destcolor[0]*a2/255.0;
blend[1] = blend[1]*(1-a2) + pv->cshifts[j].destcolor[1]*a2/255.0;
blend[2] = blend[2]*(1-a2) + pv->cshifts[j].destcolor[2]*a2/255.0;
}
if (!a2)
continue;
if (j == CSHIFT_SERVER)
{
/*server blend always goes into sw, ALWAYS*/
blend = sw_blend;
}
else
{
if (j == CSHIFT_BONUS || j == CSHIFT_DAMAGE || gl_nohwblend.ival || !r2d_canhwgamma)
blend = sw_blend;
else //powerup or contents?
blend = hw_blend;
}
blend[3] = blend[3] + a2*(1-blend[3]);
a2 = a2/blend[3];
blend[0] = blend[0]*(1-a2) + cl.cshifts[j].destcolor[0]*a2/255.0;
blend[1] = blend[1]*(1-a2) + cl.cshifts[j].destcolor[1]*a2/255.0;
blend[2] = blend[2]*(1-a2) + cl.cshifts[j].destcolor[2]*a2/255.0;
if (pv->screentint[3] > 1)
pv->screentint[3] = 1;
if (pv->screentint[3] < 0)
pv->screentint[3] = 0;
}
for (; seat < MAX_SPLITS; seat++)
{
pv = &cl.playerview[seat];
memset(pv->screentint, 0, sizeof(pv->screentint));
}
if (hw_blend[3] > 1)
hw_blend[3] = 1;
if (hw_blend[3] < 0)
hw_blend[3] = 0;
if (sw_blend[3] > 1)
sw_blend[3] = 1;
if (sw_blend[3] < 0)
sw_blend[3] = 0;
}
/*
@ -815,17 +827,20 @@ void V_UpdatePalette (qboolean force)
if (ftime < 0)
ftime = 0;
V_CalcPowerupCshift ();
for (i = 0; i < MAX_SPLITS; i++)
{
playerview_t *pv = &cl.playerview[i];
V_CalcPowerupCshift(pv);
// drop the damage value
pv->cshifts[CSHIFT_DAMAGE].percent -= ftime*150;
if (pv->cshifts[CSHIFT_DAMAGE].percent <= 0)
pv->cshifts[CSHIFT_DAMAGE].percent = 0;
// drop the damage value
cl.cshifts[CSHIFT_DAMAGE].percent -= ftime*150;
if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
cl.cshifts[CSHIFT_DAMAGE].percent = 0;
// drop the bonus value
cl.cshifts[CSHIFT_BONUS].percent -= ftime*100;
if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
cl.cshifts[CSHIFT_BONUS].percent = 0;
// drop the bonus value
pv->cshifts[CSHIFT_BONUS].percent -= ftime*100;
if (pv->cshifts[CSHIFT_BONUS].percent <= 0)
pv->cshifts[CSHIFT_BONUS].percent = 0;
}
V_CalcBlend(newhw_blend);
@ -877,10 +892,11 @@ V_UpdatePalette
void V_ClearCShifts (void)
{
int i;
int i, seat;
for (i = 0; i < NUM_CSHIFTS; i++)
cl.cshifts[i].percent = 0;
for (seat = 0; seat < MAX_SPLITS; seat++)
for (i = 0; i < NUM_CSHIFTS; i++)
cl.playerview[seat].cshifts[i].percent = 0;
}
/*
@ -1351,7 +1367,11 @@ void V_CalcRefdef (playerview_t *pv)
#ifdef Q2CLIENT
if (cls.protocol == CP_QUAKE2)
{
VectorCopy (pv->simorg, r_refdef.vieworg);
VectorCopy (pv->simangles, r_refdef.viewangles);
return;
}
#endif
if (v_viewheight.value < -7)
@ -1619,7 +1639,9 @@ static void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)
VectorCopy(org, tagcenter);
tagcenter[2] += 32;
if (!Matrix4x4_CM_Project(tagcenter, center, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y))
return; //offscreen
return; //behind the camera
if (center[0] < 0 || center[0] > 1 || center[1] < 0 || center[1] > 1)
return; //off the side of the screen
obscured = !TP_IsPlayerVisible(org);
@ -1980,6 +2002,9 @@ void V_RenderPlayerViews(playerview_t *pv)
R2D_PolyBlend ();
R_DrawNameTags();
if(cl.intermissionmode == IM_NONE)
R2D_DrawCrosshair();
cl_numvisedicts = oldnuments;
cl_numstris = oldstris;

View file

@ -713,11 +713,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//#define MAX_EDICTS ((1<<22)-1) // expandable up to 22 bits
#define MAX_EDICTS ((1<<18)-1) // expandable up to 22 bits
#endif
#define MAX_LIGHTSTYLES 255 //255 = 'invalid', and thus only 0-254 are the valid indexes.
#define MAX_LIGHTSTYLES 255 // 8bit. 255 = 'invalid', and thus only 0-254 are the valid indexes.
#define MAX_STANDARDLIGHTSTYLES 64
#define MAX_PRECACHE_MODELS 2048 // these are sent over the net as bytes/shorts
#define MAX_PRECACHE_SOUNDS 1024 // so they cannot be blindly increased
#define MAX_SSPARTICLESPRE 1024 // precached particle effect names, for server-side pointparticles/trailparticles.
#define MAX_PRECACHE_MODELS 2048 // 14bit.
#define MAX_PRECACHE_SOUNDS 1024 // 14bit.
#define MAX_SSPARTICLESPRE 1024 // 14bit. precached particle effect names, for server-side pointparticles/trailparticles.
#define MAX_VWEP_MODELS 32
#define MAX_CSMODELS 1024 // these live entirly clientside

View file

@ -2138,7 +2138,7 @@ void Cmd_ForwardToServer_f (void)
if (Cmd_Argc() > 1)
{
int split = CL_TargettedSplit(true);
int split = CL_TargettedSplit(false);
if (split)
CL_SendClientCommand(true, "%i %s", split+1, Cmd_Args());
else
@ -2229,6 +2229,7 @@ void Cmd_ExecuteString (char *text, int level)
#ifndef SERVERONLY //an emergency escape mechansim, to avoid infinatly recursing aliases.
extern qboolean keydown[];
extern int con_splitmodifier;
if (keydown[K_SHIFT] && (keydown[K_LCTRL]||keydown[K_RCTRL]) && (keydown[K_LALT]||keydown[K_RALT]))
return;
@ -2264,6 +2265,15 @@ void Cmd_ExecuteString (char *text, int level)
Cmd_ExpandStringArguments (a->value, dest, sizeof(dest));
Cbuf_InsertText (dest, execlevel, false);
#ifndef SERVERONLY
if (con_splitmodifier > 0)
{ //if the alias was execed via p1/p2 etc, make sure that propagates properly (at least for simple aliases like impulses)
//fixme: should probably prefix each line. that may have different issues however.
//don't need to care about + etc
Cbuf_InsertText (va("p %i ", con_splitmodifier), execlevel, false);
}
#endif
Con_DPrintf("Execing alias %s:\n%s\n", a->name, a->value);
return;
}
@ -3054,6 +3064,9 @@ void Cmd_set_f(void)
else
docalc = false;
if (!strncmp(Cmd_Argv(0), "seta", 4) && !Cmd_FromGamecode())
forceflags |= CVAR_ARCHIVE;
Q_strncpyz(name, Cmd_Argv(1), sizeof(name));
if (!strcmp(Cmd_Argv(0), "setfl") || Cmd_FromGamecode()) //AAHHHH!!! Q2 set command is different
@ -3109,7 +3122,7 @@ void Cmd_set_f(void)
}
}
forceflags = 0;
forceflags |= 0;
}
var = Cvar_Get (name, text, CVAR_TEAMPLAYTAINT, "Custom variables");
@ -3147,9 +3160,6 @@ void Cmd_set_f(void)
if (Cmd_ExecLevel == RESTRICT_TEAMPLAY)
var->flags |= CVAR_TEAMPLAYTAINT;
if (!strncmp(Cmd_Argv(0), "seta", 4))
var->flags |= CVAR_ARCHIVE;
}
}
else
@ -3163,12 +3173,10 @@ void Cmd_set_f(void)
Cvar_LockFromServer(var, text);
}
else
var = Cvar_Get(Cmd_Argv(1), text, 0, "User variables");
}
var = Cvar_Get(Cmd_Argv(1), text, CVAR_USERCREATED, "User variables");
if (var && !Cmd_FromGamecode())
if (!strncmp(Cmd_Argv(0), "seta", 4))
var->flags |= CVAR_ARCHIVE|CVAR_USERCREATED;
var->flags |= forceflags;
}
If_Token_Clear(mark);
}
@ -3234,7 +3242,7 @@ void Cmd_WriteConfig_f(void)
snprintf(fname, sizeof(fname), "fte.cfg");
#endif
f = FS_OpenWithFriends(fname, sysname, sizeof(sysname), 3, "quake.rc", "*.cfg", "configs/*.cfg");
f = FS_OpenWithFriends(fname, sysname, sizeof(sysname), 3, "quake.rc", "hexen.rc", "*.cfg", "configs/*.cfg");
all = cfg_save_all.ival;
}

View file

@ -2791,9 +2791,14 @@ void Mod_LoadAliasShaders(model_t *mod)
extern float r_avertexnormals[NUMVERTEXNORMALS][3];
// mdltype 0 = q1, 1 = qtest, 2 = rapo/h2
static void Q1MDL_LoadPose(galiasinfo_t *galias, dmdl_t *pq1inmodel, vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype)
static void Q1MDL_LoadPose(galiasinfo_t *galias, dmdl_t *pq1inmodel, vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype, unsigned int bbox[6])
{
int j;
#ifdef _DEBUG
bbox[0] = bbox[1] = bbox[2] = ~0;
bbox[3] = bbox[4] = bbox[5] = 0;
#endif
#ifdef HEXEN2
if (mdltype == 2)
{
@ -2812,6 +2817,14 @@ static void Q1MDL_LoadPose(galiasinfo_t *galias, dmdl_t *pq1inmodel, vecV_t *ver
{
for (j = 0; j < pq1inmodel->numverts; j++)
{
#ifdef _DEBUG
bbox[0] = min(bbox[0], pinframe[j].v[0]);
bbox[1] = min(bbox[1], pinframe[j].v[1]);
bbox[2] = min(bbox[2], pinframe[j].v[2]);
bbox[3] = max(bbox[3], pinframe[j].v[0]);
bbox[4] = max(bbox[4], pinframe[j].v[1]);
bbox[5] = max(bbox[5], pinframe[j].v[2]);
#endif
verts[j][0] = pinframe[j].v[0]*pq1inmodel->scale[0]+pq1inmodel->scale_origin[0];
verts[j][1] = pinframe[j].v[1]*pq1inmodel->scale[1]+pq1inmodel->scale_origin[1];
verts[j][2] = pinframe[j].v[2]*pq1inmodel->scale[2]+pq1inmodel->scale_origin[2];
@ -2868,6 +2881,8 @@ static void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, mod
vecV_t *verts;
int aliasframesize = (mdltype == 1) ? sizeof(daliasframe_t)-16 : sizeof(daliasframe_t);
unsigned int bbox[6];
#ifdef SERVERONLY
normals = NULL;
svec = NULL;
@ -2913,10 +2928,22 @@ static void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, mod
}
else
{
Q1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype);
Q1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype, bbox);
pframetype = (daliasframetype_t *)&pinframe[pq1inmodel->numverts];
}
#ifdef _DEBUG
if ((bbox[3] > frameinfo->bboxmax.v[0] || bbox[4] > frameinfo->bboxmax.v[1] || bbox[5] > frameinfo->bboxmax.v[2] ||
bbox[0] < frameinfo->bboxmin.v[0] || bbox[1] < frameinfo->bboxmin.v[1] || bbox[2] < frameinfo->bboxmin.v[2]) && !galias->warned)
#else
if (pinframe[0].v[2] > frameinfo->bboxmax.v[2] && !galias->warned)
#endif
{
Con_Printf(CON_WARNING"%s has incorrect frame bounds\n", loadmodel->name);
galias->warned = true;
}
// GL_GenerateNormals((float*)verts, (float*)normals, (int *)((char *)galias + galias->ofs_indexes), galias->numindexes/3, galias->numverts);
break;
@ -2975,10 +3002,23 @@ static void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, mod
}
else
{
Q1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype);
Q1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype, bbox);
pinframe += pq1inmodel->numverts;
}
#ifdef _DEBUG
if ((bbox[3] > frameinfo->bboxmax.v[0] || bbox[4] > frameinfo->bboxmax.v[1] || bbox[5] > frameinfo->bboxmax.v[2] ||
bbox[0] < frameinfo->bboxmin.v[0] || bbox[1] < frameinfo->bboxmin.v[1] || bbox[2] < frameinfo->bboxmin.v[2] ||
#else
if ((pinframe[0].v[2] > frameinfo->bboxmax.v[2] ||
#endif
frameinfo->bboxmin.v[0] < ingroup->bboxmin.v[0] || frameinfo->bboxmin.v[1] < ingroup->bboxmin.v[1] || frameinfo->bboxmin.v[2] < ingroup->bboxmin.v[2] ||
frameinfo->bboxmax.v[0] > ingroup->bboxmax.v[0] || frameinfo->bboxmax.v[1] > ingroup->bboxmax.v[1] || frameinfo->bboxmax.v[2] > ingroup->bboxmax.v[2]) && !galias->warned)
{
Con_Printf(CON_WARNING"%s has incorrect frame bounds\n", loadmodel->name);
galias->warned = true;
}
#ifndef SERVERONLY
verts = (vecV_t*)&tvec[galias->numverts];
normals = (vec3_t*)&verts[galias->numverts];
@ -3594,17 +3634,25 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize)
galias->ofs_indexes = indexes;
for (i=0 ; i<pq1inmodel->numtris ; i++)
{
unsigned int v1 = LittleLong(pinq1triangles[i].vertindex[0]);
unsigned int v2 = LittleLong(pinq1triangles[i].vertindex[1]);
unsigned int v3 = LittleLong(pinq1triangles[i].vertindex[2]);
if (v1 >= pq1inmodel->numverts || v2 >= pq1inmodel->numverts || v3 >= pq1inmodel->numverts)
{
Con_Printf(CON_ERROR"%s has invalid triangle (%u %u %u > %u)\n", mod->name, v1, v2, v3, pq1inmodel->numverts);
v1 = v2 = v3 = 0;
}
if (!pinq1triangles[i].facesfront)
{
indexes[i*3+0] = seamremap[LittleLong(pinq1triangles[i].vertindex[0])];
indexes[i*3+1] = seamremap[LittleLong(pinq1triangles[i].vertindex[1])];
indexes[i*3+2] = seamremap[LittleLong(pinq1triangles[i].vertindex[2])];
indexes[i*3+0] = seamremap[v1];
indexes[i*3+1] = seamremap[v2];
indexes[i*3+2] = seamremap[v3];
}
else
{
indexes[i*3+0] = LittleLong(pinq1triangles[i].vertindex[0]);
indexes[i*3+1] = LittleLong(pinq1triangles[i].vertindex[1]);
indexes[i*3+2] = LittleLong(pinq1triangles[i].vertindex[2]);
indexes[i*3+0] = v1;
indexes[i*3+1] = v2;
indexes[i*3+2] = v3;
}
}
end = &pinq1triangles[pq1inmodel->numtris];
@ -4269,6 +4317,8 @@ int Mod_TagNumForName(model_t *model, const char *name)
if (!model)
return 0;
if (model->loadstate != MLS_LOADED)
return 0;
#ifdef HALFLIFEMODELS
if (model->type == mod_halflife)
return HLMod_BoneForName(model, name);

View file

@ -171,6 +171,7 @@ typedef struct galiasinfo_s
int numtagframes;
int numtags;
md3tag_t *ofstags;
unsigned int warned; //passed around at load time, so we don't spam warnings
} galiasinfo_t;
typedef struct

View file

@ -1006,6 +1006,11 @@ void MSG_WriteCoord (sizebuf_t *sb, float f)
coorddata i = MSG_ToCoord(f, sb->prim.coordsize);
SZ_Write (sb, (void*)&i, sb->prim.coordsize);
}
void MSG_WriteCoord24 (sizebuf_t *sb, float f)
{
coorddata i = MSG_ToCoord(f, 3);
SZ_Write (sb, (void*)&i, 3);
}
void MSG_WriteAngle16 (sizebuf_t *sb, float f)
{
@ -1565,6 +1570,12 @@ float MSG_ReadCoord (void)
MSG_ReadData(&c, net_message.prim.coordsize);
return MSG_FromCoord(c, net_message.prim.coordsize);
}
float MSG_ReadCoord24 (void)
{
coorddata c = {{0}};
MSG_ReadData(&c, 3);
return MSG_FromCoord(c, 3);
}
void MSG_ReadPos (vec3_t pos)
{
@ -3408,10 +3419,8 @@ messedup:
//unicode-to-ascii is not provided. you're expected to utf-8 the result or something.
//does not handle colour codes or hidden chars. add your own escape sequences if you need that.
//does not guarentee removal of control codes if eg the code was specified as an explicit unicode char.
unsigned int COM_DeQuake(conchar_t chr)
unsigned int COM_DeQuake(unsigned int chr)
{
chr &= CON_CHARMASK;
/*only this range are quake chars*/
if (chr >= 0xe000 && chr < 0xe100)
{
@ -5189,15 +5198,15 @@ void COM_WorkerPartialSync(void *priorityctx, int *address, int value)
// Con_Printf("Waited %f for %s\n", Sys_DoubleTime() - time1, priorityctx);
}
static void COM_WorkerPing(void *ctx, void *data, size_t a, size_t b)
static void COM_WorkerPong(void *ctx, void *data, size_t a, size_t b)
{
double *timestamp = data;
if (!b)
COM_AddWork(WG_MAIN, COM_WorkerPing, ctx, data, 0, 1);
else
{
Con_Printf("Ping: %g\n", Sys_DoubleTime() - *timestamp);
}
Con_Printf("Ping: %g\n", Sys_DoubleTime() - *timestamp);
Z_Free(timestamp);
}
static void COM_WorkerPing(void *ctx, void *data, size_t a, size_t b)
{
COM_AddWork(WG_MAIN, COM_WorkerPong, ctx, data, 0, 0);
}
static void COM_WorkerTest_f(void)
{

View file

@ -110,8 +110,8 @@ struct netprim_s
{
qbyte coordsize;
qbyte anglesize;
#define NPQ2_ANG16 (1<<0)
#define NPQ2_SIZE32 (1<<0)
#define NPQ2_ANG16 (1u<<0)
#define NPQ2_SOLID32 (1u<<1)
qbyte q2flags;
qbyte pad;
};
@ -347,12 +347,14 @@ char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, q
#define PFS_FORCEUTF8 2 //force utf-8 decoding
#define PFS_NOMARKUP 4 //strip markup completely
#define PFS_EZQUAKEMARKUP 8 //aim for compat with ezquake instead of q3 compat
#define PFS_CENTERED 16 //flag used by console prints (text should remain centered)
#define PFS_NONOTIFY 32 //flag used by console prints (text won't be visible other than by looking at the console)
conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t *out, int outsize, int keepmarkup); //ext is usually CON_WHITEMASK, returns its null terminator
unsigned int utf8_decode(int *error, const void *in, char **out);
unsigned int utf8_encode(void *out, unsigned int unicode, int maxlen);
unsigned int iso88591_encode(char *out, unsigned int unicode, int maxlen, qboolean markup);
unsigned int qchar_encode(char *out, unsigned int unicode, int maxlen, qboolean markup);
unsigned int COM_DeQuake(conchar_t chr);
unsigned int COM_DeQuake(unsigned int unichar);
void COM_BiDi_Shutdown(void);

View file

@ -98,12 +98,14 @@ extern conchar_t q3codemasks[MAXQ3COLOURS];
#define isextendedcode(x) ((x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || x == '-')
#define ishexcode(x) ((x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f'))
#define CONL_CENTERED (1u<<0)
typedef struct conline_s {
struct conline_s *older;
struct conline_s *newer;
unsigned short length;
unsigned short maxlength;
unsigned short lines;
unsigned char numlines; //updated so we scroll properly
unsigned char flags;
unsigned short id;
float time;
} conline_t;
@ -204,13 +206,14 @@ struct font_s;
void Con_DrawOneConsole(console_t *con, qboolean focused, struct font_s *font, float fx, float fy, float fsx, float fsy);
void Con_DrawConsole (int lines, qboolean noback);
char *Con_CopyConsole(console_t *con, qboolean nomarkup, qboolean onlyiflink);
void Con_Print (char *txt);
void Con_PrintFlags(char *text, unsigned int setflags, unsigned int clearflags);
void Con_Print (const char *txt);
void Con_CenterPrint(const char *txt);
void Con_PrintFlags(const char *text, unsigned int setflags, unsigned int clearflags);
void VARGS Con_Printf (const char *fmt, ...) LIKEPRINTF(1);
void VARGS Con_TPrintf (translation_t text, ...);
void VARGS Con_DPrintf (const char *fmt, ...) LIKEPRINTF(1);
void VARGS Con_SafePrintf (char *fmt, ...) LIKEPRINTF(1);
void Con_Footerf(console_t *con, qboolean append, char *fmt, ...) LIKEPRINTF(3);
void VARGS Con_SafePrintf (const char *fmt, ...) LIKEPRINTF(1);
void Con_Footerf(console_t *con, qboolean append, const char *fmt, ...) LIKEPRINTF(3);
void Con_Clear_f (void);
void Con_DrawNotify (void);
void Con_ClearNotify (void);
@ -229,7 +232,7 @@ qboolean Con_NameForNum(int num, char *buffer, int buffersize);
console_t *Con_FindConsole(const char *name);
console_t *Con_Create(const char *name, unsigned int flags);
void Con_SetVisible (console_t *con);
void Con_PrintCon (console_t *con, char *txt, unsigned int parseflags);
void Con_PrintCon (console_t *con, const char *txt, unsigned int parseflags);
void Con_NotifyBox (char *text); // during startup for sound / cd warnings

View file

@ -2631,7 +2631,8 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
FS_ChangeGame(man, cfg_reload_on_gamedir.ival, false);
}
#define QCFG "set com_parseutf8 0\nset allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\nsv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n"
/*quake requires a few settings for compatibility*/
#define QCFG "set com_parseutf8 0\nset allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n"
/*stuff that makes dp-only mods work a bit better*/
#define DPCOMPAT QCFG "set _cl_playermodel \"\"\n set dpcompat_set 1\nset dpcompat_corruptglobals 1\nset vid_pixelheight 1\n"
/*nexuiz/xonotic has a few quirks/annoyances...*/
@ -2667,55 +2668,58 @@ const gamemode_info_t gamemode_info[] = {
//whereas the quake mission packs replace start.bsp making the original episodes unreachable.
//for quake, we also allow extracting all files from paks. some people think it loads faster that way or something.
//cmdline switch exename protocol name(dpmaster) identifying file exec dir1 dir2 dir3 dir(fte) full name
{"-quake", "q1", MASTER_PREFIX"Quake", {"id1/pak0.pak",
"id1/quake.rc"}, QCFG, {"id1", "qw", "*fte"}, "Quake"/*, "id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
{"-hipnotic", "hipnotic", MASTER_PREFIX"Hipnotic",{"hipnotic/pak0.pak"}, QCFG, {"id1", "qw", "hipnotic", "*fte"}, "Quake: Scourge of Armagon"},
{"-rogue", "rogue", MASTER_PREFIX"Rogue", {"rogue/pak0.pak"}, QCFG, {"id1", "qw", "rogue", "*fte"}, "Quake: Dissolution of Eternity"},
//cmdline switch exename protocol name(dpmaster) identifying file exec dir1 dir2 dir3 dir(fte) full name
{"-quake", "q1", MASTER_PREFIX"Quake", {"id1/pak0.pak", "id1/quake.rc"},QCFG, {"id1", "qw", "*fte"}, "Quake"/*, "id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//quake's mission packs should not be favoured over the base game nor autodetected
//third part mods also tend to depend upon the mission packs for their huds, even if they don't use any other content.
//and q2 also has a rogue/pak0.pak file that we don't want to find and cause quake2 to look like dissolution of eternity
//so just make these require the same files as good ol' quake.
{"-hipnotic", "hipnotic", MASTER_PREFIX"Hipnotic",{"id1/pak0.pak","id1/quake.rc"},QCFG, {"id1", "qw", "hipnotic", "*fte"}, "Quake: Scourge of Armagon"},
{"-rogue", "rogue", MASTER_PREFIX"Rogue", {"id1/pak0.pak","id1/quake.rc"},QCFG, {"id1", "qw", "rogue", "*fte"}, "Quake: Dissolution of Eternity"},
//various quake-based mods.
{"-nexuiz", "nexuiz", "Nexuiz", {"nexuiz.exe"}, NEXCFG, {"data", "*ftedata"}, "Nexuiz"},
{"-xonotic", "xonotic", "Xonotic", {"xonotic.exe"}, NEXCFG, {"data", "*ftedata"}, "Xonotic"},
//various quake-based standalone mods.
{"-nexuiz", "nexuiz", "Nexuiz", {"nexuiz.exe"}, NEXCFG, {"data", "*ftedata"}, "Nexuiz"},
{"-xonotic", "xonotic", "Xonotic", {"xonotic.exe"}, NEXCFG, {"data", "*ftedata"}, "Xonotic"},
// {"-spark", "spark", "Spark", {"base/src/progs.src",
// "base/qwprogs.dat",
// "base/pak0.pak"}, DMFCFG, {"base", }, "Spark"},
// "base/pak0.pak"}, DMFCFG, {"base", }, "Spark"},
// {"-scouts", "scouts", "FTE-SJ", {"basesj/src/progs.src",
// "basesj/progs.dat",
// "basesj/pak0.pak"}, NULL, {"basesj", }, "Scouts Journey"},
// {"-rmq", "rmq", "RMQ", {NULL}, RMQCFG, {"id1", "qw", "rmq", "*fte"}, "Remake Quake"},
// "basesj/pak0.pak"}, NULL, {"basesj", }, "Scouts Journey"},
// {"-rmq", "rmq", "RMQ", {NULL}, RMQCFG, {"id1", "qw", "rmq", "*fte"}, "Remake Quake"},
#ifdef HEXEN2
//hexen2's mission pack generally takes precedence if both are installed.
{"-portals", "h2mp", "FTE-H2MP", {"portals/hexen.rc",
"portals/pak3.pak"}, HEX2CFG,{"data1", "portals", "*fteh2"}, "Hexen II MP"},
{"-hexen2", "hexen2", "FTE-Hexen2", {"data1/pak0.pak"}, HEX2CFG,{"data1", "*fteh2"}, "Hexen II"},
"portals/pak3.pak"}, HEX2CFG,{"data1", "portals", "*fteh2"}, "Hexen II MP"},
{"-hexen2", "hexen2", "FTE-Hexen2", {"data1/pak0.pak"}, HEX2CFG,{"data1", "*fteh2"}, "Hexen II"},
#endif
#if defined(Q2CLIENT) || defined(Q2SERVER)
{"-quake2", "q2", "FTE-Quake2", {"baseq2/pak0.pak"}, Q2CFG, {"baseq2", "*fteq2"}, "Quake II"},
{"-quake2", "q2", "FTE-Quake2", {"baseq2/pak0.pak"}, Q2CFG, {"baseq2", "*fteq2"}, "Quake II"},
//mods of the above that should generally work.
{"-dday", "dday", "FTE-Quake2", {"dday/pak0.pak"}, Q2CFG, {"baseq2", "dday", "*fteq2"}, "D-Day: Normandy"},
{"-dday", "dday", "FTE-Quake2", {"dday/pak0.pak"}, Q2CFG, {"baseq2", "dday", "*fteq2"}, "D-Day: Normandy"},
#endif
#if defined(Q3CLIENT) || defined(Q3SERVER)
{"-quake3", "q3", "FTE-Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena"},
{"-quake3", "q3", "FTE-Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena"},
//the rest are not supported in any real way. maps-only mostly, if that
// {"-quake4", "q4", "FTE-Quake4", {"q4base/pak00.pk4"}, NULL, {"q4base", "*fteq4"}, "Quake 4"},
// {"-et", NULL, "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "*fteet"}, "Wolfenstein - Enemy Territory"},
// {"-quake4", "q4", "FTE-Quake4", {"q4base/pak00.pk4"}, NULL, {"q4base", "*fteq4"}, "Quake 4"},
// {"-et", NULL, "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "*fteet"}, "Wolfenstein - Enemy Territory"},
// {"-jk2", "jk2", "FTE-JK2", {"base/assets0.pk3"}, NULL, {"base", "*ftejk2"}, "Jedi Knight II: Jedi Outcast"},
// {"-warsow", "warsow", "FTE-Warsow", {"basewsw/pak0.pk3"}, NULL, {"basewsw", "*ftewsw"}, "Warsow"},
// {"-jk2", "jk2", "FTE-JK2", {"base/assets0.pk3"}, NULL, {"base", "*ftejk2"}, "Jedi Knight II: Jedi Outcast"},
// {"-warsow", "warsow", "FTE-Warsow", {"basewsw/pak0.pk3"}, NULL, {"basewsw", "*ftewsw"}, "Warsow"},
#endif
#if !defined(QUAKETC) && !defined(MINIMAL)
// {"-doom", "doom", "FTE-Doom", {"doom.wad"}, NULL, {"*", "*ftedoom"}, "Doom"},
// {"-doom2", "doom2", "FTE-Doom2", {"doom2.wad"}, NULL, {"*", "*ftedoom"}, "Doom2"},
// {"-doom3", "doom3", "FTE-Doom3", {"doom3.wad"}, NULL, {"based3", "*ftedoom3"},"Doom3"},
// {"-doom", "doom", "FTE-Doom", {"doom.wad"}, NULL, {"*", "*ftedoom"}, "Doom"},
// {"-doom2", "doom2", "FTE-Doom2", {"doom2.wad"}, NULL, {"*", "*ftedoom"}, "Doom2"},
// {"-doom3", "doom3", "FTE-Doom3", {"doom3.wad"}, NULL, {"based3", "*ftedoom3"},"Doom3"},
//for the luls
// {"-diablo2", NULL, "FTE-Diablo2", {"d2music.mpq"}, NULL, {"*", "*fted2"}, "Diablo 2"},
// {"-diablo2", NULL, "FTE-Diablo2", {"d2music.mpq"}, NULL, {"*", "*fted2"}, "Diablo 2"},
#endif
#if defined(HLSERVER) || defined(HLCLIENT)
//can run in windows, needs hl gamecode enabled. maps can always be viewed, but meh.
{"-halflife", "halflife", "FTE-HalfLife", {"valve/liblist.gam"}, NULL, {"valve", "*ftehl"}, "Half-Life"},
{"-halflife", "halflife", "FTE-HalfLife", {"valve/liblist.gam"}, NULL, {"valve", "*ftehl"}, "Half-Life"},
#endif
{NULL}

View file

@ -541,6 +541,17 @@ static unsigned int QDECL VFSW32_FLocate(searchpathfuncs_t *handle, flocation_t
FindClose(h);
if (loc)
{
if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
{ //when looking for reparse points, FindFirstFile only reports info about the link, not the file. which means the size is wrong.
HANDLE f = CreateFileW(widen(wide, sizeof(wide), netpath), 0, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (f)
{
LARGE_INTEGER wsize;
GetFileSizeEx(f, &wsize);
CloseHandle(f);
len = qofs_Make(wsize.LowPart, wsize.HighPart);
}
}
loc->len = len;
loc->offset = 0;
loc->index = 0;

View file

@ -1060,6 +1060,7 @@ static vfsfile_t *QDECL FSZIP_OpenVFS(searchpathfuncs_t *handle, flocation_t *lo
if (flags & ZFL_DEFLATED)
{
#ifdef ZIPCRYPT
//FIXME: Cvar_Get is not threadsafe.
char *password = (flags & ZFL_WEAKENCRYPT)?Cvar_Get("fs_zip_password", "thisispublic", 0, "Filesystem")->string:NULL;
#else
char *password = NULL;

View file

@ -1346,12 +1346,10 @@ qboolean CModQ2_LoadTexInfo (model_t *mod, qbyte *mod_base, lump_t *l, char *map
if (out->flags & TI_SKY)
Q_snprintfz(sname, sizeof(sname), "sky/%s", in->texture);
else if (out->flags & (TI_WARP|TI_FLOWING))
Q_snprintfz(sname, sizeof(sname), "warp/%s", in->texture);
else if (out->flags & (TI_TRANS33|TI_TRANS66))
Q_snprintfz(sname, sizeof(sname), "trans/%s", in->texture);
else
Q_snprintfz(sname, sizeof(sname), "wall/%s", in->texture);
Q_snprintfz(sname, sizeof(sname), "%s", in->texture);
if (out->flags & (TI_WARP|TI_FLOWING))
Q_strncatz(sname, "#WARP", sizeof(sname));
if (out->flags & TI_FLOWING)
Q_strncatz(sname, "#FLOW", sizeof(sname));
if (out->flags & TI_TRANS66)

View file

@ -72,7 +72,7 @@ void Log_String (logtype_t lognum, char *s)
char fbase[MAX_QPATH];
char fname[MAX_QPATH];
conchar_t cline[2048], *c;
unsigned int u;
unsigned int u, flags;
f = NULL;
switch(lognum)
@ -107,14 +107,13 @@ void Log_String (logtype_t lognum, char *s)
COM_ParseFunString(CON_WHITEMASK, s, cline, sizeof(cline), !(log_readable.ival & 2));
t = utf8;
for (c = cline; *c; c++)
for (c = cline; *c; )
{
if ((*c & CON_HIDDEN) && (log_readable.ival & 2))
c = Font_Decode(c, &flags, &u);
if ((flags & CON_HIDDEN) && (log_readable.ival & 2))
continue;
if (log_readable.ival&1)
u = COM_DeQuake(*c);
else
u = *c&CON_CHARMASK;
u = COM_DeQuake(u);
//at the start of a new line, we might want a timestamp (so timestamps are correct for the first char of the line, instead of the preceeding \n)
if (log_newline[lognum])

View file

@ -5823,8 +5823,6 @@ cvar_t sv_port_natpmp = CVARCD("sv_port_natpmp", NATPMP_DEFAULT_PORT, SV_Port_Na
void SVNET_RegisterCvars(void)
{
int p;
#if defined(TCPCONNECT) && defined(HAVE_IPV4)
Cvar_Register (&sv_port_tcp, "networking");
sv_port_tcp.restriction = RESTRICT_MAX;
@ -5849,29 +5847,6 @@ void SVNET_RegisterCvars(void)
Cvar_Register (&sv_port_natpmp, "networking");
sv_port_natpmp.restriction = RESTRICT_MAX;
#endif
// parse params for cvars
p = COM_CheckParm ("-port");
if (!p)
p = COM_CheckParm ("-svport");
if (p && p < com_argc)
{
int port = atoi(com_argv[p+1]);
if (!port)
port = PORT_QWSERVER;
#ifdef HAVE_IPV4
if (*sv_port_ipv4.string)
Cvar_SetValue(&sv_port_ipv4, port);
#endif
#ifdef IPPROTO_IPV6
if (*sv_port_ipv6.string)
Cvar_SetValue(&sv_port_ipv6, port);
#endif
#ifdef USEIPX
if (*sv_port_ipx.string)
Cvar_SetValue(&sv_port_ipx, port);
#endif
}
}
void NET_CloseServer(void)

View file

@ -1046,6 +1046,8 @@ void QCBUILTIN PF_setattachment(pubprogfuncs_t *prinst, struct globalvars_s *pr_
model = world->Get_CModel(world, tagentity->v->modelindex);
if (model)
{
if (model && model->loadstate == MLS_LOADING)
COM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);
tagidx = Mod_TagNumForName(model, tagname);
if (tagidx == 0)
Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i (model \"%s\") but could not find it\n", NUM_FOR_EDICT(prinst, e), NUM_FOR_EDICT(prinst, tagentity), tagname, tagname, NUM_FOR_EDICT(prinst, tagentity), model->name);
@ -1806,7 +1808,7 @@ qboolean QC_FixFileName(const char *name, const char **result, const char **fall
*fallbackread = name;
//if its a user config, ban any fallback locations so that csqc can't read passwords or whatever.
if ((!strchr(name, '/') || strnicmp(name, "configs/", 8)) && !stricmp(COM_FileExtension(name, ext, sizeof(ext)), "cfg") && strnicmp(name, "particles/", 10))
if ((!strchr(name, '/') || strnicmp(name, "configs/", 8)) && !stricmp(COM_FileExtension(name, ext, sizeof(ext)), "cfg") && strnicmp(name, "particles/", 10) && strnicmp(name, "models/", 7))
*fallbackread = NULL;
*result = va("data/%s", name);
return true;
@ -5818,9 +5820,12 @@ lh_extension_t QSG_Extensions[] = {
#ifndef SERVERONLY
{"DP_CON_SETA", 0, NULL, {NULL}, "The 'seta' console command exists, like the 'set' command, but also marks the cvar for archiving, allowing it to be written into the user's config. Use this command in your default.cfg file."},
#endif
{"DP_EF_ADDITIVE"},
{"DP_EF_BLUE"}, //hah!! This is QuakeWorld!!!
{"DP_EF_FULLBRIGHT"}, //Rerouted to hexen2 support.
{"DP_EF_NODEPTHTEST"}, //for cheats
{"DP_EF_NODRAW"}, //implemented by sending it with no modelindex
{"DP_EF_NOSHADOW"},
{"DP_EF_RED"},
// {"DP_ENT_COLORMOD"},
{"DP_ENT_CUSTOMCOLORMAP"},
@ -5901,7 +5906,7 @@ lh_extension_t QSG_Extensions[] = {
{"DP_SV_EXTERIORMODELFORCLIENT"},
{"DP_SV_NODRAWTOCLIENT"}, //I prefer my older system. Guess I might as well remove that older system at some point.
{"DP_SV_PLAYERPHYSICS", 0, NULL, {NULL}, "Allows reworking parts of NQ player physics. USE AT OWN RISK - this necessitates NQ physics and is thus guarenteed to break prediction."},
{"DP_SV_POINTPARTICLES", 3, NULL, {"particleeffectnum", "pointparticles", "trailparticles"}, "Specifies that pointparticles (and trailparticles) exists in ssqc as well as csqc (and that dp's trailparticles argument fuckup will normally work). ssqc values can be passed to csqc for use, the reverse is not true. Does NOT mean that DP's effectinfo.txt is supported, only that ssqc has functionality equivelent to csqc."},
// {"DP_SV_POINTPARTICLES", 3, NULL, {"particleeffectnum", "pointparticles", "trailparticles"}, "Specifies that pointparticles (and trailparticles) exists in ssqc as well as csqc (and that dp's trailparticles argument fuckup will normally work). ssqc values can be passed to csqc for use, the reverse is not true. Does NOT mean that DP's effectinfo.txt is supported, only that ssqc has functionality equivelent to csqc."},
{"DP_SV_POINTSOUND", 1, NULL, {"pointsound"}},
{"DP_SV_PRECACHEANYTIME", 0, NULL, {NULL}, "Specifies that the various precache builtins can be called at any time. WARNING: precaches are sent reliably while sound events, modelindexes, and particle events are not. This can mean sounds and particles might not work the first time around, or models may take a while to appear (after the reliables are received and the model is loaded from disk). Always attempt to precache a little in advance in order to reduce these issues (preferably at the start of the map...)"},
{"DP_SV_SETCOLOR"},
@ -5929,7 +5934,7 @@ lh_extension_t QSG_Extensions[] = {
{"EXT_DIMENSION_GHOST"},
{"FRIK_FILE", 11, NULL, {"stof", "fopen","fclose","fgets","fputs","strlen","strcat","substring","stov","strzone","strunzone"}},
{"FTE_CALLTIMEOFDAY", 1, NULL, {"calltimeofday"}},
{"FTE_CSQC_ALTCONSOLES_WIP", 4, NULL, {"con_getset", "con_printf", "con_draw", "con_input"}},
{"FTE_CSQC_ALTCONSOLES", 4, NULL, {"con_getset", "con_printf", "con_draw", "con_input"}, "The engine tracks multiple consoles. These may or may not be directly visible to the user."},
{"FTE_CSQC_BASEFRAME", 0, NULL, {NULL}, "Specifies that .basebone, .baseframe, .baselerpfrac, etc exist. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations."},
#ifdef HALFLIFEMODELS
{"FTE_CSQC_HALFLIFE_MODELS"}, //hl-specific skeletal model control
@ -5940,13 +5945,14 @@ lh_extension_t QSG_Extensions[] = {
{"FTE_CSQC_SKELETONOBJECTS", 15, NULL, { "skel_create", "skel_build", "skel_get_numbones", "skel_get_bonename", "skel_get_boneparent", "skel_find_bone",
"skel_get_bonerel", "skel_get_boneabs", "skel_set_bone", "skel_mul_bone", "skel_mul_bones", "skel_copybones",
"skel_delete", "frameforname", "frameduration"}},
{"FTE_CSQC_RENDERTARGETS_WIP", 0, NULL, {NULL}, "VF_DESTCOLOUR etc exist and are supported"},
{"FTE_CSQC_RENDERTARGETS", 0, NULL, {NULL}, "VF_DESTCOLOUR etc exist and are supported"},
{"FTE_ENT_SKIN_CONTENTS", 0, NULL, {NULL}, "self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder."},
{"FTE_ENT_UNIQUESPAWNID"},
{"FTE_EXTENDEDTEXTCODES"},
{"FTE_FORCESHADER", 1, NULL, {"shaderforname"}}, //I'd rename this to _CSQC_ but it does technically provide this builtin to menuqc too, not that the forceshader entity field exists there... but whatever.
{"FTE_FORCEINFOKEY", 1, NULL, {"forceinfokey"}},
{"FTE_GFX_QUAKE3SHADERS"},
{"FTE_GFX_QUAKE3SHADERS", 0, NULL, {NULL}, "specifies that the engine has full support for vanilla quake3 shaders"},
{"FTE_GFX_REMAPSHADER", 0, NULL, {NULL}, "With the raw power of stuffcmds, the r_remapshader console command is exposed! This mystical command can be used to remap any shader to another. Remapped shaders that specify $diffuse etc in some form will inherit the textures implied by the surface."},
{"FTE_ISBACKBUFFERED", 1, NULL, {"isbackbuffered"}, "Allows you to check if a client has too many reliable messages pending."},
{"FTE_MEMALLOC", 4, NULL, {"memalloc", "memfree", "memcpy", "memfill8"}, "Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games."},
#ifndef NOMEDIA
@ -5958,11 +5964,19 @@ lh_extension_t QSG_Extensions[] = {
#endif
{"FTE_MULTIPROGS", 5, NULL, {"externcall", "addprogs", "externvalue", "externset", "instr"}, "Multiple progs.dat files can be loaded inside the same qcvm."}, //multiprogs functions are available.
{"FTE_MULTITHREADED", 3, NULL, {"sleep", "fork", "abort"}},
{"FTE_MVD_PLAYERSTATS", 0, NULL, {NULL}, "In csqc, getplayerstat can be used to query any player's stats when playing back MVDs. isdemo will return 2 in this case."},
#ifdef SERVER_DEMO_PLAYBACK
{"FTE_MVD_PLAYBACK"},
#endif
#ifdef SVCHAT
{"FTE_NPCCHAT", 1, NULL, {"chat"}}, //server looks at chat files. It automagically branches through calling qc functions as requested.
#endif
#ifdef PSET_SCRIPT
{"FTE_PART_SCRIPT", 0, NULL, {NULL}, "Specifies that the r_particledesc cvar can be used to select a list of particle effects to load from particles/*.cfg, the format of which is documented elsewhere."},
{"FTE_PART_NAMESPACES", 0, NULL, {NULL}, "Specifies that the engine can use foo.bar to load effect foo from particle description bar. When used via ssqc, this should cause the client to download whatever effects as needed."},
#ifndef NOLEGACY
{"FTE_PART_NAMESPACE_EFFECTINFO", 0, NULL, {NULL}, "Specifies that effectinfo.bar can load effects from effectinfo.txt for DP compatibility."},
#endif
#endif
{"FTE_QC_CHECKCOMMAND", 1, NULL, {"checkcommand"}, "Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values."},
{"FTE_QC_CHECKPVS", 1, NULL, {"checkpvs"}},
@ -6001,6 +6015,7 @@ lh_extension_t QSG_Extensions[] = {
//reuses the FRIK_FILE builtins (with substring extension)
{"FTE_STRINGS", 17, NULL, {"stof", "strlen","strcat","substring","stov","strzone","strunzone",
"strstrofs", "str2chr", "chr2str", "strconv", "infoadd", "infoget", "strncmp", "strcasecmp", "strncasecmp", "strpad"}},
{"FTE_SV_POINTPARTICLES", 3, NULL, {"particleeffectnum", "pointparticles", "trailparticles"}, "Specifies that particleeffectnum, pointparticles, and trailparticles exist in ssqc as well as csqc. particleeffectnum acts as a precache, allowing ssqc values to be networked up with csqc for use. Use in combination with FTE_PART_SCRIPT+FTE_PART_NAMESPACES to use custom effects. This extension is functionally identical to the DP version, but avoids any misplaced assumptions about the format of the client's particle descriptions."},
{"FTE_SV_REENTER"},
{"FTE_TE_STANDARDEFFECTBUILTINS", 14, NULL, {"te_gunshot", "te_spike", "te_superspike", "te_explosion", "te_tarexplosion", "te_wizspike", "te_knightspike", "te_lavasplash",
"te_teleport", "te_lightning1", "te_lightning2", "te_lightning3", "te_lightningblood", "te_bloodqw"}},
@ -6017,6 +6032,7 @@ lh_extension_t QSG_Extensions[] = {
{"QW_ENGINE", 3, NULL, {"infokey", "stof", "logfrag"}}, //warning: interpretation of .skin on players can be dodgy, as can some other QW features that differ from NQ.
{"QWE_MVD_RECORD"}, //Quakeworld extended get the credit for this one. (mvdsv)
{"TEI_MD3_MODEL"},
{"TENEBRAE_GFX_DLIGHTS", 0, NULL,{NULL}, "Allows ssqc to attach rtlights to entities with various special properties."},
// {"TQ_RAILTRAIL"}, //treat this as the ZQ style railtrails which the client already supports, okay so the preparse stuff needs strengthening.
{"ZQ_MOVETYPE_FLY"},
{"ZQ_MOVETYPE_NOCLIP"},

View file

@ -526,6 +526,7 @@ pbool QDECL ED_CanFree (edict_t *ed);
#define MOVETYPE_6DOF 30 // flightsim mode
#define MOVETYPE_WALLWALK 31 // walks up walls and along ceilings
#define MOVETYPE_PHYSICS 32
#define MOVETYPE_FLY_WORLDONLY 33
// edict->solid values
#define SOLID_NOT 0 // no interaction with other objects

View file

@ -794,7 +794,7 @@ enum clcq2_ops_e
#define Q2U_SKIN16 (1<<25)
#define Q2U_SOUND (1<<26)
#define Q2U_SOLID (1<<27)
#define Q2UX_UNUSED4 (1<<28)
#define Q2UX_INDEX16 (1<<28) //model or sound is 16bit
#define Q2UX_UNUSED3 (1<<29)
#define Q2UX_UNUSED2 (1<<30)
#define Q2UX_UNUSED1 (1<<31)
@ -830,8 +830,9 @@ enum clcq2_ops_e
#define NQSND_VOLUME (1<<0) // a qbyte
#define NQSND_ATTENUATION (1<<1) // a qbyte
//#define DPSND_LOOPING (1<<2) // a long, supposedly
#define FTESND_FLAGS (1<<2) //
#define FTESND_MOREFLAGS (1<<2) // actually, chan flags
#define DPSND_LARGEENTITY (1<<3)
#define DPSND_LARGESOUND (1<<4)
//#define DPSND_SPEEDUSHORT4000 (1<<5) // ushort speed*4000 (speed is usually 1.0, a value of 0.0 is the same as 1.0)
#define FTESND_TIMEOFS (1<<6) //signed short, in milliseconds.
@ -1178,22 +1179,30 @@ typedef struct q1usercmd_s
//for the local player
#define Q2PS_M_TYPE (1<<0)
#define Q2PS_M_TYPE (1<<0)
#define Q2PS_M_ORIGIN (1<<1)
#define Q2PS_M_VELOCITY (1<<2)
#define Q2PS_M_TIME (1<<3)
#define Q2PS_M_VELOCITY (1<<2)
#define Q2PS_M_TIME (1<<3)
#define Q2PS_M_FLAGS (1<<4)
#define Q2PS_M_GRAVITY (1<<5)
#define Q2PS_M_DELTA_ANGLES (1<<6)
#define Q2PS_VIEWOFFSET (1<<7)
#define Q2PS_VIEWANGLES (1<<8)
#define Q2PS_KICKANGLES (1<<9)
#define Q2PS_BLEND (1<<10)
#define Q2PS_M_GRAVITY (1<<5)
#define Q2PS_M_DELTA_ANGLES (1<<6)
#define Q2PS_VIEWOFFSET (1<<7)
#define Q2PS_VIEWANGLES (1<<8)
#define Q2PS_KICKANGLES (1<<9)
#define Q2PS_BLEND (1<<10)
#define Q2PS_FOV (1<<11)
#define Q2PS_WEAPONINDEX (1<<12)
#define Q2PS_WEAPONFRAME (1<<13)
#define Q2PS_RDFLAGS (1<<14)
#define Q2PS_EXTRABITS (1<<15)
#define Q2PS_INDEX16 (1<<16)
#define Q2PS_CLIENTNUM (1<<17)
#define Q2PS_UNUSED6 (1<<18)
#define Q2PS_UNUSED5 (1<<19)
#define Q2PS_UNUSED4 (1<<20)
#define Q2PS_UNUSED3 (1<<21)
#define Q2PS_UNUSED2 (1<<22)
#define Q2PS_UNUSED1 (1<<23)
#define Q2PSX_GUNOFFSET (1<<0)
#define Q2PSX_GUNANGLES (1<<1)
@ -1254,11 +1263,14 @@ typedef struct q1usercmd_s
#define Q2SND_VOLUME (1<<0) // a qbyte
#define Q2SND_ATTENUATION (1<<1) // a qbyte
#define Q2SND_POS (1<<2) // three coordinates
#define Q2SND_ENT (1<<3) // a short 0-2: channel, 3-12: entity
#define Q2SND_OFFSET (1<<4) // a qbyte, msec offset from frame start
#define Q2SND_VOLUME (1u<<0) // a qbyte
#define Q2SND_ATTENUATION (1u<<1) // a qbyte
#define Q2SND_POS (1u<<2) // three coordinates
#define Q2SND_ENT (1u<<3) // a short 0-2: channel, 3-12: entity
#define Q2SND_OFFSET (1u<<4) // a qbyte, msec offset from frame start
#define Q2SND_LARGEIDX (1u<<5) // idx is a short
#define Q2SND_LARGEPOS (1u<<6) // float coord
#define Q2SND_EXTRABITS (1u<<7) // unused for now, reserved.
#define Q2DEFAULT_SOUND_PACKET_VOLUME 1.0
#define Q2DEFAULT_SOUND_PACKET_ATTENUATION 1.0

View file

@ -1683,13 +1683,14 @@ static void Q1BSP_RFindTouchedLeafs (model_t *wm, struct pvscache_s *ent, mnode_
int sides;
int leafnum;
if (node->contents == Q1CONTENTS_SOLID)
return;
// add an efrag if the node is a leaf
if ( node->contents < 0)
if (node->contents < 0)
{
//ignore solid leafs. this should include leaf 0 (which has no pvs info)
if (node->contents == Q1CONTENTS_SOLID)
return;
if (ent->num_leafs >= MAX_ENT_LEAFS)
{
ent->num_leafs = MAX_ENT_LEAFS+1; //too many. mark it as such so we can trivially accept huge mega-big brush models.

View file

@ -3389,19 +3389,18 @@ static void BE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist)
}
}
void D3D11BE_SubmitMeshes (qboolean drawworld, batch_t **blist, int first, int stop)
void D3D11BE_SubmitMeshes (batch_t **worldbatches, batch_t **blist, int first, int stop)
{
model_t *model = cl.worldmodel;
int i;
for (i = first; i < stop; i++)
{
if (drawworld)
if (worldbatches)
{
if (i == SHADER_SORT_PORTAL /*&& !r_noportals.ival*/ && !r_refdef.recurse)
BE_SubmitMeshesPortals(model->batches, blist[i]);
BE_SubmitMeshesPortals(worldbatches, blist[i]);
BE_SubmitMeshesSortList(model->batches[i]);
BE_SubmitMeshesSortList(worldbatches[i]);
}
BE_SubmitMeshesSortList(blist[i]);
}
@ -3412,7 +3411,7 @@ void D3D11BE_BaseEntTextures(void)
{
batch_t *batches[SHADER_SORT_COUNT];
BE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode);
D3D11BE_SubmitMeshes(false, batches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
D3D11BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
BE_SelectEntity(&r_worldentity);
}
@ -3505,7 +3504,7 @@ void D3D11BE_DoneShadows(void)
}
#endif
void D3D11BE_DrawWorld (qboolean drawworld, qbyte *vis)
void D3D11BE_DrawWorld (batch_t **worldbatches, qbyte *vis)
{
batch_t *batches[SHADER_SORT_COUNT];
RSpeedLocals();
@ -3556,7 +3555,7 @@ void D3D11BE_DrawWorld (qboolean drawworld, qbyte *vis)
BE_SelectMode(BEM_STANDARD);
RSpeedRemark();
D3D11BE_SubmitMeshes(true, batches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
D3D11BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
RSpeedEnd(RSPEED_WORLD);
#ifdef RTLIGHTS
@ -3566,13 +3565,13 @@ void D3D11BE_DrawWorld (qboolean drawworld, qbyte *vis)
RSpeedEnd(RSPEED_STENCILSHADOWS);
#endif
D3D11BE_SubmitMeshes(true, batches, SHADER_SORT_DECAL, SHADER_SORT_COUNT);
D3D11BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_DECAL, SHADER_SORT_COUNT);
}
else
{
RSpeedRemark();
shaderstate.identitylighting = 1;
D3D11BE_SubmitMeshes(false, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);
D3D11BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);
RSpeedEnd(RSPEED_DRAWENTITIES);
}

View file

@ -3357,19 +3357,18 @@ static void BE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist)
}
}
void D3D9BE_SubmitMeshes (qboolean drawworld, batch_t **blist, int first, int stop)
void D3D9BE_SubmitMeshes (batch_t **worldbatches, batch_t **blist, int first, int stop)
{
model_t *model = cl.worldmodel;
int i;
for (i = first; i < stop; i++)
{
if (drawworld)
if (worldbatches)
{
if (i == SHADER_SORT_PORTAL /*&& !r_noportals.ival*/ && !r_refdef.recurse)
BE_SubmitMeshesPortals(model->batches, blist[i]);
BE_SubmitMeshesPortals(worldbatches, blist[i]);
BE_SubmitMeshesSortList(model->batches[i]);
BE_SubmitMeshesSortList(worldbatches[i]);
}
BE_SubmitMeshesSortList(blist[i]);
}
@ -3380,7 +3379,7 @@ void D3D9BE_BaseEntTextures(void)
{
batch_t *batches[SHADER_SORT_COUNT];
BE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode);
D3D9BE_SubmitMeshes(false, batches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
D3D9BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
BE_SelectEntity(&r_worldentity);
}
@ -3410,7 +3409,7 @@ void D3D9BE_RenderShadowBuffer(unsigned int numverts, IDirect3DVertexBuffer9 *vb
}
#endif
void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis)
void D3D9BE_DrawWorld (batch_t **worldbatches, qbyte *vis)
{
batch_t *batches[SHADER_SORT_COUNT];
RSpeedLocals();
@ -3432,7 +3431,7 @@ void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis)
shaderstate.curdlight = NULL;
BE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD);
if (drawworld)
if (worldbatches)
{
float shaderstate_identitylighting;
BE_UploadLightmaps(false);
@ -3447,7 +3446,7 @@ void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis)
r_worldentity.axis[2][2] = 1;
#ifdef RTLIGHTS
if (drawworld && r_shadow_realtime_world.ival)
if (worldbatches && r_shadow_realtime_world.ival)
shaderstate_identitylighting = r_shadow_realtime_world_lightmaps.value;
else
#endif
@ -3461,7 +3460,7 @@ void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis)
BE_SelectMode(BEM_STANDARD);
RSpeedRemark();
D3D9BE_SubmitMeshes(true, batches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
D3D9BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL);
RSpeedEnd(RSPEED_WORLD);
#ifdef RTLIGHTS
@ -3476,12 +3475,12 @@ void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis)
BE_SelectMode(BEM_STANDARD);
D3D9BE_SubmitMeshes(true, batches, SHADER_SORT_DECAL, SHADER_SORT_COUNT);
D3D9BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_DECAL, SHADER_SORT_COUNT);
}
else
{
RSpeedRemark();
D3D9BE_SubmitMeshes(false, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);
D3D9BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);
RSpeedEnd(RSPEED_DRAWENTITIES);
}

View file

@ -1183,10 +1183,10 @@ static void D3D9_SetupViewPortProjection(void)
d3d9error(IDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_VIEW, (D3DMATRIX*)r_refdef.m_view));
/*d3d projection matricies scale depth to 0 to 1*/
Matrix4x4_CM_Projection_Inf(d3d_trueprojection, fov_x, fov_y, gl_mindist.value/2);
Matrix4x4_CM_Projection_Inf(d3d_trueprojection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4)/2);
d3d9error(IDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_PROJECTION, (D3DMATRIX*)d3d_trueprojection));
/*ogl projection matricies scale depth to -1 to 1, and I would rather my code used consistant culling*/
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, gl_mindist.value);
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4));
}
static void (D3D9_R_RenderView) (void)

View file

@ -1324,7 +1324,6 @@ static void (D3D11_SCR_UpdateScreen) (void)
if (!noworld)
{
R2D_PolyBlend ();
R2D_BrightenScreen();
}
@ -1434,7 +1433,7 @@ static void D3D11_SetupViewPort(void)
/*view matrix*/
Matrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg);
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, gl_mindist.value);
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4));
}
static void (D3D11_R_RenderView) (void)

View file

@ -1688,7 +1688,7 @@
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
Optimization="3"
InlineFunctionExpansion="2"
EnableIntrinsicFunctions="true"
FavorSizeOrSpeed="1"

View file

@ -4690,11 +4690,8 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist)
{
if (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)// || shaderstate.mode == BEM_WIREFRAME)
{
if (!bs->prog)
{
R_DrawSkyChain (batch);
if (R_DrawSkyChain(batch))
continue;
}
}
else if (shaderstate.mode != BEM_FOG && shaderstate.mode != BEM_CREPUSCULAR && shaderstate.mode != BEM_WIREFRAME)
continue;

View file

@ -3833,7 +3833,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty)
if (!s || s->loadstate != TSLS_LOADED)
{
if ((tr->hitcontentsmask & tr->hm->exteriorcontents) || s->loadstate != TSLS_FAILED)
if ((tr->hitcontentsmask & tr->hm->exteriorcontents) || (s && s->loadstate != TSLS_FAILED))
{
//you're not allowed to walk into sections that have not loaded.
//might as well check the entire section instead of just one tile

View file

@ -4890,7 +4890,7 @@ SPRITES
"if gl_blendsprites\n" \
"program defaultsprite\n" \
"else\n" \
"program defaultsprite#MASK=1\n" \
"program defaultsprite#MASK=0.666\n" \
"endif\n" \
"{\n" \
"map $diffuse\n" \

View file

@ -175,7 +175,7 @@ m*_t structures are in-memory
#define EF_FULLBRIGHT (1<<9) //abslight=1
#define DPEF_FLAME (1<<10) //'onfire'
#define DPEF_STARDUST (1<<11) //'showering sparks'
#define DPEF_NOSHADOW (1<<12) //doesn't cast a shadow
#define EF_NOSHADOW (1<<12) //doesn't cast a shadow
#define EF_NODEPTHTEST (1<<13) //shows through walls.
#define DPEF_SELECTABLE_ (1<<14) //highlights when prydoncursored
#define DPEF_DOUBLESIDED_ (1<<15) //disables culling

View file

@ -150,6 +150,7 @@ DYNAMIC LIGHTS BLEND RENDERING
void AddLightBlend (float r, float g, float b, float a2)
{
float a;
float *sw_blend = r_refdef.playerview->screentint;
r = bound(0, r, 1);
g = bound(0, g, 1);

View file

@ -499,11 +499,11 @@ void R_SetupGL (float stereooffset)
// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*(scr_fov.value*2)/M_PI;
// MYgluPerspective (yfov, screenaspect, 4, 4096);
Matrix4x4_CM_Projection_Far(r_refdef.m_projection, fov_x, fov_y, gl_mindist.value, gl_maxdist.value);
Matrix4x4_CM_Projection_Far(r_refdef.m_projection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4), gl_maxdist.value);
}
else
{
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, gl_mindist.value);
Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, fov_x, fov_y, bound(0.1, gl_mindist.value, 4));
}
}
else
@ -1407,6 +1407,7 @@ qboolean R_RenderScene_Cubemap(void)
int oldfbo = -1;
qboolean usefbo = true; //this appears to be a 20% speedup in my tests.
static fbostate_t fbostate; //FIXME
qboolean fboreset = false;
/*needs glsl*/
if (!gl_config.arb_shader_objects)
@ -1477,6 +1478,7 @@ qboolean R_RenderScene_Cubemap(void)
if (ffov.value > 270)
facemask |= 1<<5; /*back view*/
}
facemask = 0x3f;
break;
case 4:
shader = R_RegisterShader("postproc_laea", SUF_NONE,
@ -1509,10 +1511,14 @@ qboolean R_RenderScene_Cubemap(void)
if (sh_config.texture_non_power_of_two_pic)
{
if (prect.width < prect.height)
cmapsize = prect.width;
if (usefbo)
{
cmapsize = prect.width > prect.height?prect.width:prect.height;
if (cmapsize > 4096)//sh_config.texture_maxsize)
cmapsize = 4096;//sh_config.texture_maxsize;
}
else
cmapsize = prect.height;
cmapsize = prect.width < prect.height?prect.width:prect.height;
}
else if (!usefbo)
{
@ -1540,6 +1546,13 @@ qboolean R_RenderScene_Cubemap(void)
scenepp_postproc_cube = Image_CreateTexture("***fish***", NULL, IF_CUBEMAP|IF_RENDERTARGET|IF_CLAMP|IF_LINEAR);
qglGenTextures(1, &scenepp_postproc_cube->num);
}
else
{
qglDeleteTextures(1, &scenepp_postproc_cube->num);
scenepp_postproc_cube->num = 0;
GL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, scenepp_postproc_cube);
qglGenTextures(1, &scenepp_postproc_cube->num);
}
GL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, scenepp_postproc_cube);
for (i = 0; i < 6; i++)
@ -1550,12 +1563,14 @@ qboolean R_RenderScene_Cubemap(void)
qglTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
scenepp_postproc_cube_size = cmapsize;
fboreset = true;
}
vrect = r_refdef.vrect; //save off the old vrect
r_refdef.vrect.width = (cmapsize * vid.width) / vid.pixelwidth;
r_refdef.vrect.height = (cmapsize * vid.height) / vid.pixelheight;
r_refdef.vrect.width = (cmapsize * vid.fbvwidth) / vid.fbpwidth;
r_refdef.vrect.height = (cmapsize * vid.fbvheight) / vid.fbpheight;
r_refdef.vrect.x = 0;
r_refdef.vrect.y = prect.y;
@ -1578,7 +1593,8 @@ qboolean R_RenderScene_Cubemap(void)
if (usefbo)
{
int r = GLBE_FBO_Update(&fbostate, FBO_RB_DEPTH, &scenepp_postproc_cube, 1, r_nulltex, cmapsize, cmapsize, i);
int r = GLBE_FBO_Update(&fbostate, FBO_RB_DEPTH|(fboreset?FBO_RESET:0), &scenepp_postproc_cube, 1, r_nulltex, cmapsize, cmapsize, i);
fboreset = false;
if (oldfbo < 0)
oldfbo = r;
}
@ -1601,7 +1617,7 @@ qboolean R_RenderScene_Cubemap(void)
if (!usefbo)
{
GL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, scenepp_postproc_cube);
qglCopyTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, 0, 0, 0, vid.pixelheight - (prect.y + cmapsize), cmapsize, cmapsize);
qglCopyTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, 0, 0, 0, vid.fbpheight - (prect.y + cmapsize), cmapsize, cmapsize);
}
}
@ -1623,15 +1639,21 @@ qboolean R_RenderScene_Cubemap(void)
qglLoadIdentity ();
*/
// draw it through the shader
if (vrect.width > vrect.height)
if (r_projection.ival == 3)
{
float saspect = .5;
float taspect = vrect.height / vrect.width * ffov.value / 90;//(0.5 * vrect.width) / vrect.height;
R2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, -saspect, taspect, saspect, -taspect, shader);
}
else if (vrect.width > vrect.height)
{
float aspect = (0.5 * vrect.height) / vrect.width;
R2D_Image(0, 0, vid.width, vid.height, -0.5, aspect, 0.5, -aspect, shader);
R2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, -0.5, aspect, 0.5, -aspect, shader);
}
else
{
float aspect = (0.5 * vrect.width) / vrect.height;
R2D_Image(0, 0, vid.width, vid.height, -aspect, 0.5, aspect, -0.5, shader);
R2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, -aspect, 0.5, aspect, -0.5, shader);
}
//revert the matricies
@ -1719,7 +1741,7 @@ void GLR_RenderView (void)
//check if we're underwater (this also limits damage from stereo wallhacks).
Surf_SetupFrame();
r_refdef.flags &= ~RDF_ALLPOSTPROC;
r_refdef.flags &= ~(RDF_ALLPOSTPROC|RDF_RENDERSCALE);
if (!(r_refdef.flags & RDF_NOWORLDMODEL))
if (R_CanBloom())
@ -1885,7 +1907,7 @@ void GLR_RenderView (void)
time1 = Sys_DoubleTime ();
}
if (!dofbo && !(r_refdef.flags & RDF_NOWORLDMODEL) && R_RenderScene_Cubemap())
if (!(r_refdef.flags & RDF_NOWORLDMODEL) && R_RenderScene_Cubemap())
{
}

View file

@ -5047,14 +5047,15 @@ void Shader_DefaultBSPQ2(const char *shortname, shader_t *s, const void *args)
"{\n"
"surfaceparm nodlight\n"
"skyparms - - -\n"
"surfaceparm nodlight\n"
"}\n"
);
}
else if (!strncmp(shortname, "warp/", 5) || !strncmp(shortname, "warp33/", 7) || !strncmp(shortname, "warp66/", 7))
else if (Shader_FloatArgument(s, "#WARP"))//!strncmp(shortname, "warp/", 5) || !strncmp(shortname, "warp33/", 7) || !strncmp(shortname, "warp66/", 7))
{
Shader_DefaultScript(shortname, s, Shader_DefaultBSPWater(s, shortname));
}
else if (!strncmp(shortname, "trans/", 6))
else if (Shader_FloatArgument(s, "#ALPHA"))// !strncmp(shortname, "trans/", 6))
{
Shader_DefaultScript(shortname, s,
"{\n"

View file

@ -67,7 +67,7 @@ void R_SetSky(char *skyname)
GL_DrawSkyChain
=================
*/
void R_DrawSkyChain (batch_t *batch)
qboolean R_DrawSkyChain (batch_t *batch)
{
shader_t *skyshader;
texid_t *skyboxtex;
@ -77,6 +77,9 @@ void R_DrawSkyChain (batch_t *batch)
else
skyshader = batch->shader;
if (skyshader->prog)
return false;
if (skyshader->skydome)
skyboxtex = skyshader->skydome->farbox_textures;
else
@ -112,6 +115,8 @@ void R_DrawSkyChain (batch_t *batch)
//you can't please them all.
if (r_worldentity.model->fromgame != fg_quake3)
GL_SkyForceDepth(batch);
return true;
}
/*

View file

@ -747,7 +747,7 @@ batch_t *D3D9BE_GetTempBatch(void);
void D3D9BE_GenBrushModelVBO(model_t *mod);
void D3D9BE_ClearVBO(vbo_t *vbo);
void D3D9BE_UploadAllLightmaps(void);
void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis);
void D3D9BE_DrawWorld (batch_t **worldbatches, qbyte *vis);
qboolean D3D9BE_LightCullModel(vec3_t org, model_t *model);
void D3D9BE_SelectEntity(entity_t *ent);
qboolean D3D9BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);
@ -771,7 +771,7 @@ batch_t *D3D11BE_GetTempBatch(void);
void D3D11BE_GenBrushModelVBO(model_t *mod);
void D3D11BE_ClearVBO(vbo_t *vbo);
void D3D11BE_UploadAllLightmaps(void);
void D3D11BE_DrawWorld (qboolean drawworld, qbyte *vis);
void D3D11BE_DrawWorld (batch_t **worldbatches, qbyte *vis);
qboolean D3D11BE_LightCullModel(vec3_t org, model_t *model);
void D3D11BE_SelectEntity(entity_t *ent);
qboolean D3D11BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);

View file

@ -727,4 +727,5 @@ r_part TEQ2_BOSSTPORT
gravity -800
rgbf 1 1 1
scalefactor 0.8
sound "misc/bigtele.wav" 1 0 0 0 1
}

View file

@ -490,6 +490,8 @@ static void PDECL PR_Configure (pubprogfuncs_t *ppf, size_t addressable_size, in
{
#if defined(_WIN64) && !defined(WINRT)
addressable_size = 0x80000000; //use of virtual address space rather than physical memory means we can just go crazy and use the max of 2gb.
#elif defined(FTE_TARGET_WEB)
addressable_size = 8*1024*1024;
#else
addressable_size = 32*1024*1024;
#endif
@ -549,7 +551,7 @@ struct entvars_s *PDECL PR_entvars (pubprogfuncs_t *ppf, struct edict_s *ed)
return (struct entvars_s *)edvars(ed);
}
int PDECL PR_GetFuncArgCount(pubprogfuncs_t *ppf, func_t func)
pbool PDECL PR_GetFunctionInfo(pubprogfuncs_t *ppf, func_t func, int *args, int *builtinnum, char *funcname, size_t funcnamesize)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
@ -561,13 +563,26 @@ int PDECL PR_GetFuncArgCount(pubprogfuncs_t *ppf, func_t func)
fnum = (func & 0x00ffffff);
if (pnum >= prinst.maxprogs || !pr_progstate[pnum].functions)
return -1;
return false;
else if (fnum >= pr_progstate[pnum].progs->numfunctions)
return -1;
return false;
else
{
f = pr_progstate[pnum].functions + fnum;
return f->numparms;
if (args)
*args = f->numparms;
if (builtinnum)
*builtinnum = -f->first_statement;
if (funcname)
{
const char *srcname = PR_StringToNative(ppf, f->s_name);
size_t nlen = strlen(srcname);
if (nlen < funcnamesize)
memcpy(funcname, srcname, nlen+1);
else
*funcname = 0;
}
return true;
}
}
@ -1327,7 +1342,7 @@ pubprogfuncs_t deffuncs = {
QC_AddSharedVar,
QC_AddSharedFieldVar,
PR_RemoveProgsString,
PR_GetFuncArgCount,
PR_GetFunctionInfo,
PR_GenerateStatementString,
ED_FieldInfo,
PR_UglyValueString,

View file

@ -3232,7 +3232,7 @@ retry:
PR_CleanUpStatements16(progfuncs, st16, hexencalling);
break;
case PST_KKQWSV: //24 sucks. Guess why.
case PST_KKQWSV:
case PST_FTE32:
for (i=0 ; i<pr_progs->numstatements ; i++)
{

View file

@ -1942,8 +1942,20 @@ pbool PDECL PR_GetBuiltinCallInfo (pubprogfuncs_t *ppf, int *builtinnum, char *f
int op;
int a;
const char *fname;
op = pr_statements16[st].op;
a = pr_statements16[st].a;
switch (current_progstate->structtype)
{
case PST_DEFAULT:
case PST_QTEST:
op = pr_statements16[st].op;
a = pr_statements16[st].a;
break;
case PST_KKQWSV:
case PST_FTE32:
op = pr_statements32[st].op;
a = pr_statements32[st].a;
break;
}
*builtinnum = 0;
*function = 0;

View file

@ -292,7 +292,7 @@ typedef enum
PST_DEFAULT,//everything 16bit
PST_FTE32, //everything 32bit
PST_KKQWSV, //32bit statements, 16bit globaldefs. NO SAVED GAMES.
PST_QTEST, //16bit statements, 32bit globaldefs(differences converted on load)
PST_QTEST, //16bit statements, 32bit globaldefs(other differences converted on load)
} progstructtype_t;
#ifndef COMPILER

View file

@ -176,7 +176,7 @@ struct pubprogfuncs_s
void (PDECL *AddSharedVar) (pubprogfuncs_t *progfuncs, int start, int size);
void (PDECL *AddSharedFieldVar) (pubprogfuncs_t *progfuncs, int num, char *relstringtable);
char *(PDECL *RemoveProgsString) (pubprogfuncs_t *progfuncs, string_t str);
int (PDECL *GetFuncArgCount) (pubprogfuncs_t *progfuncs, func_t func); //ask how many args a function is meant to have
pbool (PDECL *GetFunctionInfo) (pubprogfuncs_t *progfuncs, func_t func, int *argcount, int *builtinnum, char *funcname, size_t funcnamesize); //queries the interesting info from a function def
void (PDECL *GenerateStatementString) (pubprogfuncs_t *progfuncs, int statementnum, char *out, int outlen); //disassembles a specific statement. for debugging reports.
fdef_t *(PDECL *FieldInfo) (pubprogfuncs_t *progfuncs, unsigned int *count);
char *(PDECL *UglyValueString) (pubprogfuncs_t *progfuncs, etype_t type, union eval_s *val);

View file

@ -976,6 +976,7 @@ extern int maxtypeinfos;
extern int ForcedCRC;
extern pbool defaultnoref;
extern pbool defaultnosave;
extern pbool defaultstatic;
extern int *qcc_tempofs;

View file

@ -2274,7 +2274,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_
case OP_MUL_FI:
QCC_FreeTemp(var_a); QCC_FreeTemp(var_b);
optres_constantarithmatic++;
return QCC_MakeIntConst(eval_a->_float * eval_b->_int);
return QCC_MakeFloatConst(eval_a->_float * eval_b->_int);
case OP_DIV_I:
QCC_FreeTemp(var_a); QCC_FreeTemp(var_b);
optres_constantarithmatic++;
@ -12547,8 +12547,8 @@ void QCC_PR_ParseEnum(pbool flags)
const char *name;
QCC_sref_t sref;
pbool wantint = false;
int iv = 0;
float fv = 0;
int iv = flags?1:0;
float fv = iv;
if (QCC_PR_CheckKeyword(keyword_integer, "integer") || QCC_PR_CheckKeyword(keyword_int, "int"))
wantint = true;
@ -12657,7 +12657,7 @@ void QCC_PR_ParseDefs (char *classname)
pbool isconstant = false;
pbool isvar = false;
pbool noref = defaultnoref;
pbool nosave = false;
pbool nosave = defaultnosave;
pbool allocatenew = true;
pbool inlinefunction = false;
pbool allowinline = false;
@ -13273,7 +13273,7 @@ void QCC_PR_ParseDefs (char *classname)
else
{
if (type->type == ev_function)
isconstant = !isvar;
isconstant |= !isvar && !pr_scope;
if (dostrip)
{

View file

@ -239,6 +239,7 @@ void QCC_FindBestInclude(char *newfile, char *currentfile, char *rootpath, pbool
}
pbool defaultnoref;
pbool defaultnosave;
pbool defaultstatic;
int ForcedCRC;
int QCC_PR_LexInteger (void);
@ -955,13 +956,11 @@ pbool QCC_PR_Precompiler(void)
ForcedCRC = atoi(msg);
}
else if (!QC_strcasecmp(qcc_token, "noref"))
{
defaultnoref = !!atoi(msg);
}
else if (!QC_strcasecmp(qcc_token, "nosave"))
defaultnosave = !!atoi(msg);
else if (!QC_strcasecmp(qcc_token, "defaultstatic"))
{
defaultstatic = !!atoi(msg);
}
else if (!QC_strcasecmp(qcc_token, "autoproto"))
{
if (!autoprototyped)

View file

@ -5022,6 +5022,8 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
}
}
InitCommonControls();
if (!fl_acc && !*progssrcname)
{
strcpy(progssrcname, "preprogs.src");
@ -5101,7 +5103,6 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
return 0;
}
InitCommonControls();
/*
outputbox=CreateWindowEx(WS_EX_CLIENTEDGE,
"EDIT",

View file

@ -340,10 +340,10 @@ void GUI_LoadConfig(void)
QC_strlcpy(enginebasedir, GUI_ParseInPlace(&str), sizeof(enginebasedir));
else if (!stricmp(token, "engineargs"))
QC_strlcpy(enginecommandline, GUI_ParseInPlace(&str), sizeof(enginecommandline));
else if (!stricmp(token, "srcfile"))
QC_strlcpy(progssrcname, GUI_ParseInPlace(&str), sizeof(progssrcname));
else if (!stricmp(token, "src"))
QC_strlcpy(progssrcdir, GUI_ParseInPlace(&str), sizeof(progssrcdir));
// else if (!stricmp(token, "srcfile"))
// QC_strlcpy(progssrcname, GUI_ParseInPlace(&str), sizeof(progssrcname));
// else if (!stricmp(token, "src"))
// QC_strlcpy(progssrcdir, GUI_ParseInPlace(&str), sizeof(progssrcdir));
else if (!stricmp(token, "parameters"))
QC_strlcpy(parameters, GUI_ParseInPlace(&str), sizeof(parameters));

View file

@ -1435,7 +1435,7 @@ pbool QCC_WriteData (int crc)
if (!precache_model[i].used)
dupewarncount+=QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_model[i].filename, precache_model[i].fileline, (dupewarncount>10&&!verbose)?NULL:"Model \"%s\" was precached but not directly used%s", precache_model[i].name, dupewarncount?"":" (annotate the usage with the used_model intrinsic to silence this warning)");
else if (!precache_model[i].block)
dupewarncount+=QCC_PR_Warning(WARN_NOTPRECACHED, precache_model[i].filename, precache_model[i].fileline, (dupewarncount>10&&!verbose)?NULL:"Model \"%s\" was used but not precached", precache_model[i].name);
dupewarncount+=QCC_PR_Warning(WARN_NOTPRECACHED, precache_model[i].filename, precache_model[i].fileline, (dupewarncount>10&&!verbose)?NULL:"Model \"%s\" was used but not directly precached", precache_model[i].name);
}
for (i = 0; i < numsounds; i++)
@ -1443,7 +1443,7 @@ pbool QCC_WriteData (int crc)
if (!precache_sound[i].used)
dupewarncount+=QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_sound[i].filename, precache_sound[i].fileline, (dupewarncount>10&&!verbose)?NULL:"Sound \"%s\" was precached but not directly used", precache_sound[i].name, dupewarncount?"":" (annotate the usage with the used_sound intrinsic to silence this warning)");
else if (!precache_sound[i].block)
dupewarncount+=QCC_PR_Warning(WARN_NOTPRECACHED, precache_sound[i].filename, precache_sound[i].fileline, (dupewarncount>10&&!verbose)?NULL:"Sound \"%s\" was used but not precached", precache_sound[i].name);
dupewarncount+=QCC_PR_Warning(WARN_NOTPRECACHED, precache_sound[i].filename, precache_sound[i].fileline, (dupewarncount>10&&!verbose)?NULL:"Sound \"%s\" was used but not directly precached", precache_sound[i].name);
}
if (dupewarncount > 10 && !verbose)

View file

@ -227,6 +227,13 @@ void PDECL ED_Spawned (struct edict_s *ent, int loading)
ent->xv->Version = sv.csqcentversion[ent->entnum];
ent->xv->uniquespawnid = sv.csqcentversion[ent->entnum];
if (!ent->baseline.number)
{ //make sure it has a valid baseline
extern entity_state_t nullentitystate;
memcpy(&ent->baseline, &nullentitystate, sizeof(ent->baseline));
ent->baseline.number = ent->entnum;
}
}
}
@ -2094,7 +2101,7 @@ static void SV_Effect(vec3_t org, int mdlidx, int startframe, int endframe, int
#endif
}
SV_Multicast(org, MULTICAST_PVS);
SV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);
}
#ifdef HEXEN2
@ -2848,7 +2855,7 @@ static void QCBUILTIN PF_te_blooddp (pubprogfuncs_t *prinst, globalvars_t *pr_gl
MSG_WriteCoord (&sv.multicast, org[0]);
MSG_WriteCoord (&sv.multicast, org[1]);
MSG_WriteCoord (&sv.multicast, org[2]);
SV_Multicast(org, MULTICAST_PVS);
SV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);
}
#ifdef HEXEN2
@ -3117,9 +3124,9 @@ static void QCBUILTIN PF_pointsound(pubprogfuncs_t *prinst, struct globalvars_s
}
//an evil one from telejano.
#ifndef SERVERONLY
static void QCBUILTIN PF_LocalSound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
#ifndef SERVERONLY
sfx_t *sfx;
const char * s = PR_GetStringOfs(prinst, OFS_PARM0);
@ -3131,8 +3138,10 @@ static void QCBUILTIN PF_LocalSound(pubprogfuncs_t *prinst, struct globalvars_s
if ((sfx = S_PrecacheSound(s)))
S_StartSound(cl.playerview[0].playernum, chan, sfx, cl.playerview[0].simorg, vol, 0.0, 0, 0, CF_NOSPACIALISE);
}
#endif
};
#else
#define PF_LocalSound PF_Fixme
#endif
static void set_trace_globals(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, trace_t *trace)
{
@ -3768,9 +3777,9 @@ void QCBUILTIN PF_sv_particleeffectnum(pubprogfuncs_t *prinst, struct globalvars
for (i=1 ; i<MAX_SSPARTICLESPRE ; i++)
{
if (!*sv.strings.particle_precache[i])
if (!sv.strings.particle_precache[i])
{
strcpy(sv.strings.particle_precache[i], s);
sv.strings.particle_precache[i] = PR_AddString(prinst, s, 0, false);
if (sv.state != ss_loading)
{
@ -3818,9 +3827,9 @@ int PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s)
for (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)
{
if (!*sv.strings.sound_precache[i])
if (!sv.strings.sound_precache[i])
{
Q_strncpyz(sv.strings.sound_precache[i], s, sizeof(sv.strings.sound_precache[i]));
sv.strings.sound_precache[i] = PR_AddString(prinst, s, 0, false);
/*touch the file, so any packs will be referenced*/
FS_FLocateFile(s, FSLF_IFFOUND, NULL);
@ -4084,7 +4093,7 @@ void QCBUILTIN PF_applylightstyle(int style, const char *val, vec3_t rgb)
sv.strings.lightstyles[style] = Z_StrDup(val);
#ifdef PEXT_LIGHTSTYLECOL
VectorCopy(rgb, sv.strings.lightstylecolours[style]);
VectorCopy(rgb, sv.lightstylecolours[style]);
#endif
// send message to all clients on this server
@ -5530,7 +5539,7 @@ void QCBUILTIN PF_multicast (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
NPP_Flush();
#endif
SV_Multicast (o, to);
SV_MulticastProtExt(o, to, pr_global_struct->dimension_send, 0, 0);
}
static void QCBUILTIN PF_Ignore(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
@ -6136,6 +6145,33 @@ static void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalva
G_FLOAT(OFS_RETURN) = false;
}
static void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
func_t funcref = G_INT(OFS_PARM0);
char *funcname = NULL;
int args;
int builtinno;
if (prinst->GetFunctionInfo(prinst, funcref, &args, &builtinno, funcname, sizeof(funcname)))
{ //qc defines the function at least. nothing weird there...
if (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins)
{
if (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme)
G_FLOAT(OFS_RETURN) = false; //the builtin with that number isn't defined.
else
{
G_FLOAT(OFS_RETURN) = true; //its defined, within the sane range, mapped, everything. all looks good.
//we should probably go through the available builtins and validate that the qc's name matches what would be expected
//this is really intended more for builtins defined as #0 though, in such cases, mismatched assumptions are impossible.
}
}
else
G_FLOAT(OFS_RETURN) = false; //not a valid builtin (#0 builtins get remapped according to the function name)
}
else
{ //not valid somehow.
G_FLOAT(OFS_RETURN) = false;
}
}
static void QCBUILTIN PF_builtinsupported (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -7699,7 +7735,7 @@ static void QCBUILTIN PF_h2rain_go(pubprogfuncs_t *prinst, struct globalvars_s *
MSG_WriteShort(&sv.multicast, min(count, 65535));
// colour
MSG_WriteByte(&sv.multicast, (int)colour&0xff);
SV_Multicast(NULL, MULTICAST_ALL);
SV_MulticastProtExt (NULL, MULTICAST_ALL, pr_global_struct->dimension_send, 0, 0);
}
static void QCBUILTIN PF_h2StopSound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
@ -8290,7 +8326,7 @@ static void QCBUILTIN PF_te_spark(pubprogfuncs_t *prinst, struct globalvars_s *p
// count
MSG_WriteByte(&sv.nqmulticast, bound(0, (int) G_FLOAT(OFS_PARM2), 255));
#endif
SV_Multicast(org, MULTICAST_PVS);
SV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);
}
// #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
@ -8338,7 +8374,7 @@ static void QCBUILTIN PF_te_customflash(pubprogfuncs_t *prinst, struct globalvar
MSG_WriteByte(&sv.nqmulticast, bound(0, G_VECTOR(OFS_PARM3)[1] * 255, 255));
MSG_WriteByte(&sv.nqmulticast, bound(0, G_VECTOR(OFS_PARM3)[2] * 255, 255));
#endif
SV_Multicast(org, MULTICAST_PVS);
SV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);
}
//#408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
@ -8395,7 +8431,7 @@ static void QCBUILTIN PF_te_particlecube(pubprogfuncs_t *prinst, struct globalva
VectorAdd(min, max, org);
VectorScale(org, 0.5, org);
SV_Multicast(org, MULTICAST_PVS);
SV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);
}
static void QCBUILTIN PF_te_explosionrgb(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
@ -8425,7 +8461,7 @@ static void QCBUILTIN PF_te_explosionrgb(pubprogfuncs_t *prinst, struct globalva
MSG_WriteByte(&sv.nqmulticast, bound(0, (int) (colour[1] * 255), 255));
MSG_WriteByte(&sv.nqmulticast, bound(0, (int) (colour[2] * 255), 255));
#endif
SV_Multicast(org, MULTICAST_PVS);
SV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);
}
static void QCBUILTIN PF_te_particlerain(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
@ -8479,7 +8515,7 @@ static void QCBUILTIN PF_te_particlerain(pubprogfuncs_t *prinst, struct globalva
MSG_WriteByte(&sv.nqmulticast, colour);
#endif
SV_Multicast(NULL, MULTICAST_ALL);
SV_MulticastProtExt (NULL, MULTICAST_ALL, pr_global_struct->dimension_send, 0, 0);
}
static void QCBUILTIN PF_te_particlesnow(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
@ -8533,7 +8569,7 @@ static void QCBUILTIN PF_te_particlesnow(pubprogfuncs_t *prinst, struct globalva
MSG_WriteByte(&sv.nqmulticast, colour);
#endif
SV_Multicast(NULL, MULTICAST_ALL);
SV_MulticastProtExt (NULL, MULTICAST_ALL, pr_global_struct->dimension_send, 0, 0);
}
// #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
@ -8580,7 +8616,7 @@ static void QCBUILTIN PF_te_bloodshower(pubprogfuncs_t *prinst, struct globalvar
VectorAdd(min, max, org);
VectorScale(org, 0.5, org);
SV_Multicast(org, MULTICAST_PVS); //fixme: should this be phs instead?
SV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0); //fixme: should this be phs instead?
}
//DP_SV_EFFECT
@ -9706,10 +9742,10 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"logarithm", PF_Logarithm, 0, 0, 0, 0, D("float(float v, optional float base)", "Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by.")},
{"tj_cvar_string", PF_cvar_string, 0, 0, 0, 97, D("string(string cvarname)",NULL), true}, //telejano
//DP_QC_FINDFLOAT
{"findfloat", PF_FindFloat, 0, 0, 0, 98, D("entity(entity start, .float fld, float match)", "Equivelent to the find builtin, but instead of comparing strings, this builtin compares floats. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value.\nworld is returned when there are no more entities.")}, // #98 (DP_QC_FINDFLOAT)
{"findentity", PF_FindFloat, 0, 0, 0, 98, D("entity(entity start, .entity fld, entity match)", "Equivelent to the find builtin, but instead of comparing strings, this builtin compares entities. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value.\nworld is returned when there are no more entities.")}, // #98 (DP_QC_FINDFLOAT)
{"findfloat", PF_FindFloat, 0, 0, 0, 98, D("#define findentity findfloat\nentity(entity start, .__variant fld, __variant match)", "Equivelent to the find builtin, but instead of comparing strings contents, this builtin compares the raw values. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value.\nworld is returned when there are no more entities.")}, // #98 (DP_QC_FINDFLOAT)
{"checkextension", PF_checkextension, 99, 99, 0, 99, D("float(string extname)", "Checks for an extension by its name (eg: checkextension(\"FRIK_FILE\") says that its okay to go ahead and use strcat).\nUse cvar(\"pr_checkextension\") to see if this builtin exists.")}, // #99 //darkplaces system - query a string to see if the mod supports X Y and Z.
{"checkbuiltin", PF_checkbuiltin, 0, 0, 0, 0, D("float(__variant funcref)", "Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions.")},
{"builtin_find", PF_builtinsupported,100, 100, 0, 100, D("float(string builtinname)", "Looks to see if the named builtin is valid, and returns the builtin number it exists at.")}, // #100 //per builtin system.
{"anglemod", PF_anglemod, 0, 0, 0, 102, "float(float value)"},
{"qsg_cvar_string", PF_cvar_string, 0, 0, 0, 103, D("string(string cvarname)","An old/legacy equivelent of more recent/common builtins in order to read a cvar's string value."), true},
@ -9843,8 +9879,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"sqlescape", PF_sqlescape, 0, 0, 0, 256, "string(float serveridx, string data)"}, // sqlescape (FTE_SQL)
{"sqlversion", PF_sqlversion, 0, 0, 0, 257, "string(float serveridx)"}, // sqlversion (FTE_SQL)
{"sqlreadfloat", PF_sqlreadfloat, 0, 0, 0, 258, "float(float serveridx, float queryidx, float row, float column)"}, // sqlreadfloat (FTE_SQL)
{"sqlreadblob", PF_sqlreadblob, 0, 0, 0, 0, "int(float serveridx, float queryidx, float row, float column, _variant *ptr, int maxsize)"},
{"sqlescapeblob", PF_sqlescapeblob, 0, 0, 0, 0, "string(float serveridx, _variant *ptr, int maxsize)"},
{"sqlreadblob", PF_sqlreadblob, 0, 0, 0, 0, "int(float serveridx, float queryidx, float row, float column, __variant *ptr, int maxsize)"},
{"sqlescapeblob", PF_sqlescapeblob, 0, 0, 0, 0, "string(float serveridx, __variant *ptr, int maxsize)"},
{"stoi", PF_stoi, 0, 0, 0, 259, D("int(string)", "Converts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string.")},
{"itos", PF_itos, 0, 0, 0, 260, D("string(int)", "Converts the passed true integer into a base10 string.")},
@ -10573,7 +10609,7 @@ void PR_ResetBuiltins(progstype_t type) //fix all nulls to PF_FIXME and add any
void PR_SVExtensionList_f(void)
{
int i;
int i, j;
int ebi;
int bi;
lh_extension_t *extlist;
@ -10630,6 +10666,8 @@ void PR_SVExtensionList_f(void)
{
for (bi = 0; BuiltinList[bi].name; bi++)
{
if (BuiltinList[bi].bifunc == PF_Fixme)
continue; //this builtin is unusable in ssqc. some of them are listed because of menuqc
if (!strcmp(BuiltinList[bi].name, extlist[i].builtinnames[ebi]))
break;
}
@ -10650,7 +10688,17 @@ void PR_SVExtensionList_f(void)
else
{
if (showflags & SHOW_NOTACTIVEEXT)
{
Con_Printf("^4%s was overridden (builtin: %s#%i)\n", extlist[i].name, BuiltinList[bi].name, BuiltinList[bi].ebfsnum);
for (j = 0; BuiltinList[j].name; j++)
{
if (BuiltinList[j].bifunc == pr_builtin[BuiltinList[bi].ebfsnum])
{
Con_Printf("^4%s is currently %s (#%i)\n", extlist[i].name, BuiltinList[j].name, BuiltinList[j].ebfsnum);
break;
}
}
}
}
break;
}
@ -10659,10 +10707,20 @@ void PR_SVExtensionList_f(void)
{
if (showflags & SHOW_ACTIVEEXT)
{
if (!extlist[i].numbuiltins)
Con_Printf("%s is supported\n", extlist[i].name);
if (extlist[i].description)
{
if (!extlist[i].numbuiltins)
Con_Printf("^[%s\\tip\\%s^] is supported\n", extlist[i].name, extlist[i].description);
else
Con_Printf("^[%s\\tip\\%s^] is currently active\n", extlist[i].name, extlist[i].description);
}
else
Con_Printf("%s is currently active\n", extlist[i].name);
{
if (!extlist[i].numbuiltins)
Con_Printf("%s is supported\n", extlist[i].name);
else
Con_Printf("%s is currently active\n", extlist[i].name);
}
}
}
}
@ -11131,12 +11189,13 @@ void PR_DumpPlatform_f(void)
{"CHAN_VOICE", "const float", QW|NQ|CS, NULL, CHAN_VOICE},
{"CHAN_ITEM", "const float", QW|NQ|CS, NULL, CHAN_ITEM},
{"CHAN_BODY", "const float", QW|NQ|CS, NULL, CHAN_BODY},
{"CHANF_RELIABLE", "const float", QW, NULL, 8},
{"CHANF_RELIABLE", "const float", QW, "Only valid if the flags argument is not specified. The sound will be sent reliably, which is important if it is intended to replace looping sounds on doors etc.", 8},
{"SOUNDFLAG_RELIABLE", "const float", QW|NQ, "The sound will be sent reliably, and without regard to phs.", CF_RELIABLE},
{"SOUNDFLAG_ABSVOLUME", "const float", /*QW|NQ|*/CS,"The sample's volume is not scaled by the volume cvar. Use with caution", CF_ABSVOLUME},
{"SOUNDFLAG_FORCELOOP", "const float", /*QW|NQ|*/CS,"The sound will restart once it reaches the end of the sample.", CF_FORCELOOP},
{"SOUNDFLAG_FORCELOOP", "const float", QW|NQ|CS,"The sound will restart once it reaches the end of the sample.", CF_FORCELOOP},
{"SOUNDFLAG_NOSPACIALISE", "const float", /*QW|NQ|*/CS,"The different audio channels are played at the same volume regardless of which way the player is facing, without needing to use 0 attenuation.", CF_NOSPACIALISE},
{"SOUNDFLAG_UNICAST", "const float", QW|NQ, "The sound will be heard only by the player specified by msg_entity.", CF_UNICAST},
{"ATTN_NONE", "const float", QW|NQ|CS, "Sounds with this attenuation can be heard throughout the map", ATTN_NONE},
{"ATTN_NORM", "const float", QW|NQ|CS, "Standard attenuation", ATTN_NORM},
@ -11274,6 +11333,7 @@ void PR_DumpPlatform_f(void)
{"EF_RED", "const float", QW|NQ|CS, NULL, EF_RED},
{"EF_GREEN", "const float", QW|NQ|CS, NULL, EF_GREEN},
{"EF_FULLBRIGHT", "const float", QW|NQ|CS, NULL, EF_FULLBRIGHT},
{"EF_NOSHADOW", "const float", QW|NQ|CS, NULL, EF_NOSHADOW},
{"EF_NODEPTHTEST", "const float", QW|NQ|CS, NULL, EF_NODEPTHTEST},
{"EF_NOMODELFLAGS", "const float", QW|NQ, "Surpresses the normal flags specified in the model.", EF_NOMODELFLAGS},

View file

@ -182,10 +182,10 @@ struct q2edict_s
typedef struct
{
// special messages
void (VARGS *bprintf) (int printlevel, char *fmt, ...) LIKEPRINTF(2);
void (VARGS *dprintf) (char *fmt, ...) LIKEPRINTF(1);
void (VARGS *cprintf) (q2edict_t *ent, int printlevel, char *fmt, ...) LIKEPRINTF(3);
void (VARGS *centerprintf) (q2edict_t *ent, char *fmt, ...) LIKEPRINTF(2);
void (VARGS *bprintf) (int printlevel, const char *fmt, ...) LIKEPRINTF(2);
void (VARGS *dprintf) (const char *fmt, ...) LIKEPRINTF(1);
void (VARGS *cprintf) (q2edict_t *ent, int printlevel, const char *fmt, ...) LIKEPRINTF(3);
void (VARGS *centerprintf) (q2edict_t *ent, const char *fmt, ...) LIKEPRINTF(2);
void (VARGS *sound) (q2edict_t *ent, int channel, int soundindex, float volume, float attenuation, float timeofs);
void (VARGS *positioned_sound) (vec3_t origin, q2edict_t *ent, int channel, int soundinedex, float volume, float attenuation, float timeofs);
@ -193,16 +193,16 @@ typedef struct
// and misc data like the sky definition and cdtrack.
// All of the current configstrings are sent to clients when
// they connect, and changes are sent to all connected clients.
void (VARGS *configstring) (int num, char *string);
void (VARGS *configstring) (int num, const char *string);
void (VARGS *error) (char *fmt, ...) LIKEPRINTF(1);
void (VARGS *error) (const char *fmt, ...) LIKEPRINTF(1);
// the *index functions create configstrings and some internal server state
int (VARGS *modelindex) (char *name);
int (VARGS *soundindex) (char *name);
int (VARGS *imageindex) (char *name);
int (VARGS *modelindex) (const char *name);
int (VARGS *soundindex) (const char *name);
int (VARGS *imageindex) (const char *name);
void (VARGS *setmodel) (q2edict_t *ent, char *name);
void (VARGS *setmodel) (q2edict_t *ent, const char *name);
// collision detection
q2trace_t (VARGS *trace) (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, q2edict_t *passent, int contentmask);
@ -228,7 +228,7 @@ typedef struct
void (VARGS *WriteShort) (int c);
void (VARGS *WriteLong) (int c);
void (VARGS *WriteFloat) (float f);
void (VARGS *WriteString) (char *s);
void (VARGS *WriteString) (const char *s);
void (VARGS *WritePosition) (vec3_t pos); // some fractional bits
void (VARGS *WriteDir) (vec3_t pos); // single byte encoded, very coarse
void (VARGS *WriteAngle) (float f);
@ -239,9 +239,9 @@ typedef struct
void (VARGS *FreeTags) (int tag);
// console variable interaction
cvar_t *(VARGS *cvar) (char *var_name, char *value, int flags);
cvar_t *(VARGS *cvar_set) (char *var_name, char *value);
cvar_t *(VARGS *cvar_forceset) (char *var_name, char *value);
cvar_t *(VARGS *cvar) (const char *var_name, const char *value, int flags);
cvar_t *(VARGS *cvar_set) (const char *var_name, const char *value);
cvar_t *(VARGS *cvar_forceset) (const char *var_name, const char *value);
// ClientCommand and ServerCommand parameter access
int (VARGS *argc) (void);
@ -250,7 +250,7 @@ typedef struct
// add commands to the server console as if they were typed in
// for map changing, etc
void (VARGS *AddCommandString) (char *text);
void (VARGS *AddCommandString) (const char *text);
void (VARGS *DebugGraph) (float value, int color);
} game_import_t;
@ -269,19 +269,19 @@ typedef struct
void (VARGS *Shutdown) (void);
// each new level entered will cause a call to SpawnEntities
void (VARGS *SpawnEntities) (char *mapname, char *entstring, char *spawnpoint);
void (VARGS *SpawnEntities) (const char *mapname, const char *entstring, const char *spawnpoint);
// Read/Write Game is for storing persistant cross level information
// about the world state and the clients.
// WriteGame is called every time a level is exited.
// ReadGame is called on a loadgame.
void (VARGS *WriteGame) (char *filename, qboolean autosave);
void (VARGS *ReadGame) (char *filename);
void (VARGS *WriteGame) (const char *filename, qboolean autosave);
void (VARGS *ReadGame) (const char *filename);
// ReadLevel is called after the default map information has been
// loaded with SpawnEntities
void (VARGS *WriteLevel) (char *filename);
void (VARGS *ReadLevel) (char *filename);
void (VARGS *WriteLevel) (const char *filename);
void (VARGS *ReadLevel) (const char *filename);
qboolean (VARGS *ClientConnect) (q2edict_t *ent, char *userinfo);
void (VARGS *ClientBegin) (q2edict_t *ent);

View file

@ -1,4 +1,5 @@
#include "quakedef.h"
#include "pr_common.h"
#ifndef CLIENTONLY
@ -94,6 +95,7 @@ void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
char *file;
char *modelnames[MAX_PRECACHE_MODELS];
char *soundnames[MAX_PRECACHE_SOUNDS];
if (version != 667 && version != 5 && version != 6) //5 for NQ, 6 for ZQ/FQ
{
@ -265,6 +267,15 @@ void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
}
modelnames[i] = Z_StrDup(sv.strings.model_precache[i]);
}
for (i = 1; i < MAX_PRECACHE_SOUNDS; i++)
{
if (!sv.strings.sound_precache[i])
{
soundnames[i] = NULL;
break;
}
soundnames[i] = Z_StrDup(sv.strings.sound_precache[i]);
}
// load the edicts out of the savegame file
// the rest of the file is sent directly to the progs engine.
@ -288,6 +299,14 @@ void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
if (!modelnames[i])
break;
sv.strings.model_precache[i] = PR_AddString(svprogfuncs, modelnames[i], 0, false);
Z_Free(modelnames[i]);
}
for (i = 1; i < MAX_PRECACHE_SOUNDS; i++)
{
if (!soundnames[i])
break;
sv.strings.sound_precache[i] = PR_AddString(svprogfuncs, soundnames[i], 0, false);
Z_Free(soundnames[i]);
}
filepos = VFS_TELL(f);
@ -521,14 +540,14 @@ void LoadModelsAndSounds(vfsfile_t *f)
for (; i < MAX_PRECACHE_MODELS; i++)
sv.strings.model_precache[i] = NULL;
// sv.sound_precache[0] = PR_AddString(svprogfuncs, "", 0);
sv.strings.sound_precache[0] = PR_AddString(svprogfuncs, "", 0, false);
for (i=1; i < MAX_PRECACHE_SOUNDS; i++)
{
VFS_GETS(f, str, sizeof(str));
if (!*str)
break;
// sv.sound_precache[i] = PR_AddString(svprogfuncs, str, 0);
sv.strings.sound_precache[i] = PR_AddString(svprogfuncs, str, 0, false);
}
if (i == MAX_PRECACHE_SOUNDS)
{
@ -537,11 +556,11 @@ void LoadModelsAndSounds(vfsfile_t *f)
SV_Error("Too many sound precaches in loadgame cache");
}
for (; i < MAX_PRECACHE_SOUNDS; i++)
*sv.strings.sound_precache[i] = 0;
sv.strings.sound_precache[i] = NULL;
}
/*ignoreplayers - says to not tell gamecode (a loadgame rather than a level change)*/
qboolean SV_LoadLevelCache(char *savename, char *level, char *startspot, qboolean isloadgame)
qboolean SV_LoadLevelCache(const char *savename, const char *level, const char *startspot, qboolean isloadgame)
{
eval_t *eval, *e2;
@ -847,7 +866,7 @@ qboolean SV_LoadLevelCache(char *savename, char *level, char *startspot, qboolea
return true; //yay
}
void SV_SaveLevelCache(char *savedir, qboolean dontharmgame)
void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
{
size_t len;
char *s;
@ -887,10 +906,9 @@ void SV_SaveLevelCache(char *savedir, qboolean dontharmgame)
}
if (savedir)
Q_snprintfz (name, sizeof(name), "saves/%s/%s", savedir, svs.name);
Q_snprintfz (name, sizeof(name), "saves/%s/%s.lvc", savedir, svs.name);
else
Q_snprintfz (name, sizeof(name), "saves/%s", svs.name);
COM_DefaultExtension (name, ".lvc", sizeof(name));
Q_snprintfz (name, sizeof(name), "saves/%s.lvc", svs.name);
FS_CreatePath(name, FS_GAMEONLY);
@ -985,15 +1003,15 @@ void SV_SaveLevelCache(char *savedir, qboolean dontharmgame)
for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
if (sv.strings.lightstyles[i])
VFS_PRINTF (f, "lstyle %i %s %f %f %f\n", i, COM_QuotedString(sv.strings.lightstyles[i], buf, sizeof(buf), false), sv.strings.lightstylecolours[i][0], sv.strings.lightstylecolours[i][1], sv.strings.lightstylecolours[i][2]);
VFS_PRINTF (f, "lstyle %i %s %f %f %f\n", i, COM_QuotedString(sv.strings.lightstyles[i], buf, sizeof(buf), false), sv.lightstylecolours[i][0], sv.lightstylecolours[i][1], sv.lightstylecolours[i][2]);
for (i=1 ; i<MAX_PRECACHE_MODELS ; i++)
if (sv.strings.model_precache[i] && *sv.strings.model_precache[i])
VFS_PRINTF (f, "model %i %s\n", i, COM_QuotedString(sv.strings.model_precache[i], buf, sizeof(buf), false));
for (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)
if (*sv.strings.sound_precache[i])
if (sv.strings.sound_precache[i] && *sv.strings.sound_precache[i])
VFS_PRINTF (f, "sound %i %s\n", i, COM_QuotedString(sv.strings.sound_precache[i], buf, sizeof(buf), false));
for (i=1 ; i<MAX_SSPARTICLESPRE ; i++)
if (*sv.strings.particle_precache[i])
if (sv.strings.particle_precache[i] && *sv.strings.particle_precache[i])
VFS_PRINTF (f, "particles %i %s\n", i, sv.strings.particle_precache[i]);
for (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++)
VFS_PRINTF (f, "vwep %i %s\n", i, COM_QuotedString(sv.strings.vw_model_precache[i], buf, sizeof(buf), false));
@ -1038,7 +1056,7 @@ void SV_SaveLevelCache(char *savedir, qboolean dontharmgame)
VFS_PRINTF (f,"\n");
for (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)
{
if (*sv.strings.sound_precache[i])
if (sv.strings.sound_precache[i] && *sv.strings.sound_precache[i])
VFS_PRINTF (f, "%s\n", sv.strings.sound_precache[i]);
else
break;
@ -1063,7 +1081,7 @@ void SV_SaveLevelCache(char *savedir, qboolean dontharmgame)
#define FTESAVEGAME_VERSION 25000
void SV_Savegame (char *savename)
void SV_Savegame (const char *savename, qboolean mapchange)
{
extern cvar_t nomonsters;
extern cvar_t gamecfg;
@ -1091,11 +1109,16 @@ void SV_Savegame (char *savename)
char str[MAX_LOCALINFO_STRING+1];
char *savefilename;
if (!sv.state)
if (!sv.state || sv.state == ss_clustermode)
{
Con_Printf("Server is not active - unable to save\n");
return;
}
if (sv.state == ss_cinematic)
{
Con_Printf("Server is playing a cinematic - unable to save\n");
return;
}
if (sv.allocated_client_slots == 1 && svs.gametype == GT_PROGS)
{
@ -1109,7 +1132,7 @@ void SV_Savegame (char *savename)
/*catch invalid names*/
if (!*savename || strstr(savename, ".."))
savename = "quicksav";
savename = "quick";
savefilename = va("saves/%s/info.fsv", savename);
FS_CreatePath(savefilename, FS_GAMEONLY);
@ -1126,6 +1149,12 @@ void SV_Savegame (char *savename)
VFS_PRINTF(f, "%i\n", sv.allocated_client_slots);
for (cl = svs.clients, clnum=0; clnum < sv.allocated_client_slots; cl++,clnum++)
{
//FIXME: the qc is only told about the new client when the client finally sends a begin.
// this means that if we save a client that is still connecting, the loading code HAS to assume that the qc already knows about the player.
// this means that such players would not be loaded properly anyway, and would bug out the server.
// so its best to not bother saving them at all. pro-top: don't save shortly after a map change in coop/sp.
//istobeloaded means that the qc has already been told about the client from a previous saved game, regardless of the fact that they're still technically connecting (this may even be a zombie with no actual client connection).
//note that autosave implies that we're saving on a map boundary. this is for q2 gamecode. q1 can't cope.
if (cl->state < cs_spawned && !cl->istobeloaded) //don't save if they are still connecting
{
VFS_PRINTF(f, "\n");
@ -1194,7 +1223,8 @@ void SV_Savegame (char *savename)
#ifndef SERVERONLY
//try to save screenshots automagically.
savefilename = va("saves/%s/screeny.%s", savename, scr_sshot_type.string);
Q_snprintfz(comment, sizeof(comment), "saves/%s/screeny.%s", savename, "tga");//scr_sshot_type.string);
savefilename = comment;
FS_Remove(savefilename, FS_GAMEONLY);
if (cls.state == ca_active && qrenderer > QR_NONE)
{
@ -1255,7 +1285,7 @@ void SV_Savegame (char *savename)
char syspath[256];
if (!FS_NativePath(va("saves/%s/game.gsv", savename), FS_GAMEONLY, syspath, sizeof(syspath)))
return;
ge->WriteGame(syspath, false);
ge->WriteGame(syspath, mapchange);
FS_FlushFSHashReally(true);
}
else
@ -1269,10 +1299,56 @@ void SV_Savegame (char *savename)
void SV_Savegame_f (void)
{
if (Cmd_Argc() <= 2)
SV_Savegame(Cmd_Argv(1));
SV_Savegame(Cmd_Argv(1), false);
else
Con_Printf("%s: invalid number of arguments\n", Cmd_Argv(0));
}
cvar_t sv_autosave = CVARD("sv_autosave", "1", "Interval for autosaves, in minutes.");
void SV_AutoSave(void)
{
#ifndef SERVERONLY
const char *autosavename;
int i;
if (sv_autosave.value <= 0)
return;
if (sv.state != ss_active)
return;
//don't bother to autosave multiplayer games.
//this may be problematic with splitscreen, but coop rules tend to apply there anyway.
if (sv.allocated_client_slots != 1)
return;
for (i = 0; i < sv.allocated_client_slots; i++)
{
if (svs.clients[i].state == cs_spawned)
{
if (svs.clients[i].edict->v->health <= 0)
return; //autosaves with a dead player are just cruel.
if ((int)svs.clients[i].edict->v->flags & (FL_GODMODE | FL_NOTARGET))
return; //autosaves to highlight cheaters is also just spiteful.
if (svs.clients[i].edict->v->movetype != MOVETYPE_WALK)
return; //noclip|fly are cheaters, toss|bounce are bad at playing. etc.
if (!((int)svs.clients[i].edict->v->flags & FL_ONGROUND))
return; //autosaves while people are jumping are awkward.
if (svs.clients[i].edict->v->velocity[0] || svs.clients[i].edict->v->velocity[1] || svs.clients[i].edict->v->velocity[2])
return; //people running around are likely to result in poor saves
}
}
autosavename = M_ChooseAutoSave();
Con_Printf("Autosaving to %s\n", autosavename);
SV_Savegame(autosavename, false);
sv.autosave_time = sv.time + sv_autosave.value * 60;
#endif
}
void SV_Loadgame_f (void)
{
levelcache_t *cache;
@ -1292,7 +1368,7 @@ void SV_Loadgame_f (void)
Q_strncpyz(savename, Cmd_Argv(1), sizeof(savename));
if (!*savename || strstr(savename, ".."))
strcpy(savename, "quicksav");
strcpy(savename, "quick");
Q_snprintfz (filename, sizeof(filename), "saves/%s/info.fsv", savename);
f = FS_OpenVFS (filename, "rb", FS_GAME);
@ -1511,5 +1587,7 @@ void SV_Loadgame_f (void)
SV_LoadLevelCache(savename, str, "", true);
sv.allocated_client_slots = slots;
sv.spawned_client_slots += loadzombies;
sv.autosave_time = sv.time + sv_autosave.value*60;
}
#endif

View file

@ -116,6 +116,7 @@ typedef struct
unsigned int csqcchecksum;
qboolean mapchangelocked;
double autosave_time;
double time;
double starttime;
int framenum;
@ -137,18 +138,23 @@ typedef struct
union {
#ifdef Q2SERVER
struct {
char configstring[Q2MAX_CONFIGSTRINGS][MAX_QPATH];
const char *configstring[Q2MAX_CONFIGSTRINGS];
const char *q2_extramodels[MAX_PRECACHE_MODELS]; // NULL terminated
const char *q2_extrasounds[MAX_PRECACHE_SOUNDS]; // NULL terminated
};
#endif
struct {
const char *vw_model_precache[32];
const char *vw_model_precache[32];
const char *model_precache[MAX_PRECACHE_MODELS]; // NULL terminated
char particle_precache[MAX_SSPARTICLESPRE][MAX_QPATH]; // NULL terminated
char sound_precache[MAX_PRECACHE_SOUNDS][MAX_QPATH]; // NULL terminated
const char *lightstyles[MAX_LIGHTSTYLES];
vec3_t lightstylecolours[MAX_LIGHTSTYLES];
const char *particle_precache[MAX_SSPARTICLESPRE]; // NULL terminated
const char *sound_precache[MAX_PRECACHE_SOUNDS]; // NULL terminated
const char *lightstyles[MAX_LIGHTSTYLES];
};
const char *ptrs[1];
} strings;
qboolean stringsalloced; //if true, we need to free the string pointers safely rather than just memsetting them to 0
vec3_t lightstylecolours[MAX_LIGHTSTYLES];
char h2miditrack[MAX_QPATH];
qbyte h2cdtrack;
@ -286,6 +292,7 @@ typedef struct
int *csqcentversion;//prevents ent versions from going backwards
} server_t;
void SV_WipeServerState(void);
typedef enum
{
@ -322,7 +329,8 @@ typedef struct //merge?
{
int areabytes;
qbyte areabits[MAX_Q2MAP_AREAS/8]; // portalarea visibility bits
q2player_state_t ps;
q2player_state_t ps[MAX_SPLITS]; //yuck
int clientnum[MAX_SPLITS];
int num_entities;
int first_entity; // into the circular sv_packet_entities[]
int senttime; // for ping calculations
@ -888,6 +896,7 @@ typedef struct
//=============================================================================
/*
// edict->movetype values
#define MOVETYPE_NONE 0 // never moves
#define MOVETYPE_ANGLENOCLIP 1
@ -920,6 +929,7 @@ typedef struct
#define DAMAGE_NO 0
#define DAMAGE_YES 1
#define DAMAGE_AIM 2
*/
#define PVSF_NORMALPVS 0x0
#define PVSF_NOTRACECHECK 0x1
@ -1024,6 +1034,7 @@ void SV_DropClient (client_t *drop);
struct quakeparms_s;
void SV_Init (struct quakeparms_s *parms);
void SV_ExecInitialConfigs(char *defaultexec);
void SV_ArgumentOverrides(void);
int SV_CalcPing (client_t *cl, qboolean forcecalc);
void SV_FullClientUpdate (client_t *client, client_t *to);
@ -1038,9 +1049,9 @@ 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.
void SV_SaveLevelCache(char *savename, qboolean dontharmgame);
void SV_Savegame (char *savename);
qboolean SV_LoadLevelCache(char *savename, char *level, char *startspot, qboolean ignoreplayers);
void SV_SaveLevelCache(const char *savename, qboolean dontharmgame);
void SV_Savegame (const char *savename, qboolean autosave);
qboolean SV_LoadLevelCache(const char *savename, const char *level, const char *startspot, qboolean ignoreplayers);
void SV_Physics_Client (edict_t *ent, int num);
@ -1101,7 +1112,7 @@ void MSV_Status(void);
//
// sv_init.c
//
void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean usecinematic);
void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, qboolean usecinematic);
void SV_UnspawnServer (void);
void SV_FlushSignon (void);
void SV_UpdateMaxPlayers(int newmax);
@ -1159,12 +1170,12 @@ void SVQ1_StartSound (float *origin, wedict_t *entity, int channel, const char *
void SV_PrintToClient(client_t *cl, int level, const char *string);
void SV_TPrintToClient(client_t *cl, int level, const char *string);
void SV_StuffcmdToClient(client_t *cl, const char *string);
void VARGS SV_ClientPrintf (client_t *cl, int level, char *fmt, ...) LIKEPRINTF(3);
void VARGS SV_ClientPrintf (client_t *cl, int level, const char *fmt, ...) LIKEPRINTF(3);
void VARGS SV_ClientTPrintf (client_t *cl, int level, translation_t text, ...);
void VARGS SV_BroadcastPrintf (int level, char *fmt, ...) LIKEPRINTF(2);
void VARGS SV_BroadcastPrintf (int level, const char *fmt, ...) LIKEPRINTF(2);
void VARGS SV_BroadcastTPrintf (int level, translation_t fmt, ...);
void VARGS SV_BroadcastCommand (char *fmt, ...) LIKEPRINTF(1);
void SV_SendServerInfoChange(char *key, const char *value);
void VARGS SV_BroadcastCommand (const char *fmt, ...) LIKEPRINTF(1);
void SV_SendServerInfoChange(const char *key, const char *value);
void SV_SendMessagesToAll (void);
void SV_FindModelNumbers (void);
@ -1227,7 +1238,7 @@ qboolean PR_ShouldTogglePause(client_t *initiator, qboolean pausedornot);
// sv_ents.c
//
void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignorepvs);
void SVFTE_EmitBaseline(entity_state_t *to, qboolean numberisimportant, sizebuf_t *msg, client_t *client);
void SVFTE_EmitBaseline(entity_state_t *to, qboolean numberisimportant, sizebuf_t *msg, unsigned int pext2);
void SVQ3Q1_BuildEntityPacket(client_t *client, packet_entities_t *pack);
int SV_HullNumForPlayer(int h2hull, float *mins, float *maxs);
void SV_GibFilterInit(void);
@ -1435,7 +1446,13 @@ void SV_FlushDemoSignon (void);
void DestFlush(qboolean compleate);
// savegame.c
void SV_LegacySavegame_f(void);
void SV_Savegame_f (void);
void SV_Loadgame_f (void);
void SV_AutoSave(void);
void SV_FlushLevelCache(void);
extern cvar_t sv_autosave;
int SV_RateForClient(client_t *cl);

View file

@ -18,6 +18,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include "pr_common.h"
#ifndef CLIENTONLY
@ -776,7 +777,8 @@ void SV_Map_f (void)
if (q2savetos0)
{
SV_Savegame("s0");
if (sv.state != ss_cinematic) //too weird.
SV_Savegame("s0", true);
}
@ -1761,7 +1763,7 @@ static void SV_Status_f (void)
break;
Con_Printf("models : %i/%i\n", count, MAX_PRECACHE_MODELS);
for (count = 1; count < MAX_PRECACHE_SOUNDS; count++)
if (!*sv.strings.sound_precache[count])
if (!sv.strings.sound_precache[count])
break;
Con_Printf("sounds : %i/%i\n", count, MAX_PRECACHE_SOUNDS);
}
@ -2023,7 +2025,7 @@ for (i = sv.mvdrecording?-1:0; i < sv.allocated_client_slots; i++) \
if ((cl = (i==-1?&demo.recorder:&svs.clients[i]))) \
if ((i == -1) || cl->state >= cs_connected)
void SV_SendServerInfoChange(char *key, const char *value)
void SV_SendServerInfoChange(const char *key, const char *value)
{
int i;
client_t *cl;

View file

@ -220,7 +220,7 @@ void MSV_MapCluster_f(void)
NET_InitServer();
//child processes return 0 and fall through
memset(&sv, 0, sizeof(sv));
SV_WipeServerState();
Q_strncpyz(sv.modelname, Cmd_Argv(1), sizeof(sv.modelname));
if (!*sv.modelname)
Q_strncpyz(sv.modelname, "start", sizeof(sv.modelname));

View file

@ -1167,13 +1167,13 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_
}
/*dump out the delta from baseline (used for baselines and statics, so has no svc)*/
void SVFTE_EmitBaseline(entity_state_t *to, qboolean numberisimportant, sizebuf_t *msg, client_t *client)
void SVFTE_EmitBaseline(entity_state_t *to, qboolean numberisimportant, sizebuf_t *msg, unsigned int pext2)
{
unsigned int bits;
if (numberisimportant)
MSG_WriteEntity(msg, to->number);
bits = UF_RESET | SVFTE_DeltaCalcBits(&nullentitystate, to);
SVFTE_WriteUpdate(bits, to, msg, client->fteprotocolextensions2);
SVFTE_WriteUpdate(bits, to, msg, pext2);
}
/*SVFTE_EmitPacketEntities

View file

@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include "pr_common.h"
#ifndef CLIENTONLY
extern int total_loading_size, current_loading_size, loading_stage;
char *T_GetString(int num);
@ -754,6 +755,18 @@ void SV_SetupNetworkBuffers(qboolean bigcoords)
sv.num_signon_buffers = 1;
}
void SV_WipeServerState(void)
{
if (sv.stringsalloced)
{
unsigned int i;
for (i = 0; i < sizeof(sv.strings) / sizeof(sv.strings.ptrs[0]); i++)
Z_Free((char*)sv.strings.ptrs[i]);
}
memset (&sv, 0, sizeof(sv));
sv.logindatabase = -1;
}
/*
================
SV_SpawnServer
@ -764,7 +777,7 @@ clients along with it.
This is only called from the SV_Map_f() function.
================
*/
void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean usecinematic)
void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, qboolean usecinematic)
{
extern cvar_t allow_download_refpackages;
func_t f;
@ -880,8 +893,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
BZ_Free(sv.csqcentversion);
// wipe the entire per-level structure
memset (&sv, 0, sizeof(sv));
sv.logindatabase = -1;
SV_WipeServerState();
SV_SetupNetworkBuffers(sv_bigcoords.ival);
@ -1119,7 +1131,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
if (svs.gametype == GT_Q1QVM)
{
int subs;
strcpy(sv.strings.sound_precache[0], "");
sv.strings.sound_precache[0] = "";
sv.strings.model_precache[0] = "";
subs = sv.world.worldmodel->numsubmodels;
@ -1152,9 +1164,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
)
{
int subs;
strcpy(sv.strings.sound_precache[0], "");
sv.strings.model_precache[0] = "";
sv.strings.model_precache[0] = PR_AddString(svprogfuncs, "", 0, false);
sv.strings.model_precache[1] = PR_AddString(svprogfuncs, sv.modelname, 0, false);
subs = sv.world.worldmodel->numsubmodels;
@ -1180,32 +1190,38 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
extern int map_checksum;
extern cvar_t sv_airaccelerate;
memset(sv.strings.configstring, 0, sizeof(sv.strings.configstring));
sv.stringsalloced = true;
memset(&sv.strings, 0, sizeof(sv.strings));
if (deathmatch.value)
sprintf(sv.strings.configstring[Q2CS_AIRACCEL], "%g", sv_airaccelerate.value);
sv.strings.configstring[Q2CS_AIRACCEL] = Z_StrDup(va("%g", sv_airaccelerate.value));
else
strcpy(sv.strings.configstring[Q2CS_AIRACCEL], "0");
sv.strings.configstring[Q2CS_AIRACCEL] = Z_StrDup("0");
// init map checksum config string but only for Q2/Q3 maps
if (sv.world.worldmodel->fromgame == fg_quake2 || sv.world.worldmodel->fromgame == fg_quake3)
sprintf(sv.strings.configstring[Q2CS_MAPCHECKSUM], "%i", map_checksum);
sv.strings.configstring[Q2CS_MAPCHECKSUM] = Z_StrDup(va("%i", map_checksum));
else
strcpy(sv.strings.configstring[Q2CS_MAPCHECKSUM], "0");
sv.strings.configstring[Q2CS_MAPCHECKSUM] = Z_StrDup("0");
subs = sv.world.worldmodel->numsubmodels;
if (subs > Q2MAX_MODELS-2)
if (subs > MAX_PRECACHE_MODELS-1)
{
Con_Printf("Warning: worldmodel has too many submodels\n");
subs = Q2MAX_MODELS-2;
subs = MAX_PRECACHE_MODELS-1;
}
strcpy(sv.strings.configstring[Q2CS_MODELS+1], sv.modelname);
for (i=1; i<sv.world.worldmodel->numsubmodels; i++)
sv.strings.configstring[Q2CS_MODELS+1] = Z_StrDup(sv.modelname);
for (i=1; i<subs && i < Q2MAX_MODELS-2; i++)
{
Q_snprintfz(sv.strings.configstring[Q2CS_MODELS+1+i], sizeof(sv.strings.configstring[Q2CS_MODELS+1+i]), "*%u", i);
sv.strings.configstring[Q2CS_MODELS+1+i] = Z_StrDup(va("*%u", i));
sv.models[i+1] = Mod_ForName (Mod_FixName(sv.strings.configstring[Q2CS_MODELS+1+i], sv.modelname), MLV_WARN);
}
for ( ; i<subs; i++)
{
sv.strings.q2_extramodels[1+i] = Z_StrDup(va("*%u", i));
sv.models[i+1] = Mod_ForName (Mod_FixName(sv.strings.q2_extramodels[1+i], sv.modelname), MLV_WARN);
}
}
#endif
@ -1727,6 +1743,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
SV_SetMoveVars();
sv.starttime = Sys_DoubleTime() - sv.time;
sv.autosave_time = sv.time + sv_autosave.value*60;
}
#endif

View file

@ -21,11 +21,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "netinc.h"
#include <sys/types.h>
#ifndef CLIENTONLY
#define Q2EDICT_NUM(i) (q2edict_t*)((char *)ge->edicts+i*ge->edict_size)
#define Q2EDICT_NUM(i) (q2edict_t*)((char *)ge->edicts+(i)*ge->edict_size)
void SV_LegacySavegame_f(void);
void SV_Savegame_f (void);
void SV_Loadgame_f (void);
#define INVIS_CHAR1 12
#define INVIS_CHAR2 (char)138
#define INVIS_CHAR3 (char)160
@ -1742,6 +1739,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client)
int i;
int maxpacketentities;
extern cvar_t pr_maxedicts;
client_t *seat;
client->fteprotocolextensions &= Net_PextMask(1, ISNQCLIENT(client));
client->fteprotocolextensions2 &= Net_PextMask(2, ISNQCLIENT(client));
@ -1878,6 +1876,14 @@ void SV_ClientProtocolExtensionsChanged(client_t *client)
}
break;
}
//make sure we have the right limits for splitscreen clients too (mostly for viewmodel safety checks)
for (seat = client->controlled; seat; seat = seat->controlled)
{
seat->max_net_clients = client->max_net_clients;
seat->max_net_ents = client->max_net_ents;
seat->maxmodels = client->maxmodels;
}
}
@ -1947,6 +1953,9 @@ client_t *SV_AddSplit(client_t *controller, char *info, int id)
cl->fteprotocolextensions2 = controller->fteprotocolextensions2;
cl->penalties = controller->penalties;
cl->protocol = controller->protocol;
cl->maxmodels = controller->maxmodels;
cl->max_net_clients = controller->max_net_clients;
cl->max_net_ents = controller->max_net_ents;
Q_strncatz(cl->guid, va("%s:%i", controller->guid, curclients), sizeof(cl->guid));
@ -1958,7 +1967,38 @@ client_t *SV_AddSplit(client_t *controller, char *info, int id)
cl->playerclass = 0;
cl->pendingentbits = NULL;
cl->edict = EDICT_NUM(svprogfuncs, i+1);
cl->edict = NULL;
#ifdef Q2SERVER
cl->q2edict = NULL;
#endif
switch(svs.gametype)
{
#ifdef Q2SERVER
case GT_QUAKE2:
cl->q2edict = Q2EDICT_NUM(i+1);
if (!ge->ClientConnect(cl->q2edict, cl->userinfo))
{
const char *reject = Info_ValueForKey(cl->userinfo, "rejmsg");
if (*reject)
SV_ClientPrintf(controller, PRINT_HIGH, "Splitscreen Refused: %s\n", reject);
else
SV_ClientPrintf(controller, PRINT_HIGH, "Splitscreen Refused\n");
Con_DPrintf ("Game rejected a connection.\n");
*cl->userinfo = 0;
cl->namebuf[0] = 0;
return NULL;
}
ge->ClientUserinfoChanged(cl->q2edict, cl->userinfo);
break;
#endif
default:
cl->edict = EDICT_NUM(svprogfuncs, i+1);
break;
}
prev->controlled = cl;
prev = cl;
@ -1985,7 +2025,8 @@ client_t *SV_AddSplit(client_t *controller, char *info, int id)
if (cl->state >= cs_connected)
{
cl->sendinfo = true;
SV_SetUpClientEdict(cl, cl->edict);
if (svprogfuncs)
SV_SetUpClientEdict(cl, cl->edict);
}
if (cl->state >= cs_spawned)
SV_Begin_Core(cl);
@ -2229,7 +2270,7 @@ client_t *SVC_DirectConnect(void)
switch(Q_atoi(Cmd_Argv(0)))
{
case PROTOCOL_VERSION_FTE:
if (protocol == SCP_QUAKEWORLD)
if (protocol == SCP_QUAKEWORLD || protocol == SCP_QUAKE2)
{
protextsupported = Q_atoi(Cmd_Argv(1));
Con_DPrintf("Client supports 0x%x fte extensions\n", protextsupported);
@ -4496,7 +4537,12 @@ void SV_MVDStream_Poll(void);
isidle = true;
#endif
if (SV_Physics ())
{
isidle = false;
if (sv.time > sv.autosave_time)
SV_AutoSave();
}
}
else
{
@ -4773,6 +4819,9 @@ void SV_InitLocal (void)
Cmd_AddCommand ("openroute", SV_OpenRoute_f);
#ifndef SERVERONLY
Cvar_Register(&sv_autosave, cvargroup_servercontrol);
#endif
Cmd_AddCommand ("savegame_legacy", SV_LegacySavegame_f);
Cmd_AddCommand ("savegame", SV_Savegame_f);
Cmd_AddCommand ("loadgame", SV_Loadgame_f);
@ -5142,6 +5191,17 @@ SV_Init
void SV_Demo_Init(void);
#endif
void SV_ArgumentOverrides(void)
{
int p;
// parse params for cvars
p = COM_CheckParm ("-svport");
if (!p)
p = COM_CheckParm ("-port");
if (p && p < com_argc)
Cvar_Set(Cvar_FindVar("sv_port"), com_argv[p+1]);
}
void SV_ExecInitialConfigs(char *defaultexec)
{
Cbuf_AddText("cvar_purgedefaults\n", RESTRICT_LOCAL); //reset cvar defaults to their engine-specified values. the tail end of 'exec default.cfg' will update non-cheat defaults to mod-specified values.
@ -5163,6 +5223,8 @@ void SV_ExecInitialConfigs(char *defaultexec)
// process command line arguments
Cbuf_Execute ();
SV_ArgumentOverrides();
}
void SV_Init (quakeparms_t *parms)

View file

@ -1990,14 +1990,14 @@ void SV_MVD_SendInitialGamestate(mvddest_t *dest)
if (!sv.strings.lightstyles[i])
continue;
#ifdef PEXT_LIGHTSTYLECOL
if ((demo.recorder.fteprotocolextensions & PEXT_LIGHTSTYLECOL) && (sv.strings.lightstylecolours[i][0]!=1||sv.strings.lightstylecolours[i][1]!=1||sv.strings.lightstylecolours[i][2]!=1) && sv.strings.lightstyles[i])
if ((demo.recorder.fteprotocolextensions & PEXT_LIGHTSTYLECOL) && (sv.lightstylecolours[i][0]!=1||sv.lightstylecolours[i][1]!=1||sv.lightstylecolours[i][2]!=1) && sv.strings.lightstyles[i])
{
MSG_WriteByte (&buf, svcfte_lightstylecol);
MSG_WriteByte (&buf, (unsigned char)i);
MSG_WriteByte (&buf, 0x87);
MSG_WriteShort(&buf, sv.strings.lightstylecolours[i][0]*1024);
MSG_WriteShort(&buf, sv.strings.lightstylecolours[i][1]*1024);
MSG_WriteShort(&buf, sv.strings.lightstylecolours[i][2]*1024);
MSG_WriteShort(&buf, sv.lightstylecolours[i][0]*1024);
MSG_WriteShort(&buf, sv.lightstylecolours[i][1]*1024);
MSG_WriteShort(&buf, sv.lightstylecolours[i][2]*1024);
MSG_WriteString (&buf, sv.strings.lightstyles[i]);
}
else

View file

@ -417,7 +417,7 @@ static int WPhys_FlyMove (world_t *w, wedict_t *ent, const vec3_t gravitydir, fl
impact = trace.ent;
}
if (trace.startsolid)
if (trace.allsolid)//should be (trace.startsolid), but that breaks compat. *sigh*
{ // entity is trapped in another solid
VectorClear (ent->v->velocity);
return 3;

View file

@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// sv_main.c -- server main program
#include "quakedef.h"
#include "pr_common.h"
#ifndef CLIENTONLY
@ -353,7 +354,7 @@ Sends text across to be displayed if the level passes
Is included in mvds.
=================
*/
void VARGS SV_ClientPrintf (client_t *cl, int level, char *fmt, ...)
void VARGS SV_ClientPrintf (client_t *cl, int level, const char *fmt, ...)
{
va_list argptr;
char string[1024];
@ -416,7 +417,7 @@ SV_BroadcastPrintf
Sends text to all active clients
=================
*/
void VARGS SV_BroadcastPrintf (int level, char *fmt, ...)
void VARGS SV_BroadcastPrintf (int level, const char *fmt, ...)
{
va_list argptr;
char string[1024];
@ -515,7 +516,7 @@ SV_BroadcastCommand
Sends text to all active clients
=================
*/
void VARGS SV_BroadcastCommand (char *fmt, ...)
void VARGS SV_BroadcastCommand (const char *fmt, ...)
{
va_list argptr;
char string[1024];
@ -571,7 +572,7 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
int cluster;
int j;
qboolean reliable;
int pnum = -1;
client_t *oneclient = NULL, *split;
if (to == MULTICAST_INIT)
{
@ -603,6 +604,7 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
}
#ifdef Q2BSPS
//in theory, this q2/q3 path is only still different thanks to areas, but it also supports q2 gamecode properly.
if (sv.world.worldmodel->fromgame == fg_quake2 || sv.world.worldmodel->fromgame == fg_quake3)
{
int area1, area2, leafnum;
@ -651,8 +653,10 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
if (svprogfuncs)
{
edict_t *ent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->msg_entity);
pnum = NUM_FOR_EDICT(svprogfuncs, ent) - 1;
oneclient = svs.clients + NUM_FOR_EDICT(svprogfuncs, ent) - 1;
}
else
oneclient = NULL; //unsupported in this game mode
mask = NULL;
break;
@ -662,54 +666,64 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
}
// send the data to all relevent clients
for (j = 0, client = svs.clients; j < svs.allocated_client_slots; j++, client++)
for (j = 0; j < svs.allocated_client_slots; j++)
{
client = &svs.clients[j];
if (client->state != cs_spawned)
continue;
if (client->protocol == SCP_QUAKEWORLD)
{
if (client->fteprotocolextensions & without)
{
// Con_Printf ("Version supressed multicast - without pext\n");
continue;
}
if (!(~client->fteprotocolextensions & ~with))
{
// Con_Printf ("Version supressed multicast - with pext\n");
continue;
}
}
if (client->controller)
continue; //FIXME: send if at least one of the players is near enough.
if (pnum >= 0)
for (split = client; split; split = split->controlled)
{
if (pnum != j)
continue;
}
else if (mask)
{
if (client->penalties & BAN_BLIND)
continue;
#ifdef Q2SERVER
if (ge)
leafnum = CM_PointLeafnum (sv.world.worldmodel, client->q2edict->s.origin);
else
#endif
if (client->protocol == SCP_QUAKEWORLD)
{
if (svprogfuncs)
if (client->fteprotocolextensions & without)
{
if (!((int)client->edict->xv->dimension_see & dimension_mask))
continue;
// Con_Printf ("Version supressed multicast - without pext\n");
continue;
}
if (!(~client->fteprotocolextensions & ~with))
{
// Con_Printf ("Version supressed multicast - with pext\n");
continue;
}
leafnum = CM_PointLeafnum (sv.world.worldmodel, client->edict->v->origin);
}
cluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
area2 = CM_LeafArea (sv.world.worldmodel, leafnum);
if (!CM_AreasConnected (sv.world.worldmodel, area1, area2))
continue;
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
continue;
if (oneclient)
{
if (oneclient != split)
continue;
}
else if (mask)
{
if (split->penalties & BAN_BLIND)
continue;
#ifdef Q2SERVER
if (ge)
leafnum = CM_PointLeafnum (sv.world.worldmodel, split->q2edict->s.origin);
else
#endif
{
if (svprogfuncs)
{
if (!((int)split->edict->xv->dimension_see & dimension_mask))
continue;
}
leafnum = CM_PointLeafnum (sv.world.worldmodel, split->edict->v->origin);
}
cluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
area2 = CM_LeafArea (sv.world.worldmodel, leafnum);
if (!CM_AreasConnected (sv.world.worldmodel, area1, area2))
continue;
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
continue;
}
break;
}
if (!split)
continue;
switch (client->protocol)
{
@ -804,8 +818,10 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
if (svprogfuncs)
{
edict_t *ent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->msg_entity);
pnum = NUM_FOR_EDICT(svprogfuncs, ent) - 1;
oneclient = svs.clients + NUM_FOR_EDICT(svprogfuncs, ent) - 1;
}
else
oneclient = NULL;
mask = NULL;
break;
@ -821,52 +837,51 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
continue;
if (client->controller)
continue; //FIXME: send if at least one of the players is near enough.
if (client->protocol == SCP_QUAKEWORLD)
{
if (client->fteprotocolextensions & without)
{
// Con_Printf ("Version supressed multicast - without pext\n");
continue;
}
if (!(client->fteprotocolextensions & with) && with)
{
// Con_Printf ("Version supressed multicast - with pext\n");
continue;
}
}
if (client->penalties & BAN_BLIND)
continue;
if (pnum >= 0)
for (split = client; split; split = split->controlled)
{
if (pnum != j)
continue;
}
else if (svprogfuncs)
{
client_t *seat = client;
for(seat = client; seat; seat = seat->controlled)
if (split->protocol == SCP_QUAKEWORLD)
{
if (!((int)seat->edict->xv->dimension_see & dimension_mask))
if (split->fteprotocolextensions & without)
{
// Con_Printf ("Version supressed multicast - without pext\n");
continue;
}
if (!(split->fteprotocolextensions & with) && with)
{
// Con_Printf ("Version supressed multicast - with pext\n");
continue;
}
}
if (split->penalties & BAN_BLIND)
continue;
if (oneclient)
{
if (oneclient != split)
continue;
}
else if (svprogfuncs)
{
if (!((int)split->edict->xv->dimension_see & dimension_mask))
continue;
if (!mask) //no pvs? broadcast.
goto inrange;
break;
if (to == MULTICAST_PHS_R || to == MULTICAST_PHS)
{
vec3_t delta;
VectorSubtract(origin, seat->edict->v->origin, delta);
VectorSubtract(origin, split->edict->v->origin, delta);
if (DotProduct(delta, delta) <= 1024*1024)
goto inrange;
break;
}
{
vec3_t pos;
VectorAdd(seat->edict->v->origin, seat->edict->v->view_ofs, pos);
VectorAdd(split->edict->v->origin, split->edict->v->view_ofs, pos);
cluster = sv.world.worldmodel->funcs.ClusterForPoint (sv.world.worldmodel, pos);
if (cluster>= 0 && !(mask[cluster>>3] & (1<<(cluster&7)) ) )
{
@ -875,9 +890,11 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
}
}
}
break;
}
if (!split)
continue;
inrange:
switch (client->protocol)
{
case SCP_BAD:
@ -953,17 +970,20 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int
//mvds are all reliables really.
case MULTICAST_ONE_R:
case MULTICAST_ONE:
if (svprogfuncs)
{
edict_t *ent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->msg_entity);
pnum = NUM_FOR_EDICT(svprogfuncs, ent) - 1;
int pnum;
if (svprogfuncs)
{
edict_t *ent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->msg_entity);
pnum = NUM_FOR_EDICT(svprogfuncs, ent) - 1;
}
else
{
pnum = 0; //FIXME
Con_Printf("SV_MulticastProtExt: unsupported unicast\n");
}
msg = MVDWrite_Begin(dem_single, pnum, sv.multicast.cursize);
}
else
{
pnum = 0; //FIXME
Con_Printf("SV_MulticastProtExt: unsupported unicast\n");
}
msg = MVDWrite_Begin(dem_single, pnum, sv.multicast.cursize);
break;
}
SZ_Write(msg, sv.multicast.data, sv.multicast.cursize);
@ -1035,17 +1055,17 @@ void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const cha
else
{
for (sound_num=1 ; sound_num<MAX_PRECACHE_SOUNDS
&& *sv.strings.sound_precache[sound_num] ; sound_num++)
&& sv.strings.sound_precache[sound_num] ; sound_num++)
if (!strcmp(sample, sv.strings.sound_precache[sound_num]))
break;
if ( sound_num == MAX_PRECACHE_SOUNDS || !*sv.strings.sound_precache[sound_num] )
if ( sound_num == MAX_PRECACHE_SOUNDS || !sv.strings.sound_precache[sound_num] )
{
if (sound_num < MAX_PRECACHE_SOUNDS)
{
Con_Printf("WARNING: SV_StartSound: sound %s not precached\n", sample);
//late precache it. use multicast to ensure that its sent NOW (and to all). normal reliables would mean it would arrive after the svc_sound
Q_strncpyz(sv.strings.sound_precache[sound_num], sample, sizeof(sv.strings.sound_precache[sound_num]));
sv.strings.sound_precache[sound_num] = PR_AddString(svprogfuncs, sample, 0, false);
Con_DPrintf("Delayed sound precache: %s\n", sample);
MSG_WriteByte(&sv.multicast, svcfte_precache);
MSG_WriteShort(&sv.multicast, sound_num+PC_SOUND);
@ -1076,6 +1096,7 @@ void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const cha
// reliable = true;
extfield_mask = 0;
extfield_mask |= (flags & CF_NOSPACIALISE) << 8;
if (volume != DEFAULT_SOUND_PACKET_VOLUME)
extfield_mask |= NQSND_VOLUME;
if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
@ -1088,13 +1109,18 @@ void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const cha
extfield_mask |= FTESND_PITCHADJ;
if (timeofs != 0)
extfield_mask |= FTESND_TIMEOFS;
if (extfield_mask > 0xff)
extfield_mask |= FTESND_MOREFLAGS;
#ifdef PEXT_SOUNDDBL
if (channel >= 8 || ent >= 2048 || sound_num > 0xff || (pitchadj && pitchadj != 100) || timeofs)
if (channel >= 8 || ent >= 2048 || (extfield_mask & ~(NQSND_VOLUME|NQSND_ATTENUATION)))
{
//if any of the above conditions evaluates to true, then we can't use standard qw protocols
MSG_WriteByte (&sv.multicast, svcfte_soundextended);
MSG_WriteByte (&sv.multicast, extfield_mask);
MSG_WriteByte (&sv.multicast, extfield_mask&0xff);
if (extfield_mask & FTESND_MOREFLAGS)
MSG_WriteByte (&sv.multicast, extfield_mask>>8);
if (extfield_mask & NQSND_VOLUME)
MSG_WriteByte (&sv.multicast, bound(0, volume, 255));
if (extfield_mask & NQSND_ATTENUATION)
@ -1174,10 +1200,18 @@ void SV_StartSound (int ent, vec3_t origin, int seenmask, int channel, const cha
for (i=0 ; i<3 ; i++)
MSG_WriteCoord (&sv.nqmulticast, origin[i]);
#endif
if (use_phs)
SV_MulticastProtExt(origin, reliable ? MULTICAST_PHS_R : MULTICAST_PHS, seenmask, requiredextensions, 0);
if (flags & CF_UNICAST)
{
SV_MulticastProtExt(origin, reliable ? MULTICAST_ONE_R : MULTICAST_ONE, seenmask, requiredextensions, 0);
}
else
SV_MulticastProtExt(origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL, seenmask, requiredextensions, 0);
{
if (use_phs)
SV_MulticastProtExt(origin, reliable ? MULTICAST_PHS_R : MULTICAST_PHS, seenmask, requiredextensions, 0);
else
SV_MulticastProtExt(origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL, seenmask, requiredextensions, 0);
}
}
void SVQ1_StartSound (float *origin, wedict_t *wentity, int channel, const char *sample, int volume, float attenuation, int pitchadj, float timeofs, unsigned int flags)
@ -1253,6 +1287,8 @@ void SV_WriteEntityDataToMessage (client_t *client, sizebuf_t *msg, int pnum)
float newa;
ent = client->edict;
if (client->controller)
client = client->controller;
if (!ent)
return;
@ -1281,6 +1317,13 @@ void SV_WriteEntityDataToMessage (client_t *client, sizebuf_t *msg, int pnum)
// a fixangle might get lost in a dropped packet. Oh well.
if (ent->v->fixangle)
{
if (!client->lockangles)
{
//try to keep them vaugely reliable.
if (client->netchan.message.cursize < client->netchan.message.maxsize/2)
msg = &client->netchan.message;
}
if (pnum)
{
MSG_WriteByte(msg, svcfte_choosesplitclient);
@ -1502,7 +1545,7 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
if (ent->v->armorvalue)
bits |= SU_ARMOR;
weaponmodelindex = SV_ModelIndex(ent->v->weaponmodel + svprogfuncs->stringtable);
weaponmodelindex = SV_ModelIndex(PR_GetString(svprogfuncs, ent->v->weaponmodel));
if (weaponmodelindex)
bits |= SU_WEAPONMODEL;

View file

@ -375,21 +375,27 @@ void SV_New_f (void)
if (sv.state == ss_cinematic)
playernum = -1;
if (ISQ2CLIENT(host_client))
ClientReliableWrite_Short (host_client, playernum);
else
ClientReliableWrite_Byte (host_client, playernum);
split->state = cs_connected;
split->connection_started = realtime;
#ifdef SVRANKING
split->stats_started = realtime;
#endif
splitnum++;
if (ISQ2CLIENT(host_client))
{
ClientReliableWrite_Short (host_client, playernum);
break;
}
else
ClientReliableWrite_Byte (host_client, playernum);
}
if (host_client->fteprotocolextensions & PEXT_SPLITSCREEN)
ClientReliableWrite_Byte (host_client, 128);
if (!ISQ2CLIENT(host_client))
{
if (host_client->fteprotocolextensions & PEXT_SPLITSCREEN)
ClientReliableWrite_Byte (host_client, 128);
}
}
// send full levelname
@ -640,7 +646,7 @@ void SVNQ_New_f (void)
MSG_WriteByte (&host_client->netchan.message, 0);
//fixme: don't send too many sounds.
for (i = 1; *sv.strings.sound_precache[i] ; i++)
for (i = 1; sv.strings.sound_precache[i] ; i++)
MSG_WriteString (&host_client->netchan.message, sv.strings.sound_precache[i]);
MSG_WriteByte (&host_client->netchan.message, 0);
@ -667,8 +673,8 @@ void SVNQ_New_f (void)
#ifdef Q2SERVER
void SVQ2_ConfigStrings_f (void)
{
int start;
char *str;
unsigned int start;
const char *str;
Con_DPrintf ("Configstrings() from %s\n", host_client->name);
@ -701,136 +707,50 @@ void SVQ2_ConfigStrings_f (void)
&& start < Q2MAX_CONFIGSTRINGS)
{
str = sv.strings.configstring[start];
if (*str)
if (str && *str)
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, str);
}
/*
//choose range to grab from.
if (start < Q2CS_CDTRACK)
start++;
}
//model overflows
if (start == Q2MAX_CONFIGSTRINGS)
start = 0x8000;
while ( host_client->netchan.message.cursize < host_client->netchan.message.maxsize/2
&& start < 0x8000+MAX_PRECACHE_MODELS)
{
str = sv.strings.q2_extramodels[start-0x8000];
if (str && *str)
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, sv.name);
MSG_WriteString (&host_client->netchan.message, str);
}
else if (start < Q2CS_SKY)
start++;
}
//sound overflows
if (start == 0x8000+MAX_PRECACHE_MODELS)
start = 0xc000;
while ( host_client->netchan.message.cursize < host_client->netchan.message.maxsize/2
&& start < 0xc000+MAX_PRECACHE_SOUNDS)
{
str = sv.strings.q2_extrasounds[start-0xc000];
if (str && *str)
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
if (svprogfuncs)
MSG_WriteString (&host_client->netchan.message, va("%i", sv.edicts->v->sounds));
else
MSG_WriteString (&host_client->netchan.message, "0");
MSG_WriteString (&host_client->netchan.message, str);
}
else if (start < Q2CS_SKYAXIS)
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, "unit1_");
}
else if (start < Q2CS_SKYROTATE)
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, "0");
}
else if (start < Q2CS_STATUSBAR)
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, "0");
}
else if (start < Q2CS_AIRACCEL)
{//show status bar
if (start == Q2CS_STATUSBAR)
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, sv.statusbar);
}
}
else if (start < Q2CS_MAXCLIENTS)
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, va("%i", (int)1));
}
else if (start < Q2CS_MAPCHECKSUM)
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, va("%i", (int)32));
}
else if (start < Q2CS_MODELS)
{
extern int map_checksum;
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, va("%i", map_checksum));
}
else if (start < Q2CS_SOUNDS)
{
if (*sv.model_precache[start-Q2CS_MODELS])
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, sv.model_precache[start-Q2CS_MODELS]);
}
}
else if (start < Q2CS_IMAGES)
{
if (*sv.sound_precache[start-Q2CS_SOUNDS])
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, sv.sound_precache[start-Q2CS_SOUNDS]);
}
}
else if (start < Q2CS_LIGHTS)
{
if (*sv.image_precache[start-Q2CS_IMAGES])
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, sv.image_precache[start-Q2CS_IMAGES]);
}
}
else if (start < Q2CS_ITEMS)
{
if (start-Q2CS_LIGHTS < MAX_LIGHTSTYLES && sv.lightstyles[start-Q2CS_LIGHTS])
{
MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
MSG_WriteShort (&host_client->netchan.message, start);
MSG_WriteString (&host_client->netchan.message, sv.lightstyles[start-Q2CS_LIGHTS]);
}
}
else if (start < Q2CS_PLAYERSKINS)
{
// MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
// MSG_WriteShort (&host_client->netchan.message, start);
// MSG_WriteString (&host_client->netchan.message, sv.configstrings[start-Q2CS_ITEMS]);
}
else if (start < Q2CS_GENERAL)
{
// MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
// MSG_WriteShort (&host_client->netchan.message, start);
// MSG_WriteString (&host_client->netchan.message, sv.configstrings[start]);
}
else
{
// MSG_WriteByte (&host_client->netchan.message, svcq2_configstring);
// MSG_WriteShort (&host_client->netchan.message, start);
// MSG_WriteString (&host_client->netchan.message, sv.configstrings[start]);
}
*/
start++;
}
// send next command
if (start == Q2MAX_CONFIGSTRINGS)
if (start == 0xc000+MAX_PRECACHE_SOUNDS)
{
MSG_WriteByte (&host_client->netchan.message, svcq2_stufftext);
MSG_WriteString (&host_client->netchan.message, va("cmd baselines %i 0\n",svs.spawncount) );
@ -1055,9 +975,9 @@ void SV_SendClientPrespawnInfo(client_t *client)
}
client->prespawn_idx++;
if (client->prespawn_idx >= maxclientsupportedsounds || !*sv.strings.sound_precache[client->prespawn_idx])
if (client->prespawn_idx >= maxclientsupportedsounds || !sv.strings.sound_precache[client->prespawn_idx])
{
if (*sv.strings.sound_precache[client->prespawn_idx] && !(client->plimitwarned & PLIMIT_SOUNDS))
if (sv.strings.sound_precache[client->prespawn_idx] && !(client->plimitwarned & PLIMIT_SOUNDS))
{
client->plimitwarned |= PLIMIT_SOUNDS;
SV_ClientPrintf(client, PRINT_HIGH, "WARNING: Your client's network protocol only supports %i sounds. Please upgrade or enable extensions.\n", client->prespawn_idx);
@ -1205,7 +1125,7 @@ void SV_SendClientPrespawnInfo(client_t *client)
break;
}
if (*sv.strings.particle_precache[client->prespawn_idx])
if (sv.strings.particle_precache[client->prespawn_idx])
{
ClientReliableWrite_Begin (client, ISNQCLIENT(client)?svcdp_precache:svcfte_precache, 4 + strlen(sv.strings.particle_precache[client->prespawn_idx]));
ClientReliableWrite_Short (client, client->prespawn_idx | PC_PARTICLE);
@ -1298,7 +1218,7 @@ void SV_SendClientPrespawnInfo(client_t *client)
if (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
{
MSG_WriteByte(&client->netchan.message, svcfte_spawnstatic2);
SVFTE_EmitBaseline(state, false, &client->netchan.message, client);
SVFTE_EmitBaseline(state, false, &client->netchan.message, client->fteprotocolextensions2);
continue;
}
if (client->fteprotocolextensions & PEXT_SPAWNSTATIC2)
@ -1402,7 +1322,7 @@ void SV_SendClientPrespawnInfo(client_t *client)
else if (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
{
MSG_WriteByte(&client->netchan.message, svcfte_spawnbaseline2);
SVFTE_EmitBaseline(state, true, &client->netchan.message, client);
SVFTE_EmitBaseline(state, true, &client->netchan.message, client->fteprotocolextensions2);
}
else if (client->fteprotocolextensions & PEXT_SPAWNSTATIC2)
{
@ -1625,15 +1545,15 @@ void SVQW_Spawn_f (void)
if (!sv.strings.lightstyles[i])
continue;
#ifdef PEXT_LIGHTSTYLECOL
if ((host_client->fteprotocolextensions & PEXT_LIGHTSTYLECOL) && (sv.strings.lightstylecolours[i][0]!=1||sv.strings.lightstylecolours[i][1]!=1||sv.strings.lightstylecolours[i][2]!=1) && sv.strings.lightstyles[i])
if ((host_client->fteprotocolextensions & PEXT_LIGHTSTYLECOL) && (sv.lightstylecolours[i][0]!=1||sv.lightstylecolours[i][1]!=1||sv.lightstylecolours[i][2]!=1) && sv.strings.lightstyles[i])
{
ClientReliableWrite_Begin (host_client, svcfte_lightstylecol,
10 + (sv.strings.lightstyles[i] ? strlen(sv.strings.lightstyles[i]) : 1));
ClientReliableWrite_Byte (host_client, (char)i);
ClientReliableWrite_Char (host_client, 0x87);
ClientReliableWrite_Short (host_client, sv.strings.lightstylecolours[i][0]*1024);
ClientReliableWrite_Short (host_client, sv.strings.lightstylecolours[i][1]*1024);
ClientReliableWrite_Short (host_client, sv.strings.lightstylecolours[i][2]*1024);
ClientReliableWrite_Short (host_client, sv.lightstylecolours[i][0]*1024);
ClientReliableWrite_Short (host_client, sv.lightstylecolours[i][1]*1024);
ClientReliableWrite_Short (host_client, sv.lightstylecolours[i][2]*1024);
ClientReliableWrite_String (host_client, sv.strings.lightstyles[i]);
}
else
@ -1889,6 +1809,7 @@ void SV_Begin_Core(client_t *split)
split->edict->v->maxs[2] = 32;
split->edict->v->movetype = MOVETYPE_NOCLIP;
}
VectorCopy(split->edict->v->origin, split->edict->v->oldorigin); //make sure oldorigin isn't 0 0 0 or anything too clumsy like that. stuck somewhere killable is better than stuck outside the map.
}
oh = host_client;
@ -2830,14 +2751,6 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam
*p = (char)tolower((unsigned char)*p);
}
if (!SV_AllowDownload(name))
{
Sys_Printf ("%s denied download of %s due to path/name rules\n", host_client->name, name);
return -2; //not permitted (even if it exists).
}
//mvdsv demo downloading support demonum/ -> demos/XXXX (sets up the client paths)
if (!strncmp(name, "demonum/", 8))
{
@ -2851,11 +2764,17 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam
Sys_Printf ("%s requested invalid demonum %s\n", host_client->name, name+8);
return -1; //not found
}
*replacementname = va("demos/%s\n", mvdname);
return -4; //redirect
name = *replacementname = va("demos/%s", mvdname);
return -4;
}
}
if (!SV_AllowDownload(name))
{
Sys_Printf ("%s denied download of %s due to path/name rules\n", host_client->name, name);
return -2; //not permitted (even if it exists).
}
//mvdsv demo downloading support. demos/ -> demodir (sets up the server paths)
if (!strncmp(name, "demos/", 6))
name = va("%s/%s", sv_demoDir.string, name+6);
@ -3073,12 +2992,27 @@ void SV_BeginDownload_f(void)
if (result == -4)
{
#ifdef PEXT_CHUNKEDDOWNLOADS
if (host_client->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS)
qboolean isezquake = !strncmp(Info_ValueForKey(host_client->userinfo, "*client"), "ezQuake", 7);
if ((host_client->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS) && !isezquake)
{
//redirect the client (before the message saying download failed)
char *s = va("dlsize \"%s\" r \"%s\"\n", name, redirection);
// char *s = va("dlsize \"%s\" r \"%s\"\n", name, redirection);
// ClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(s));
// ClientReliableWrite_String (host_client, s);
ClientReliableWrite_Begin (host_client, svc_download, 10+strlen(name));
ClientReliableWrite_Long (host_client, -1);
ClientReliableWrite_Long (host_client, result);
ClientReliableWrite_String (host_client, redirection);
return;
}
else
{ //crappy hack for crappy clients. tell them to download the new file instead without telling them about any failure.
//this will seriously mess with any download queues or anything like that
char *s = va("download \"%s\"\n", redirection);
ClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(s));
ClientReliableWrite_String (host_client, s);
return;
}
#endif
}
@ -3719,6 +3653,9 @@ static void SV_UpdateSeats(client_t *controller)
curclients++;
}
if (controller->protocol == SCP_QUAKE2)
return; //wait for the clientinfo stuff instead.
ClientReliableWrite_Begin(controller, svc_signonnum, 2+curclients);
ClientReliableWrite_Byte(controller, curclients);
for (curclients = 0, cl = controller; cl; cl = cl->controlled, curclients++)
@ -3977,6 +3914,13 @@ void SV_SetInfo_f (void)
if (!strcmp(Info_ValueForKey(host_client->userinfo, key), oldval))
return; // key hasn't changed
if (svs.gametype == GT_QUAKE2)
{
ge->ClientUserinfoChanged (host_client->q2edict, host_client->userinfo); //tell the gamecode
SV_ExtractFromUserinfo(host_client, true); //let the server routines know
return;
}
// process any changed values
SV_ExtractFromUserinfo (host_client, true);
@ -5614,8 +5558,6 @@ ucmd_t ucmdsq2[] = {
{"baselines", SVQ2_BaseLines_f, true},
{"begin", SV_Begin_f, true},
// {"setinfo", SV_SetInfo_f, true},
{"serverinfo", SV_ShowServerinfo_f, true},
{"info", SV_ShowServerinfo_f, true},
@ -5624,7 +5566,10 @@ ucmd_t ucmdsq2[] = {
{"nextserver", SVQ2_NextServer_f, true},
{"ftevote", SV_Vote_f, true},
//fte stuff
{"setinfo", SV_SetInfo_f, true},
{"ftevote", SV_Vote_f, true}, //voting... kinda messed up by 'vote' being common in mods
{"addseat", Cmd_AddSeat_f, true}, //for splitscreen
//#ifdef SVRANKING
// {"topten", Rank_ListTop10_f, true},
@ -6647,8 +6592,9 @@ if (sv_player->v->health > 0 && before && !after )
{
vec3_t delta;
VectorSubtract (pmove.angles, sv_player->v->v_angle, delta);
delta[0] = pmove.angles[0] - SHORT2ANGLE(pmove.cmd.angles[0]);
delta[1] = pmove.angles[1] - SHORT2ANGLE(pmove.cmd.angles[1]);
delta[2] = pmove.angles[2] - SHORT2ANGLE(pmove.cmd.angles[2]);
if (delta[0] || delta[1] || delta[2])
{
if (host_client->fteprotocolextensions2 & PEXT2_SETANGLEDELTA)
@ -6700,7 +6646,22 @@ if (sv_player->v->health > 0 && before && !after )
VectorCopy (pmove.safeorigin, sv_player->v->oldorigin);
VectorCopy (pmove.origin, sv_player->v->origin);
VectorCopy (pmove.angles, sv_player->v->v_angle);
if (!sv_player->v->fixangle)
{
VectorCopy (pmove.angles, sv_player->v->v_angle);
//some clients (especially NQ ones) attempt to cheat. don't let it benefit them.
//some things would break though.
if ((!sv_player->xv->gravitydir[0] && !sv_player->xv->gravitydir[1] && !sv_player->xv->gravitydir[2]) && sv_player->v->movetype == MOVETYPE_WALK)
{
float minpitch = *sv_minpitch.string?sv_minpitch.value:-70;
float maxpitch = *sv_maxpitch.string?sv_maxpitch.value:80;
if (sv_player->v->v_angle[0] < minpitch)
sv_player->v->v_angle[0] = minpitch;
if (sv_player->v->v_angle[0] > maxpitch)
sv_player->v->v_angle[0] = maxpitch;
}
}
// VectorCopy (pmove.gravitydir, sv_player->xv->gravitydir);
if (pmove.gravitydir[0] || pmove.gravitydir[1] || (pmove.gravitydir[2] && pmove.gravitydir[2] != -1))
@ -7346,11 +7307,12 @@ void SVQ2_ExecuteClientMessage (client_t *cl)
char *s;
usercmd_t oldest, oldcmd, newcmd;
q2client_frame_t *frame;
qboolean move_issued = false; //only allow one move command
int move_issued = 0; //only allow one move command
int checksumIndex;
qbyte checksum, calculatedChecksum;
int seq_hash;
int lastframe;
client_t *split;
if (!ge)
{
@ -7432,75 +7394,88 @@ void SVQ2_ExecuteClientMessage (client_t *cl)
break;
case clcq2_move:
if (move_issued)
if (move_issued >= MAX_SPLITS)
return; // someone is trying to cheat...
move_issued = true;
for (checksumIndex = 0, split = cl; split && checksumIndex < move_issued; checksumIndex++)
split = split->controlled;
checksumIndex = MSG_GetReadCount();
checksum = (qbyte)MSG_ReadByte ();
lastframe = MSG_ReadLong();
if (lastframe != host_client->delta_sequence)
if (move_issued)
{
cl->delta_sequence = lastframe;
checksumIndex = -1;
checksum = 0;
}
else
{
checksumIndex = MSG_GetReadCount();
checksum = (qbyte)MSG_ReadByte ();
lastframe = MSG_ReadLong();
if (lastframe != split->delta_sequence)
{
split->delta_sequence = lastframe;
}
}
MSGQ2_ReadDeltaUsercmd (&nullcmd, &oldest);
MSGQ2_ReadDeltaUsercmd (&oldest, &oldcmd);
MSGQ2_ReadDeltaUsercmd (&oldcmd, &newcmd);
if ( cl->state != cs_spawned )
break;
// if the checksum fails, ignore the rest of the packet
calculatedChecksum = Q2COM_BlockSequenceCRCByte(
net_message.data + checksumIndex + 1,
MSG_GetReadCount() - checksumIndex - 1,
seq_hash);
if (calculatedChecksum != checksum)
if ( split && split->state == cs_spawned )
{
Con_DPrintf ("Failed command checksum for %s(%d) (%d != %d)\n",
cl->name, cl->netchan.incoming_sequence, checksum, calculatedChecksum);
return;
}
if (cl->penalties & BAN_CRIPPLED)
{
cl->lastcmd.forwardmove = 0; //hmmm.... does this work well enough?
oldest.forwardmove = 0;
newcmd.forwardmove = 0;
cl->lastcmd.sidemove = 0;
oldest.sidemove = 0;
newcmd.sidemove = 0;
cl->lastcmd.upmove = 0;
oldest.upmove = 0;
newcmd.upmove = 0;
}
host_client->q2edict->client->ping = SV_CalcPing (host_client, false);
if (!sv.paused)
{
if (net_drop < 20)
if (checksumIndex != -1)
{
while (net_drop > 2)
{
ge->ClientThink (host_client->q2edict, (q2usercmd_t*)&cl->lastcmd);
net_drop--;
}
if (net_drop > 1)
ge->ClientThink (host_client->q2edict, (q2usercmd_t*)&oldest);
if (net_drop > 0)
ge->ClientThink (host_client->q2edict, (q2usercmd_t*)&oldcmd);
}
ge->ClientThink (host_client->q2edict, (q2usercmd_t*)&newcmd);
}
// if the checksum fails, ignore the rest of the packet
calculatedChecksum = Q2COM_BlockSequenceCRCByte(
net_message.data + checksumIndex + 1,
MSG_GetReadCount() - checksumIndex - 1,
seq_hash);
cl->lastcmd = newcmd;
if (calculatedChecksum != checksum)
{
Con_DPrintf ("Failed command checksum for %s(%d) (%d != %d)\n",
cl->name, cl->netchan.incoming_sequence, checksum, calculatedChecksum);
return;
}
}
if (split->penalties & BAN_CRIPPLED)
{
split->lastcmd.forwardmove = 0; //hmmm.... does this work well enough?
oldest.forwardmove = 0;
newcmd.forwardmove = 0;
split->lastcmd.sidemove = 0;
oldest.sidemove = 0;
newcmd.sidemove = 0;
split->lastcmd.upmove = 0;
oldest.upmove = 0;
newcmd.upmove = 0;
}
split->q2edict->client->ping = SV_CalcPing (split, false);
if (!sv.paused)
{
if (net_drop < 20)
{
while (net_drop > 2)
{
ge->ClientThink (split->q2edict, (q2usercmd_t*)&split->lastcmd);
net_drop--;
}
if (net_drop > 1)
ge->ClientThink (split->q2edict, (q2usercmd_t*)&oldest);
if (net_drop > 0)
ge->ClientThink (split->q2edict, (q2usercmd_t*)&oldcmd);
}
ge->ClientThink (split->q2edict, (q2usercmd_t*)&newcmd);
}
split->lastcmd = newcmd;
}
move_issued++;
break;
case clcq2_userinfo:

View file

@ -110,16 +110,36 @@ void MSGQ2_WriteDeltaEntity (q2entity_state_t *from, q2entity_state_t *to, sizeb
bits |= Q2U_EVENT;
if ( to->modelindex != from->modelindex )
{
bits |= Q2U_MODEL;
if (to->modelindex > 0xff)
bits |= Q2UX_INDEX16;
}
if ( to->modelindex2 != from->modelindex2 )
{
bits |= Q2U_MODEL2;
if (to->modelindex2 > 0xff)
bits |= Q2UX_INDEX16;
}
if ( to->modelindex3 != from->modelindex3 )
{
bits |= Q2U_MODEL3;
if (to->modelindex3 > 0xff)
bits |= Q2UX_INDEX16;
}
if ( to->modelindex4 != from->modelindex4 )
{
bits |= Q2U_MODEL4;
if (to->modelindex4 > 0xff)
bits |= Q2UX_INDEX16;
}
if ( to->sound != from->sound )
{
bits |= Q2U_SOUND;
if (to->sound > 0xff)
bits |= Q2UX_INDEX16;
}
if (newentity || (to->renderfx & Q2RF_BEAM))
bits |= Q2U_OLDORIGIN;
@ -165,13 +185,33 @@ void MSGQ2_WriteDeltaEntity (q2entity_state_t *from, q2entity_state_t *to, sizeb
MSG_WriteByte (msg, to->number);
if (bits & Q2U_MODEL)
MSG_WriteByte (msg, to->modelindex);
{
if (bits & Q2UX_INDEX16)
MSG_WriteShort (msg, to->modelindex);
else
MSG_WriteByte (msg, to->modelindex);
}
if (bits & Q2U_MODEL2)
MSG_WriteByte (msg, to->modelindex2);
{
if (bits & Q2UX_INDEX16)
MSG_WriteShort (msg, to->modelindex2);
else
MSG_WriteByte (msg, to->modelindex2);
}
if (bits & Q2U_MODEL3)
MSG_WriteByte (msg, to->modelindex3);
{
if (bits & Q2UX_INDEX16)
MSG_WriteShort (msg, to->modelindex3);
else
MSG_WriteByte (msg, to->modelindex3);
}
if (bits & Q2U_MODEL4)
MSG_WriteByte (msg, to->modelindex4);
{
if (bits & Q2UX_INDEX16)
MSG_WriteShort (msg, to->modelindex4);
else
MSG_WriteByte (msg, to->modelindex4);
}
if (bits & Q2U_FRAME8)
MSG_WriteByte (msg, to->frame);
@ -222,11 +262,22 @@ void MSGQ2_WriteDeltaEntity (q2entity_state_t *from, q2entity_state_t *to, sizeb
}
if (bits & Q2U_SOUND)
MSG_WriteByte (msg, to->sound);
{
if (bits & Q2UX_INDEX16)
MSG_WriteShort (msg, to->sound);
else
MSG_WriteByte (msg, to->sound);
}
if (bits & Q2U_EVENT)
MSG_WriteByte (msg, to->event);
if (bits & Q2U_SOLID)
MSG_WriteShort (msg, to->solid);
{
if (net_message.prim.q2flags & NPQ2_SOLID32)
MSG_WriteLong (msg, to->solid);
else
MSG_WriteShort (msg, to->solid);
}
}
@ -346,7 +397,7 @@ SV_WritePlayerstateToClient
=============
*/
void SVQ2_WritePlayerstateToClient (q2client_frame_t *from, q2client_frame_t *to, sizebuf_t *msg)
void SVQ2_WritePlayerstateToClient (unsigned int pext, int seat, int extflags, q2client_frame_t *from, q2client_frame_t *to, sizebuf_t *msg)
{
int i;
int pflags;
@ -354,20 +405,24 @@ void SVQ2_WritePlayerstateToClient (q2client_frame_t *from, q2client_frame_t *to
q2player_state_t dummy;
int statbits;
ps = &to->ps;
ps = &to->ps[seat];
if (!from)
{
memset (&dummy, 0, sizeof(dummy));
ops = &dummy;
}
else
ops = &from->ps;
ops = &from->ps[seat];
//
// determine what needs to be sent
//
pflags = 0;
if (pext & PEXT_SPLITSCREEN)
if (!from || from->clientnum[seat] != to->clientnum[seat])
pflags |= Q2PS_CLIENTNUM;
if (ps->pmove.pm_type != ops->pmove.pm_type)
pflags |= Q2PS_M_TYPE;
@ -426,13 +481,27 @@ void SVQ2_WritePlayerstateToClient (q2client_frame_t *from, q2client_frame_t *to
if (ps->gunframe != ops->gunframe)
pflags |= Q2PS_WEAPONFRAME;
pflags |= Q2PS_WEAPONINDEX;
if (ps->gunindex != ops->gunindex)
pflags |= Q2PS_WEAPONINDEX;
if (pext & PEXT_MODELDBL)
{
if ((pflags & Q2PS_WEAPONINDEX) && ps->gunindex > 0xff)
pflags |= Q2PS_INDEX16;
if ((pflags & Q2PS_WEAPONFRAME) && ps->gunframe > 0xff)
pflags |= Q2PS_INDEX16;
}
if (pflags > 0xffff)
pflags |= Q2PS_EXTRABITS;
//
// write it
//
MSG_WriteByte (msg, svcq2_playerinfo);
MSG_WriteShort (msg, pflags);
MSG_WriteShort (msg, pflags&0xffff);
if (pflags & Q2PS_EXTRABITS)
MSG_WriteByte (msg, pflags>>16);
//
// write the pmove_state_t
@ -497,12 +566,18 @@ void SVQ2_WritePlayerstateToClient (q2client_frame_t *from, q2client_frame_t *to
if (pflags & Q2PS_WEAPONINDEX)
{
MSG_WriteByte (msg, ps->gunindex);
if (pflags & Q2PS_INDEX16)
MSG_WriteShort(msg, ps->gunindex);
else
MSG_WriteByte (msg, ps->gunindex);
}
if (pflags & Q2PS_WEAPONFRAME)
{
MSG_WriteByte (msg, ps->gunframe);
if (pflags & Q2PS_INDEX16)
MSG_WriteShort (msg, ps->gunframe);
else
MSG_WriteByte (msg, ps->gunframe);
MSG_WriteChar (msg, ps->gunoffset[0]*4);
MSG_WriteChar (msg, ps->gunoffset[1]*4);
MSG_WriteChar (msg, ps->gunoffset[2]*4);
@ -533,6 +608,9 @@ void SVQ2_WritePlayerstateToClient (q2client_frame_t *from, q2client_frame_t *to
for (i=0 ; i<Q2MAX_STATS ; i++)
if (statbits & (1<<i) )
MSG_WriteShort (msg, ps->stats[i]);
if ((extflags & Q2PSX_CLIENTNUM) || (pflags & Q2PS_CLIENTNUM))
MSG_WriteByte(msg, to->clientnum[seat]);
}
@ -545,6 +623,9 @@ void SVQ2_WriteFrameToClient (client_t *client, sizebuf_t *msg)
{
q2client_frame_t *frame, *oldframe;
int lastframe;
client_t *split;
int seat;
int extflags = 0;
//Com_Printf ("%i -> %i\n", client->lastframe, sv.framenum);
// this is the frame we are creating
@ -571,6 +652,7 @@ void SVQ2_WriteFrameToClient (client_t *client, sizebuf_t *msg)
MSG_WriteLong (msg, sv.framenum);
MSG_WriteLong (msg, lastframe); // what we are delta'ing from
MSG_WriteByte (msg, client->chokecount&0xff); // rate dropped packets
extflags |= Q2PSX_OLD;
client->chokecount = 0;
// send over the areabits
@ -578,7 +660,8 @@ void SVQ2_WriteFrameToClient (client_t *client, sizebuf_t *msg)
SZ_Write (msg, frame->areabits, frame->areabytes);
// delta encode the playerstate
SVQ2_WritePlayerstateToClient (oldframe, frame, msg);
for (split = client, seat = 0; split; split = split->controlled, seat++)
SVQ2_WritePlayerstateToClient (client->fteprotocolextensions, seat, extflags, oldframe, frame, msg);
// delta encode the entities
SVQ2_EmitPacketEntities (oldframe, frame, msg);
@ -605,28 +688,25 @@ void SVQ2_Ents_Init(void);
void SVQ2_BuildClientFrame (client_t *client)
{
int e, i;
vec3_t org;
vec3_t org[MAX_SPLITS];
int clientarea[MAX_SPLITS];
q2edict_t *clent[MAX_SPLITS];
client_t *split;
q2edict_t *ent;
q2edict_t *clent;
q2client_frame_t *frame;
q2entity_state_t *state;
int l;
int clientarea, clientcluster;
int leafnum;
int seat;
int c_fullsend;
qbyte clientpvs[(MAX_MAP_LEAFS+7)>>3];
qbyte *clientphs;
qbyte *clientphs = NULL;
int seats;
if (client->state < cs_spawned)
return;
SVQ2_Ents_Init();
clent = client->q2edict;
if (!clent->client)
return;
#if 0
numprojs = 0; // no projectiles yet
#endif
@ -639,26 +719,48 @@ void SVQ2_BuildClientFrame (client_t *client)
// this is the frame we are creating
frame = &client->frameunion.q2frames[sv.framenum & Q2UPDATE_MASK];
// find the client's PVS
for (i=0 ; i<3 ; i++)
org[i] = clent->client->ps.pmove.origin[i]*0.125 + clent->client->ps.viewoffset[i];
leafnum = CM_PointLeafnum (sv.world.worldmodel, org);
clientarea = CM_LeafArea (sv.world.worldmodel, leafnum);
clientcluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
// calculate the visible areas
frame->areabytes = CM_WriteAreaBits (sv.world.worldmodel, frame->areabits, clientarea, false);
// grab the current player_state_t
frame->ps = clent->client->ps;
for (seat = 0, split = client; split; split = split->controlled, seat++)
{
int clientcluster;
int leafnum;
if (sv.paused)
frame->ps.pmove.pm_type = Q2PM_FREEZE;
clent[seat] = split->q2edict;
frame->clientnum[seat] = split - svs.clients;
if (!split->q2edict->client)
{ //shouldn't happen
VectorClear(org[seat]);
clientarea[seat] = 0;
memset(&frame->ps[seat], 0, sizeof(frame->ps[seat]));
frame->ps[seat].pmove.pm_type = Q2PM_FREEZE;
continue;
}
sv.world.worldmodel->funcs.FatPVS(sv.world.worldmodel, org, clientpvs, sizeof(clientpvs), false);
clientphs = CM_ClusterPHS (sv.world.worldmodel, clientcluster);
// find the client's PVS
for (i=0 ; i<3 ; i++)
org[seat][i] = clent[seat]->client->ps.pmove.origin[i]*0.125 + clent[seat]->client->ps.viewoffset[i];
leafnum = CM_PointLeafnum (sv.world.worldmodel, org[seat]);
clientarea[seat] = CM_LeafArea (sv.world.worldmodel, leafnum);
clientcluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
// calculate the visible areas
frame->areabytes = CM_WriteAreaBits (sv.world.worldmodel, frame->areabits, clientarea[seat], seat != 0);
sv.world.worldmodel->funcs.FatPVS(sv.world.worldmodel, org[seat], clientpvs, sizeof(clientpvs), seat!=0);
clientphs = CM_ClusterPHS (sv.world.worldmodel, clientcluster);
frame->ps[seat] = clent[seat]->client->ps;
if (sv.paused)
frame->ps[seat].pmove.pm_type = Q2PM_FREEZE;
}
seats = seat;
for (; seat < MAX_SPLITS; seat++)
{
memset(&frame->ps[seat], 0, sizeof(frame->ps[seat]));
frame->clientnum[seat] = 0xff; //invalid
}
// build up the list of visible entities
frame->num_entities = 0;
@ -679,60 +781,67 @@ void SVQ2_BuildClientFrame (client_t *client)
&& !ent->s.event)
continue;
// ignore if not touching a PV leaf
if (ent != clent)
for (seat = 0; seat < seats; seat++)
{
// check area
if (!CM_AreasConnected (sv.world.worldmodel, clientarea, ent->areanum))
{ // doors can legally straddle two areas, so
// we may need to check another one
if (!ent->areanum2
|| !CM_AreasConnected (sv.world.worldmodel, clientarea, ent->areanum2))
continue; // blocked by a door
}
// beams just check one point for PHS
if (ent->s.renderfx & Q2RF_BEAM)
// ignore if not touching a PV leaf
if (ent != clent[seat])
{
l = ent->clusternums[0];
if ( !(clientphs[l >> 3] & (1 << (l&7) )) )
continue;
}
else
{
// FIXME: if an ent has a model and a sound, but isn't
// in the PVS, only the PHS, clear the model
// check area
if (!CM_AreasConnected (sv.world.worldmodel, clientarea[seat], ent->areanum))
{ // doors can legally straddle two areas, so
// we may need to check another one
if (!ent->areanum2
|| !CM_AreasConnected (sv.world.worldmodel, clientarea[seat], ent->areanum2))
continue; // blocked by a door
}
if (ent->num_clusters == -1)
{ // too many leafs for individual check, go by headnode
if (!CM_HeadnodeVisible (sv.world.worldmodel, ent->headnode, clientpvs))
// beams just check one point for PHS
if (ent->s.renderfx & Q2RF_BEAM)
{
l = ent->clusternums[0];
if ( !(clientphs[l >> 3] & (1 << (l&7) )) )
continue;
c_fullsend++;
}
else
{ // check individual leafs
for (i=0 ; i < ent->num_clusters ; i++)
{
l = ent->clusternums[i];
if (clientpvs[l >> 3] & (1 << (l&7) ))
break;
{
// FIXME: if an ent has a model and a sound, but isn't
// in the PVS, only the PHS, clear the model
if (ent->num_clusters == -1)
{ // too many leafs for individual check, go by headnode
if (!CM_HeadnodeVisible (sv.world.worldmodel, ent->headnode, clientpvs))
continue;
c_fullsend++;
}
else
{ // check individual leafs
for (i=0 ; i < ent->num_clusters ; i++)
{
l = ent->clusternums[i];
if (clientpvs[l >> 3] & (1 << (l&7) ))
break;
}
if (i == ent->num_clusters)
continue; // not visible
}
if (i == ent->num_clusters)
continue; // not visible
}
if (!ent->s.modelindex)
{ // don't send sounds if they will be attenuated away
vec3_t delta;
float len;
if (!ent->s.modelindex)
{ // don't send sounds if they will be attenuated away
vec3_t delta;
float len;
VectorSubtract (org, ent->s.origin, delta);
len = Length (delta);
if (len > 400)
continue;
VectorSubtract (org[seat], ent->s.origin, delta);
len = Length (delta);
if (len > 400)
continue;
}
}
}
break;
}
if (seat == seats)
continue; //not visible to any seat
seat = 0; //FIXME
// add it to the circular client_entities array
state = &svs_client_entities[svs_next_client_entities%svs_num_client_entities];
@ -744,7 +853,7 @@ void SVQ2_BuildClientFrame (client_t *client)
*state = ent->s;
// don't mark players missiles as solid
if (ent->owner == client->q2edict)
if (ent->owner == clent[seat])
state->solid = 0;
svs_next_client_entities++;

View file

@ -126,6 +126,24 @@ static void VARGS PFQ2_Unicast (q2edict_t *ent, qboolean reliable)
if (client->state < cs_connected)
return;
if (client->controller)
{
client_t *peer;
for (p = 0, peer = client->controller; peer; peer = peer->controlled, p++)
{
if (peer == client)
break;
}
client = client->controller;
//svcq2_playerinfo is not normally valid except within the svc_frame message.
//this means its 'free' to repurpose for things like splitscreen. woot.
MSG_WriteShort(&sv.q2multicast, 0);
memmove(sv.q2multicast.data+2, sv.q2multicast.data, sv.q2multicast.cursize-2);
sv.q2multicast.data[0] = svcq2_playerinfo;
sv.q2multicast.data[1] = p;
}
if (reliable)
SZ_Write (&client->netchan.message, sv.q2multicast.data, sv.q2multicast.cursize);
else
@ -142,7 +160,7 @@ PF_dprintf
Debug print to server console
===============
*/
static void VARGS PFQ2_dprintf (char *fmt, ...)
static void VARGS PFQ2_dprintf (const char *fmt, ...)
{
char msg[1024];
va_list argptr;
@ -162,7 +180,7 @@ PF_cprintf
Print to a single client
===============
*/
static void VARGS PFQ2_cprintf (q2edict_t *ent, int level, char *fmt, ...)
static void VARGS PFQ2_cprintf (q2edict_t *ent, int level, const char *fmt, ...)
{
char msg[1024];
va_list argptr;
@ -202,7 +220,7 @@ PF_centerprintf
centerprint to a single client
===============
*/
static void VARGS PFQ2_centerprintf (q2edict_t *ent, char *fmt, ...)
static void VARGS PFQ2_centerprintf (q2edict_t *ent, const char *fmt, ...)
{
char msg[1024];
va_list argptr;
@ -232,7 +250,7 @@ PF_error
Abort the server with a game error
===============
*/
static void VARGS PFQ2_error (char *fmt, ...)
static void VARGS PFQ2_error (const char *fmt, ...)
{
char msg[1024];
va_list argptr;
@ -250,7 +268,7 @@ PF_Configstring
===============
*/
static void VARGS PFQ2_Configstring (int i, char *val)
static void VARGS PFQ2_Configstring (int i, const char *val)
{
if (i < 0 || i >= Q2MAX_CONFIGSTRINGS)
Sys_Error ("configstring: bad index %i\n", i);
@ -258,53 +276,12 @@ static void VARGS PFQ2_Configstring (int i, char *val)
if (!val)
val = "";
strcpy(sv.strings.configstring[i], val);
Z_Free((char*)sv.strings.configstring[i]);
sv.strings.configstring[i] = Z_StrDup(val);
if (i == Q2CS_NAME)
Q_strncpyz(sv.mapname, val, sizeof(sv.mapname));
/*
//work out range
if (i >= Q2CS_LIGHTS && i < Q2CS_LIGHTS+Q2MAX_LIGHTSTYLES)
{
j = i - Q2CS_LIGHTS;
if (j < MAX_LIGHTSTYLES)
{
if (sv.lightstyles[j])
Z_Free(sv.lightstyles[j]);
sv.lightstyles[j] = Z_Malloc(strlen(val)+1);
strcpy(sv.lightstyles[j], val);
}
}
else if (i >= Q2CS_MODELS && i < Q2CS_MODELS+Q2MAX_MODELS)
{
Q_strncpyS(sv.model_precache[i-Q2CS_MODELS], val, MAX_QPATH-1);
}
else if (i >= Q2CS_SOUNDS && i < Q2CS_SOUNDS+Q2MAX_SOUNDS)
{
Q_strncpyS(sv.sound_precache[i-Q2CS_SOUNDS], val, MAX_QPATH-1);
}
else if (i >= Q2CS_IMAGES && i < Q2CS_IMAGES+Q2MAX_IMAGES)
{
Q_strncpyS(sv.image_precache[i-Q2CS_IMAGES], val, MAX_QPATH-1);
}
else if (i == Q2CS_STATUSBAR)
{
if (sv.statusbar)
Z_Free(sv.statusbar);
sv.statusbar = Z_Malloc(strlen(val)+1);
strcpy(sv.statusbar, val);
}
else if (i == Q2CS_NAME)
{
Q_strncpyz(sv.mapname, val, sizeof(sv.name));
}
else
{
Con_Printf("Ignoring configstring %i\n", i);
}
*/
if (sv.state != ss_loading)
{ // send the update to everyone
SZ_Clear (&sv.q2multicast);
@ -316,25 +293,69 @@ static void VARGS PFQ2_Configstring (int i, char *val)
}
}
static int SVQ2_FindIndex (char *name, int start, int max, qboolean create)
static int SVQ2_FindIndex (const char *name, int start, int max, int overflowtype)
{
int i;
int stringlength = MAX_QPATH;
char *strings = sv.strings.configstring[start];
strings += stringlength;
const char *strings;
if (!name || !name[0])
return 0;
for (i=1 ; i<max && strings[0] ; i++, strings+=stringlength)
for (i=1 ; i<max ; i++)
{
strings = sv.strings.configstring[start+i];
if (!strings || !*strings)
break;
if (!strcmp(strings, name))
return i;
if (!create)
return 0;
}
if (i == max)
{
if (overflowtype)
{
const char **overflowstrings;
switch(overflowtype)
{
case 1:
overflowstrings = sv.strings.q2_extramodels;
max = countof(sv.strings.q2_extramodels);
i++; //do not allow 255 to be allocated, ever. just live with the gap.
start = 0x8000;
break;
case 2:
overflowstrings = sv.strings.q2_extrasounds;
max = countof(sv.strings.q2_extrasounds);
start = 0x8000|0x4000;
break;
default:
overflowstrings = NULL; //ssh
max = i;
break;
}
for ( ; i<max ; i++)
{
strings = overflowstrings[i];
if (!strings || !*strings)
{
overflowstrings[i] = Z_StrDup(name);
if (sv.state != ss_loading)
{
SZ_Clear (&sv.q2multicast);
MSG_WriteChar (&sv.q2multicast, svcq2_configstring);
MSG_WriteShort (&sv.q2multicast, start+i);
MSG_WriteString (&sv.q2multicast, name);
SV_Multicast (vec3_origin, MULTICAST_ALL_R);
}
return i;
}
if (!strcmp(strings, name))
return i;
}
}
Sys_Error ("*Index: overflow");
}
PFQ2_Configstring(start + i, name);
@ -342,19 +363,20 @@ static int SVQ2_FindIndex (char *name, int start, int max, qboolean create)
}
static int VARGS SVQ2_ModelIndex (char *name)
static int VARGS SVQ2_ModelIndex (const char *name)
{
return SVQ2_FindIndex (name, Q2CS_MODELS, Q2MAX_MODELS, true);
//model 255 is special for players. don't use it.
return SVQ2_FindIndex (name, Q2CS_MODELS, Q2MAX_MODELS-1, 1);
}
static int VARGS SVQ2_SoundIndex (char *name)
static int VARGS SVQ2_SoundIndex (const char *name)
{
return SVQ2_FindIndex (name, Q2CS_SOUNDS, Q2MAX_SOUNDS, true);
return SVQ2_FindIndex (name, Q2CS_SOUNDS, Q2MAX_SOUNDS, 2);
}
static int VARGS SVQ2_ImageIndex (char *name)
static int VARGS SVQ2_ImageIndex (const char *name)
{
return SVQ2_FindIndex (name, Q2CS_IMAGES, Q2MAX_IMAGES, true);
return SVQ2_FindIndex (name, Q2CS_IMAGES, Q2MAX_IMAGES, 0);
}
/*
@ -364,7 +386,7 @@ PF_setmodel
Also sets mins and maxs for inline bmodels
=================
*/
static void VARGS PFQ2_setmodel (q2edict_t *ent, char *name)
static void VARGS PFQ2_setmodel (q2edict_t *ent, const char *name)
{
int i;
model_t *mod;
@ -410,7 +432,7 @@ static void VARGS PFQ2_WriteByte (int c) {MSG_WriteByte (&sv.q2multicast, c & 0x
static void VARGS PFQ2_WriteShort (int c) {MSG_WriteShort (&sv.q2multicast, c & 0xffff);}
static void VARGS PFQ2_WriteLong (int c) {MSG_WriteLong (&sv.q2multicast, c);}
static void VARGS PFQ2_WriteFloat (float f) {MSG_WriteFloat (&sv.q2multicast, f);}
static void VARGS PFQ2_WriteString (char *s) {MSG_WriteString (&sv.q2multicast, s);}
static void VARGS PFQ2_WriteString (const char *s) {MSG_WriteString (&sv.q2multicast, s);}
static void VARGS PFQ2_WriteAngle (float f) {MSG_WriteAngle (&sv.q2multicast, f);}
static void VARGS PFQ2_WritePos (vec3_t pos) { MSG_WriteCoord (&sv.q2multicast, pos[0]);
MSG_WriteCoord (&sv.q2multicast, pos[1]);
@ -487,15 +509,6 @@ qboolean VARGS PFQ2_AreasConnected(unsigned int area1, unsigned int area2)
}
#define Q2SND_VOLUME (1<<0) // a byte
#define Q2SND_ATTENUATION (1<<1) // a byte
#define Q2SND_POS (1<<2) // three coordinates
#define Q2SND_ENT (1<<3) // a short 0-2: channel, 3-12: entity
#define Q2SND_OFFSET (1<<4) // a byte, msec offset from frame start
#define Q2DEFAULT_SOUND_PACKET_VOLUME 1.0
#define Q2DEFAULT_SOUND_PACKET_ATTENUATION 1.0
#define Q2ATTN_NONE 0 // full volume the entire level
@ -508,7 +521,7 @@ qboolean VARGS PFQ2_AreasConnected(unsigned int area1, unsigned int area2)
#define Q2CHAN_NO_PHS_ADD 8
#define Q2CHAN_RELIABLE 16
void VARGS SVQ2_StartSound (vec3_t origin, q2edict_t *entity, int channel,
static void VARGS SVQ2_StartSound (vec3_t origin, q2edict_t *entity, int channel,
int soundindex, float volume,
float attenuation, float timeofs)
{
@ -518,6 +531,7 @@ void VARGS SVQ2_StartSound (vec3_t origin, q2edict_t *entity, int channel,
int ent;
vec3_t origin_v;
qboolean use_phs;
unsigned int needext = 0;
if (volume < 0 || volume > 1.0)
Sys_Error ("SV_StartSound: volume = %f", volume);
@ -545,6 +559,11 @@ void VARGS SVQ2_StartSound (vec3_t origin, q2edict_t *entity, int channel,
flags |= Q2SND_VOLUME;
if (attenuation != Q2DEFAULT_SOUND_PACKET_ATTENUATION)
flags |= Q2SND_ATTENUATION;
if (soundindex > 0xff)
{
flags |= Q2SND_LARGEIDX;
needext |= PEXT_SOUNDDBL;
}
// the client doesn't know that bmodels have weird origins
// the origin can also be explicitly set
@ -572,11 +591,25 @@ void VARGS SVQ2_StartSound (vec3_t origin, q2edict_t *entity, int channel,
{
VectorCopy (entity->s.origin, origin_v);
}
if (flags & Q2SND_POS)
{
for (i=0 ; i<3 ; i++)
if (-32768/8.0 > origin_v[i] || origin_v[i] > 32767/8.0)
{
flags |= Q2SND_LARGEPOS;
needext |= PEXT_FLOATCOORDS;
break;
}
}
}
MSG_WriteByte (&sv.q2multicast, svcq2_sound);
MSG_WriteByte (&sv.q2multicast, flags);
MSG_WriteByte (&sv.q2multicast, soundindex);
if (flags & Q2SND_LARGEIDX)
MSG_WriteShort (&sv.q2multicast, soundindex);
else
MSG_WriteByte (&sv.q2multicast, soundindex);
if (flags & Q2SND_VOLUME)
MSG_WriteByte (&sv.q2multicast, volume*255);
@ -590,9 +623,18 @@ void VARGS SVQ2_StartSound (vec3_t origin, q2edict_t *entity, int channel,
if (flags & Q2SND_POS)
{
MSG_WriteCoord (&sv.q2multicast, origin[0]);
MSG_WriteCoord (&sv.q2multicast, origin[1]);
MSG_WriteCoord (&sv.q2multicast, origin[2]);
if (flags & Q2SND_LARGEPOS)
{
MSG_WriteFloat (&sv.q2multicast, origin[0]);
MSG_WriteFloat (&sv.q2multicast, origin[1]);
MSG_WriteFloat (&sv.q2multicast, origin[2]);
}
else
{
MSG_WriteCoord (&sv.q2multicast, origin[0]);
MSG_WriteCoord (&sv.q2multicast, origin[1]);
MSG_WriteCoord (&sv.q2multicast, origin[2]);
}
}
// if the sound doesn't attenuate,send it to everyone
@ -601,19 +643,9 @@ void VARGS SVQ2_StartSound (vec3_t origin, q2edict_t *entity, int channel,
use_phs = false;
if (channel & Q2CHAN_RELIABLE)
{
if (use_phs)
SV_Multicast (origin, MULTICAST_PHS_R);
else
SV_Multicast (origin, MULTICAST_ALL_R);
}
SV_MulticastProtExt(origin, use_phs?MULTICAST_PHS_R:MULTICAST_ALL_R, FULLDIMENSIONMASK, needext, 0);
else
{
if (use_phs)
SV_Multicast (origin, MULTICAST_PHS);
else
SV_Multicast (origin, MULTICAST_ALL);
}
SV_MulticastProtExt(origin, use_phs?MULTICAST_PHS:MULTICAST_ALL, FULLDIMENSIONMASK, needext, 0);
}
static void VARGS PFQ2_StartSound (q2edict_t *entity, int channel, int sound_num, float volume,
@ -653,7 +685,7 @@ static int VARGS SVQ2_PointContents (vec3_t p)
// return CM_PointContents(p, 0);
}
static cvar_t *VARGS Q2Cvar_Get (char *var_name, char *value, int flags)
static cvar_t *VARGS Q2Cvar_Get (const char *var_name, const char *value, int flags)
{
cvar_t *var = Cvar_Get(var_name, value, flags, "Quake2 game variables");
if (!var)
@ -664,7 +696,7 @@ static cvar_t *VARGS Q2Cvar_Get (char *var_name, char *value, int flags)
return var;
}
cvar_t *VARGS Q2Cvar_Set (char *var_name, char *value)
cvar_t *VARGS Q2Cvar_Set (const char *var_name, const char *value)
{
cvar_t *var = Cvar_FindVar(var_name);
if (!var)
@ -674,7 +706,7 @@ cvar_t *VARGS Q2Cvar_Set (char *var_name, char *value)
}
return Cvar_Set(var, value);
}
cvar_t *VARGS Q2Cvar_ForceSet (char *var_name, char *value)
cvar_t *VARGS Q2Cvar_ForceSet (const char *var_name, const char *value)
{
cvar_t *var = Cvar_FindVar(var_name);
if (!var)
@ -704,7 +736,7 @@ void VARGS SVQ2_ShutdownGameProgs (void)
ge = NULL;
}
static void VARGS AddCommandString(char *command)
static void VARGS AddCommandString(const char *command)
{
Cbuf_AddText(command, RESTRICT_LOCAL);
}
@ -717,7 +749,7 @@ Init the game subsystem for a new map
===============
*/
void VARGS Q2SCR_DebugGraph(float value, int color)
static void VARGS Q2SCR_DebugGraph(float value, int color)
{return;}
static void VARGS SVQ2_LinkEdict (q2edict_t *ent)

View file

@ -2166,6 +2166,8 @@ static qboolean GenerateCollisionMesh_BSP(world_t *world, model_t *mod, wedict_t
mesh_t *mesh;
unsigned int numverts;
unsigned int numindexes,i;
int *ptr_elements;
float *ptr_verts;
numverts = 0;
numindexes = 0;
@ -2192,8 +2194,8 @@ static qboolean GenerateCollisionMesh_BSP(world_t *world, model_t *mod, wedict_t
Con_DPrintf("entity %i (classname %s) has no geometry\n", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname));
return false;
}
ed->ode.ode_element3i = (int*)BZ_Malloc(numindexes*sizeof(*ed->ode.ode_element3i));
ed->ode.ode_vertex3f = (float*)BZ_Malloc(numverts*sizeof(vec3_t));
ptr_elements = (int*)BZ_Malloc(numindexes*sizeof(*ptr_elements));
ptr_verts = (float*)BZ_Malloc(numverts*sizeof(vec3_t));
numverts = 0;
numindexes = 0;
@ -2207,13 +2209,13 @@ static qboolean GenerateCollisionMesh_BSP(world_t *world, model_t *mod, wedict_t
{
mesh = surf->mesh;
for (i = 0; i < mesh->numvertexes; i++)
VectorSubtract(mesh->xyz_array[i], geomcenter, (ed->ode.ode_vertex3f + 3*(numverts+i)));
VectorSubtract(mesh->xyz_array[i], geomcenter, (ptr_verts + 3*(numverts+i)));
for (i = 0; i < mesh->numindexes; i+=3)
{
//flip the triangles as we go
ed->ode.ode_element3i[numindexes+i+0] = numverts+mesh->indexes[i+2];
ed->ode.ode_element3i[numindexes+i+1] = numverts+mesh->indexes[i+1];
ed->ode.ode_element3i[numindexes+i+2] = numverts+mesh->indexes[i+0];
ptr_elements[numindexes+i+0] = numverts+mesh->indexes[i+2];
ptr_elements[numindexes+i+1] = numverts+mesh->indexes[i+1];
ptr_elements[numindexes+i+2] = numverts+mesh->indexes[i+0];
}
numverts += mesh->numvertexes;
numindexes += i;
@ -2238,19 +2240,21 @@ static qboolean GenerateCollisionMesh_BSP(world_t *world, model_t *mod, wedict_t
vec = mod->vertexes[edge->v[1]].position;
}
VectorSubtract(vec, geomcenter, (ed->ode.ode_vertex3f + 3*(numverts+i)));
VectorSubtract(vec, geomcenter, (ptr_verts + 3*(numverts+i)));
}
for (i = 2; i < surf->numedges; i++)
{
//quake is backwards, not ode
ed->ode.ode_element3i[numindexes++] = numverts+i;
ed->ode.ode_element3i[numindexes++] = numverts+i-1;
ed->ode.ode_element3i[numindexes++] = numverts;
ptr_elements[numindexes++] = numverts+i;
ptr_elements[numindexes++] = numverts+i-1;
ptr_elements[numindexes++] = numverts;
}
numverts += surf->numedges;
}
}
ed->ode.ode_element3i = ptr_elements;
ed->ode.ode_vertex3f = ptr_verts;
ed->ode.ode_numvertices = numverts;
ed->ode.ode_numtriangles = numindexes/3;
return true;
@ -2265,6 +2269,8 @@ static qboolean GenerateCollisionMesh_Alias(world_t *world, model_t *mod, wedict
galiasinfo_t *inf;
unsigned int surfnum = 0;
entity_t re;
int *ptr_elements;
float *ptr_verts;
numverts = 0;
numindexes = 0;
@ -2287,8 +2293,8 @@ static qboolean GenerateCollisionMesh_Alias(world_t *world, model_t *mod, wedict
Con_DPrintf("entity %i (classname %s) has no geometry\n", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname));
return false;
}
ed->ode.ode_element3i = (int*)BZ_Malloc(numindexes*sizeof(*ed->ode.ode_element3i));
ed->ode.ode_vertex3f = (float*)BZ_Malloc(numverts*sizeof(vec3_t));
ptr_elements = (int*)BZ_Malloc(numindexes*sizeof(*ptr_elements));
ptr_verts = (float*)BZ_Malloc(numverts*sizeof(vec3_t));
numverts = 0;
numindexes = 0;
@ -2298,13 +2304,13 @@ static qboolean GenerateCollisionMesh_Alias(world_t *world, model_t *mod, wedict
{
Alias_GAliasBuildMesh(&mesh, NULL, inf, surfnum++, &re, false);
for (i = 0; i < mesh.numvertexes; i++)
VectorSubtract(mesh.xyz_array[i], geomcenter, (ed->ode.ode_vertex3f + 3*(numverts+i)));
VectorSubtract(mesh.xyz_array[i], geomcenter, (ptr_verts + 3*(numverts+i)));
for (i = 0; i < mesh.numindexes; i+=3)
{
//flip the triangles as we go
ed->ode.ode_element3i[numindexes+i+0] = numverts+mesh.indexes[i+2];
ed->ode.ode_element3i[numindexes+i+1] = numverts+mesh.indexes[i+1];
ed->ode.ode_element3i[numindexes+i+2] = numverts+mesh.indexes[i+0];
ptr_elements[numindexes+i+0] = numverts+mesh.indexes[i+2];
ptr_elements[numindexes+i+1] = numverts+mesh.indexes[i+1];
ptr_elements[numindexes+i+2] = numverts+mesh.indexes[i+0];
}
numverts += inf->numverts;
numindexes += inf->numindexes;
@ -2313,13 +2319,15 @@ static qboolean GenerateCollisionMesh_Alias(world_t *world, model_t *mod, wedict
Alias_FlushCache(); //it got built using an entity on the stack, make sure other stuff doesn't get hurt.
ed->ode.ode_element3i = ptr_elements;
ed->ode.ode_vertex3f = ptr_verts;
ed->ode.ode_numvertices = numverts;
ed->ode.ode_numtriangles = numindexes/3;
return true;
}
//Bullet has a fit if we have any degenerate triangles, so make sure we can determine some surface normal
static void World_Bullet_CleanupMesh(wedict_t *ed)
static void CollisionMesh_CleanupMesh(wedict_t *ed)
{
float *v1, *v2, *v3;
vec3_t d1, d2, cr;
@ -2363,7 +2371,7 @@ qboolean QDECL World_GenerateCollisionMesh(world_t *world, model_t *mod, wedict_
if (result)
{
World_Bullet_CleanupMesh(ed);
CollisionMesh_CleanupMesh(ed);
if (ed->ode.ode_numtriangles > 0)
return true;
}

View file

@ -167,7 +167,7 @@ void SWBE_DrawMesh_List(shader_t *shader, int nummeshes, struct mesh_s **mesh, s
void SWBE_DrawMesh_Single(shader_t *shader, struct mesh_s *meshchain, struct vbo_s *vbo, unsigned int be_flags);
void SWBE_SubmitBatch(struct batch_s *batch);
struct batch_s *SWBE_GetTempBatch(void);
void SWBE_DrawWorld(qboolean drawworld, qbyte *vis);
void SWBE_DrawWorld(batch_t **worldbatches, qbyte *vis);
void SWBE_Init(void);
void SWBE_GenBrushModelVBO(struct model_s *mod);
void SWBE_ClearVBO(struct vbo_s *vbo);

View file

@ -6,6 +6,11 @@
vecV_t vertbuf[65535];
swimage_t sw_nulltex =
{
1, 1, 0, 0, 0, 0
};
static struct
{
int foo;
@ -420,6 +425,7 @@ void SWBE_TransformVerticies(swvert_t *v, mesh_t *mesh)
// v->colour[3] = mesh->colors4b_array[i][3];
}
}
static void SWBE_DrawMesh_Internal(shader_t *shader, mesh_t *mesh, struct vbo_s *vbo, struct texnums_s *texnums, unsigned int be_flags)
{
wqcom_t *com;
@ -438,7 +444,10 @@ static void SWBE_DrawMesh_Internal(shader_t *shader, mesh_t *mesh, struct vbo_s
{
com = SWRast_BeginCommand(&commandqueue, WTC_TRIFAN, mesh->numvertexes*sizeof(swvert_t) + sizeof(com->trifan) - sizeof(com->trifan.verts));
com->trifan.texture = texnums->base->ptr;
if (texnums->base)
com->trifan.texture = texnums->base->ptr;
else
com->trifan.texture = &sw_nulltex;
com->trifan.numverts = mesh->numvertexes;
SWBE_TransformVerticies(com->trifan.verts, mesh);
@ -449,7 +458,10 @@ static void SWBE_DrawMesh_Internal(shader_t *shader, mesh_t *mesh, struct vbo_s
{
com = SWRast_BeginCommand(&commandqueue, WTC_TRISOUP, (mesh->numvertexes*sizeof(swvert_t)) + sizeof(com->trisoup) - sizeof(com->trisoup.verts) + (sizeof(index_t)*mesh->numindexes));
com->trisoup.texture = texnums->base->ptr;
if (texnums->base)
com->trisoup.texture = texnums->base->ptr;
else
com->trisoup.texture = &sw_nulltex;
com->trisoup.numverts = mesh->numvertexes;
com->trisoup.numidx = mesh->numindexes;
@ -530,19 +542,18 @@ static void SWBE_SubmitMeshesSortList(batch_t *sortlist)
}
}
void SWBE_SubmitMeshes (qboolean drawworld, batch_t **blist, int start, int stop)
void SWBE_SubmitMeshes (batch_t **worldbatches, batch_t **blist, int start, int stop)
{
model_t *model = cl.worldmodel;
int i;
for (i = start; i <= stop; i++)
{
if (drawworld)
if (worldbatches)
{
// if (i == SHADER_SORT_PORTAL && !r_noportals.ival && !r_refdef.recurse)
// SWBE_SubmitMeshesPortals(model->batches, blist[i]);
// SWBE_SubmitMeshesPortals(worldbatches, blist[i]);
SWBE_SubmitMeshesSortList(model->batches[i]);
SWBE_SubmitMeshesSortList(worldbatches[i]);
}
SWBE_SubmitMeshesSortList(blist[i]);
}
@ -599,7 +610,7 @@ void SWBE_Set2D(void)
SWBE_UpdateUniforms();
}
void SWBE_DrawWorld(qboolean drawworld, qbyte *vis)
void SWBE_DrawWorld(batch_t **worldbatches, qbyte *vis)
{
batch_t *batches[SHADER_SORT_COUNT];
@ -621,7 +632,7 @@ void SWBE_DrawWorld(qboolean drawworld, qbyte *vis)
shaderstate.curentity = NULL;
SWBE_SelectEntity(&r_worldentity);
SWBE_SubmitMeshes(drawworld, batches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);
SWBE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);
SWBE_Set2D();
}

View file

@ -103,11 +103,11 @@ qboolean SW_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips)
imgdata[i+3] = 255;
}
for (i = 0; i < mips->mipcount; i++)
if (mips->mip[i].needfree)
Z_Free(mips->mip[i].data);
if (mips->extrafree)
Z_Free(mips->extrafree);
// for (i = 0; i < mips->mipcount; i++)
// if (mips->mip[i].needfree)
// Z_Free(mips->mip[i].data);
// if (mips->extrafree)
// Z_Free(mips->extrafree);
return true;
}

109
quakec/menusys/README.TXT Normal file
View file

@ -0,0 +1,109 @@
This directory contains my menusys mod (with menusys qc library).
This mod is provided to serve as a base mod for menuqc or menus within csqc.
To add a new item onto the menus, you can open up the relavent menu/*.qc file, find an item of the same widget type, and dupe then edit that line for your new item, following the example given.
There's often a little extra logic used to center the menu vertically, so you may also need to tweak some numbers before anything is added.
You should NOT need to edit menusys/*.qc - you can if you want, but I wouldn't recommend it to beginners.
To add a new menu, open your .src file, find the concommandslist macro and add an extra line following the example given. This provides you with console command -> function mappings (as well as including the right qc file, if specified - hurrah for each menu being its own qc file). Then you can just copy+paste one of my menus and tweak it a little.
Some more detailed info on menusys:
The menusys library is heirachical. It uses the concept of a 'desktop' object as the root node (in csqc, this would normally be expected to provide the draw calls for the actual game).
Then top-level windows/menus are embedded upon that desktop. And then sliders and buttons etc are normally embedded in those windows/menus. Note that you could directly embed your widgets on the desktop, but this can make using the mouse awkward as there's no easy way to release the mouse so you can interact with things.
Menusys also uses inheritance. Thus, you can easily create your own 'frame' object that inherits from mitem_frame, and thereby acts as a container for other widgets (for example).
DP compatibility:
This mod should also work in DP, however there are some exceptions.
Things not supported by DP obviously cannot be used with DP. This includes 3d models in menuqc, server browsers in csqc, etc.
My personal focus is on FTE, and thus you will likely want to research DP cvars in order to include any additional DP-only cvars that you may wish to include.
I've used a variable called 'dp_workarounds' in many places in order to work around the DP bugs / incompatibilities that are obvious enough for me to notice.
Feel free to provide diffs to fix anything that you notice (make sure they work in FTE too though).
MenuQC:
The presence of menu.dat causes the engine's built-in menus to become inaccessible. This means that any mod wishing to provide a single extra option on the menus needs to rewrite the entire thing from scratch...
And menuqc is somewhat mythical as a result - hopefully this mod will challenge that perception by making it slightly more accessible.
CSQC:
The CSQC version of this mod provides a 'full' set of the menus equivelent to the menuqc ones. This typically means that using both becomes redundant. Thus you may well wish to remove most of the menus from csqc so that you can use only the menuqc ones. If you want in-game menus like class customisation then you will likely want those menus in csqc in order to more easily communicate with your server.
Alternatively, if you set pr_csqcformenus to 1 in your default.cfg, then FTE will use your csprogs.dat instead of any menu.dat. This allows you to implement ALL your menus within a single module, which you may find to be a simplification. Note that this does not bypass cheat protections, and thus the csqc version will still generally not work on public servers. However, if you're making a stand-alone mod, this means that you can completely disregard menu.dat module (NOTE: this is only an option in FTE, not DP).
If your entire game is strictly single player, then look up my PureCSQC mod some time, which does not involve ssqc or even a server at all. Using PureCSQC as a base mod may help some headaches, however you'll need to merge the entry points yourself somehow (PureCSQC incorporates its own partial menu system more as an example of 2d input/drawing than actual menus).
When both modules are loaded, the csqc receives escape events and thus invokes its own menu. Because the menus are linked via console commands, you can easily implement the in-game menu in csqc, and all other menus in menuqc.
Your mod:
If you're creating a separate mod with its own special defaults then it is recommended to provide your own default.cfg
Your default.cfg should not only contain bind commands for your default key settings, and defaults for engine cvars, but you should also use the set or seta commands for your own cvars.
(note that if you use '-v -v' on fteqcc's commandline, it will generate a full autocvar listing from each module, including default values and some descriptions - just change any you wish to be flagged for archiving to use seta instead of set).
Keybinds:
One common thing modders want is new keybinds.
you can either open menus/options_keys.qc and edit the array there.
Alternatively, you can create a bindlist.lst file with <command> <description> quoted pairs on each line. If the command is a hyphen then its treated as a caption.
If there are too many keys, the frame menuitem thing will add a scrollbar for you, so go ahead and define a hundred different keys.
A Mod Options Menu:
The easy way to add a menu is to just copy+paste an existing one, like the video menu.
First, open up the appropriate src file and add a line like the following in the place that should be obvious:
cmd("m_mod", M_Options_Mod, menu/options_mod.qc) \
That should create the console command for you, associate that with a function, AND include your menu's qc file into the progs.
Now copy menu/options_video.qc to the obvious name, and start editing it.
Okay, these lines:
mitem_exmenu m;
m = spawn(mitem_exmenu, item_text:_("Video Options"), item_flags:IF_SELECTABLE, item_command:"m_options");
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
desktop.item_focuschange(m, IF_KFOCUSED);
m.totop();
creates a fullscreen menu thing, they add it into the desktop, they give focus to it, and they make sure that it appears on top of everything else.
the spawn call is some special class stuff, with named field initialisations. The 'Video Options' thing isn't actually visible, but hey. The m_options string says which console command to issue when the menu is closed (by right-click or escape), this allows you to return to the previous menu.
And the following lines:
float h = 200 * 0.5;
mitem_pic banner = spawn(mitem_pic, item_text:"gfx/p_option.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
m.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [(160-160-banner.item_size_x)*0.5, -h-32], [banner.item_size_x, -h-8]);
adds a static image. woo.
The RS_ constants are 'ReSize' constants. They say how to calculate the items top-left and bottom-right positions whenever the item is added or its parent is resized. The two vectors are additional offsets from the reference points that the RS flags give.
In this case, it just centers the image above some 200qu-high area in the middle of the screen.
And these lines are fun:
mitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);
m.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, -h], [160, h*2]);
they an invisible 'frame' that then holds the widgits that you're actually going to interact with.
While that sounds a little useless at first, this provides two things: 1) if there are two many items, the frame_hasscroll value causes it to include a scrollbar, allowing the items to be scrolled without affecting any of the artwork attached to the menu itself (like that title image). and 2) child items are clipped to this frame.
This line:
addmenuback(m);
quite simply just creates a widgit on the menu that darkens the background (insertion order affects draw order, this should be last-ish, so that it is drawn BEFORE any of the other widgits, such that it darkens everything other than the menu.
Lines like this:
fr.add(menuitemcombo_spawn(_("Video Width"), "vid_width", '280 8', _(
"0 \"Default\" "
"640 \"640\" "
"800 \"800\" "
"1024 \"1024\" "
"1280 \"1280\" "
"1920 \"1920\" "
)), fl, [0, pos], [0, 8]); pos += 8;
just adds a single widgit into the frame's area (its the same add method as before, with the same RS_ flags in the fl argument), so I only really need to explain menuitemcombo_spawn here.
menuitemcombo_spawn's first argument is the text to put next to it. The _("") weirdness provides some internationalisation thing that I'm far too lazy to go into right now (ask divverent or something for tools to use this properly).
The second argument is the 'cvar' that its attached to (actually, you can subclass the parent item and handle the get+set events yourself).
The third is the size of the widget. Generally they should be wide enough for both the text and the value. And about 8 vpixels high.
And finally, the fourth argument is the list of possible values, and the text that should be shown if that value is selected. Easy, right?
Alternatively, this line:
fr.add(menuitemslider_spawn(_("View Size"), "viewsize", '50 120 10', '280 8'), fl, [0, pos], [0, 8]); pos += 8;
spawns a 'slider' opject. Its basically the same as combos, except that the 3rd argument is the mix, max, and step-size values. Note that min+max can be inverted if you want (you should also negate the graduation in this case).
And this line:
fr.add(menuitemcheck_spawn(_("Show Framerate"), dp("showfps", "show_fps"), '280 8'), fl, [0, pos], [0, 8]); pos += 8;
is how you add a checkbox. There is no 'third' argument in this case, just that fourth (okay, so it does have a third, but shush).
The 'dp' function you see there is part of how my code handles cvar differences between DP and other engines. If cvar_type reports that the first cvar exists, then it uses that. Otherwise it uses the second. Simple.
Unfortunately this only detects whether a cvar exists or not, and can't handle different values very well, which is what the vid_fullscreen hack is all about. Yes, that sort of thing can be really awkward...
And finally, in order to add a link to your new menu from the main or options menu or whereever, just open the qc file for that menu and copy one of the add+spawn lines to provide a textitem that invokes your menu's console command when clicked.
Easy.
Server browser:
QC-based server browsers have a significant limitation - they have no access to the system clipboard. You can't copy+paste ip numbers etc.
This is why I've decided to just utilise FTE's normal server browser even with menuqc active. This isn't an option in DP unfortunately, and your mod might be sufficiently different for the engine's to not work too well. Or you might want to exclude other servers...
If you want to tie your server browser to a specific gamedir, for instance, you can find the MOD_GAMEDIR define, set it to something, and enable it.
My code uses lots of preprocessor tricks too, sorry about that. You'll get used to them eventually, and hopefully understand why I used them too!...
You'll likely need to polish it quite a lot before its truely friendly.
If you do polish it, feel free to send me a patch (assuming it's not too specific to your mod).

View file

@ -0,0 +1,112 @@
mitem_desktop thedesktop;
void(mitem_desktop desktop) M_Pop =
{
mitem it = desktop.item_kactivechild;
if (it)
it.item_remove();
};
void(mitem_desktop desktop) M_ToggleMenu =
{
mitem it = desktop.item_kactivechild;
if (it)
it.item_remove();
else
M_Main(desktop);
};
float(string str) CSQC_ConsoleCommand =
{
local float args;
args = tokenize(str);
switch(argv(0))
{
#define cmd(n,fnc,inc) case n: fnc(thedesktop); return TRUE;
concommandslist
#undef cmd
default:
return FALSE;
}
return TRUE;
};
float(float isnew) updateplayer =
{
self.drawmask = 1;
if (self.entnum == player_localentnum)
self.renderflags = RF_EXTERNALMODEL;
else
self.renderflags = 0;
return TRUE;
};
//void(float apilevel, string enginename, float engineversion) CSQC_Init =
//{
// deltalisten("progs/player.mdl", updateplayer, 0);
//};
var float autocvar_dp_workarounds_allow = TRUE;
var float autocvar_dp_workarounds_force = FALSE;
void(float apilevel, string enginename, float engineversion) CSQC_Init =
{
dprint(sprintf("CSQC: Running on \"%s\" ver=%g, api=%g\n", enginename, engineversion, apilevel));
if (!apilevel)
dp_workarounds = autocvar_dp_workarounds_allow;
if (autocvar_dp_workarounds_force)
dp_workarounds = TRUE;
if (dp_workarounds)
print("DP-specific workarounds are enabled\n");
//make sure the engine knows what commands we want to handle
#define cmd(n,fnc,inc) registercommand(n);
concommandslist
#undef cmd
// Hud_Init(); //make sure the hud images are precached properly, so there's no stalls.
};
float (float event, float parama, float paramb, float devid) CSQC_InputEvent =
{
if (!thedesktop)
return event!=IE_KEYUP;
if (items_keypress(thedesktop, event, parama, paramb, devid))
return TRUE;
return FALSE;
};
/*The desktop object will not normally draw anything, but you can get the desktop object to do the drawing by overriding its 'drawgame' method.
The primary advantage of doing the drawing this way is that the menu system can properly handle mouse positions in 3d space with multiple views. The menu system also handles splitscreen efficiently. Note that the menu system will handle clearing the scene and adding entities before this function is called.
You could instead draw the game then draw the menusystem over the top, if you're more comfortable with that.
*/
class mydesktop : mitem_desktop
{
virtual void(float seat, vector minpos, vector size) drawgame =
{
setproperty(VF_DRAWENGINESBAR, TRUE);
/*
entity playerent = findfloat(world, entnum, player_localentnum);
if (playerent)
{
vector vorg = playerent.origin - playerent.gravitydir*getstat(STAT_VIEWHEIGHT);
vector vang = playerent.v_angle;
setproperty(VF_ORIGIN, vorg);
setproperty(VF_ANGLES, vang);
makevectors(vang);
SetListener(vorg, v_forward, v_right, v_up, playerent.waterlevel==3);
}
*/
renderscene();
};
};
void(float width, float height, float do2d) CSQC_UpdateView =
{
if (!thedesktop)
thedesktop = spawn(mydesktop);
items_draw(thedesktop);
};
//void(float width, float height, float do2d) CSQC_UpdateView_Loading = CSQC_UpdateView;

Some files were not shown because too many files have changed in this diff Show more