mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-13 07:47:45 +00:00
ce3c561cfe
Particle system functions renamed a bit, a few other cleanups in that area. Console handling tweeked. Better rules for subconsoles and plugins. Commands are coloured if it'll be execed, which should help reduce occurences of chat being commands. tab compleation tweeked, partial compleation no longer changes the suggestion. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@895 fc73d0e0-1445-4013-8a0c-d673dee63da5
4338 lines
101 KiB
C
4338 lines
101 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
// cl_parse.c -- parse a message received from the server
|
|
|
|
#include "quakedef.h"
|
|
|
|
void CL_GetNumberedEntityInfo (int num, float *org, float *ang);
|
|
void CLNQ_ParseDarkPlaces5Entities(void);
|
|
void CL_SetStat (int pnum, int stat, int value);
|
|
|
|
int nq_dp_protocol;
|
|
|
|
|
|
|
|
char *svc_strings[] =
|
|
{
|
|
"svc_bad",
|
|
"svc_nop",
|
|
"svc_disconnect",
|
|
"svc_updatestat",
|
|
"svc_version", // [long] server version
|
|
"svc_setview", // [short] entity number
|
|
"svc_sound", // <see code>
|
|
"svc_time", // [float] server time
|
|
"svc_print", // [string] null terminated string
|
|
"svc_stufftext", // [string] stuffed into client's console buffer
|
|
// the string should be \n terminated
|
|
"svc_setangle", // [vec3] set the view angle to this absolute value
|
|
|
|
"svc_serverdata", // [long] version ...
|
|
"svc_lightstyle", // [qbyte] [string]
|
|
"svc_updatename", // [qbyte] [string]
|
|
"svc_updatefrags", // [qbyte] [short]
|
|
"svc_clientdata", // <shortbits + data>
|
|
"svc_stopsound", // <see code>
|
|
"svc_updatecolors", // [qbyte] [qbyte]
|
|
"svc_particle", // [vec3] <variable>
|
|
"svc_damage", // [qbyte] impact [qbyte] blood [vec3] from
|
|
|
|
"svc_spawnstatic",
|
|
"svc_spawnstatic2",
|
|
"svc_spawnbaseline",
|
|
|
|
"svc_temp_entity", // <variable>
|
|
"svc_setpause",
|
|
"svc_signonnum",
|
|
"svc_centerprint",
|
|
"svc_killedmonster",
|
|
"svc_foundsecret",
|
|
"svc_spawnstaticsound",
|
|
"svc_intermission",
|
|
"svc_finale",
|
|
|
|
"svc_cdtrack",
|
|
"svc_sellscreen",
|
|
|
|
"svc_smallkick",
|
|
"svc_bigkick",
|
|
|
|
"svc_updateping",
|
|
"svc_updateentertime",
|
|
|
|
"svc_updatestatlong",
|
|
"svc_muzzleflash",
|
|
"svc_updateuserinfo",
|
|
"svc_download",
|
|
"svc_playerinfo",
|
|
"svc_nails",
|
|
"svc_choke",
|
|
"svc_modellist",
|
|
"svc_soundlist",
|
|
"svc_packetentities",
|
|
"svc_deltapacketentities",
|
|
"svc_maxspeed",
|
|
"svc_entgravity",
|
|
|
|
"svc_setinfo",
|
|
"svc_serverinfo",
|
|
"svc_updatepl",
|
|
"NEW svc_nails2",
|
|
"OBSOLETE",
|
|
"NEW svc_view2",
|
|
"NEW svc_lightstylecol",
|
|
"NEW svc_bulletentext",
|
|
"NEW svc_lightnings",
|
|
"NEW svc_modellistshort",
|
|
"NEW svc_ftesetclientpersist",
|
|
"NEW svc_setportalstate",
|
|
"NEW svc_particle2",
|
|
"NEW svc_particle3",
|
|
"NEW svc_particle4",
|
|
"NEW svc_spawnbaseline2",
|
|
"NEW svc_customtempent",
|
|
"NEW svc_choosesplitclient",
|
|
"NEW PROTOCOL"
|
|
};
|
|
|
|
char *svc_nqstrings[] =
|
|
{
|
|
"nqsvc_bad",
|
|
"nqsvc_nop",
|
|
"nqsvc_disconnect",
|
|
"nqsvc_updatestat",
|
|
"nqsvc_version", // [long] server version
|
|
"nqsvc_setview", // [short] entity number
|
|
"nqsvc_sound", // <see code>
|
|
"nqsvc_time", // [float] server time
|
|
"nqsvc_print", // [string] null terminated string
|
|
"nqsvc_stufftext", // [string] stuffed into client's console buffer
|
|
// the string should be \n terminated
|
|
"nqsvc_setangle", // [vec3] set the view angle to this absolute value
|
|
|
|
"nqsvc_serverinfo", // [long] version
|
|
// [string] signon string
|
|
// [string]..[0]model cache [string]...[0]sounds cache
|
|
// [string]..[0]item cache
|
|
"nqsvc_lightstyle", // [qbyte] [string]
|
|
"nqsvc_updatename", // [qbyte] [string]
|
|
"nqsvc_updatefrags", // [qbyte] [short]
|
|
"nqsvc_clientdata", // <shortbits + data>
|
|
"nqsvc_stopsound", // <see code>
|
|
"nqsvc_updatecolors", // [qbyte] [qbyte]
|
|
"nqsvc_particle", // [vec3] <variable>
|
|
"nqsvc_damage", // [qbyte] impact [qbyte] blood [vec3] from
|
|
|
|
"nqsvc_spawnstatic",
|
|
"nqOBSOLETE svc_spawnbinary",
|
|
"nqsvc_spawnbaseline",
|
|
|
|
"nqsvc_temp_entity", // <variable>
|
|
"nqsvc_setpause",
|
|
"nqsvc_signonnum",
|
|
"nqsvc_centerprint",
|
|
"nqsvc_killedmonster",
|
|
"nqsvc_foundsecret",
|
|
"nqsvc_spawnstaticsound",
|
|
"nqsvc_intermission",
|
|
"nqsvc_finale", // [string] music [string] text
|
|
"nqsvc_cdtrack", // [qbyte] track [qbyte] looptrack
|
|
"nqsvc_sellscreen",
|
|
"nqsvc_cutscene",
|
|
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL",
|
|
"NEW PROTOCOL"
|
|
};
|
|
|
|
extern cvar_t requiredownloads, cl_standardchat;
|
|
int oldparsecountmod;
|
|
int parsecountmod;
|
|
double parsecounttime;
|
|
|
|
int cl_spikeindex, cl_playerindex, cl_h_playerindex, cl_flagindex, cl_rocketindex, cl_grenadeindex, cl_gib1index, cl_gib2index, cl_gib3index;
|
|
|
|
#ifdef PEXT_LIGHTUPDATES
|
|
int cl_lightningindex;
|
|
#endif
|
|
|
|
//=============================================================================
|
|
|
|
int packet_latency[NET_TIMINGS];
|
|
|
|
int CL_CalcNet (void)
|
|
{
|
|
int a, i;
|
|
frame_t *frame;
|
|
int lost;
|
|
// char st[80];
|
|
|
|
for (i=cls.netchan.outgoing_sequence-UPDATE_BACKUP+1
|
|
; i <= cls.netchan.outgoing_sequence
|
|
; i++)
|
|
{
|
|
frame = &cl.frames[i&UPDATE_MASK];
|
|
if (frame->receivedtime == -1)
|
|
packet_latency[i&NET_TIMINGSMASK] = 9999; // dropped
|
|
else if (frame->receivedtime == -2)
|
|
packet_latency[i&NET_TIMINGSMASK] = 10000; // choked
|
|
else if (frame->invalid)
|
|
packet_latency[i&NET_TIMINGSMASK] = 9998; // invalid delta
|
|
else
|
|
packet_latency[i&NET_TIMINGSMASK] = (frame->receivedtime - frame->senttime)*20;
|
|
}
|
|
|
|
lost = 0;
|
|
for (a=0 ; a<NET_TIMINGS ; a++)
|
|
{
|
|
i = (cls.netchan.outgoing_sequence-a) & NET_TIMINGSMASK;
|
|
if (packet_latency[i] == 9999)
|
|
lost++;
|
|
}
|
|
return lost * 100 / NET_TIMINGS;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
//note: this will overwrite existing files.
|
|
//returns true if the download is going to be downloaded after the call.
|
|
qboolean CL_EnqueDownload(char *filename, qboolean verbose, qboolean ignorefailedlist)
|
|
{
|
|
downloadlist_t *dl;
|
|
if (strchr(filename, '\\') || strchr(filename, ':') || strstr(filename, ".."))
|
|
{
|
|
Con_Printf("Denying download of \"%s\"\n", filename);
|
|
return false;
|
|
}
|
|
|
|
if (cls.demoplayback)
|
|
return false;
|
|
|
|
if (!ignorefailedlist)
|
|
{
|
|
for (dl = cl.faileddownloads; dl; dl = dl->next) //yeah, so it failed... Ignore it.
|
|
{
|
|
if (!strcmp(dl->name, filename))
|
|
{
|
|
if (verbose)
|
|
Con_Printf("We've failed to download \"%s\" already\n", filename);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (dl = cl.downloadlist; dl; dl = dl->next) //It's already on our list. Ignore it.
|
|
{
|
|
if (!strcmp(dl->name, filename))
|
|
{
|
|
if (verbose)
|
|
Con_Printf("Already waiting for \"%s\"\n", filename);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!strcmp(cls.downloadname, filename))
|
|
{
|
|
if (verbose)
|
|
Con_Printf("Already downloading \"%s\"\n", filename);
|
|
return true;
|
|
}
|
|
|
|
dl = Z_Malloc(sizeof(downloadlist_t));
|
|
strcpy(dl->name, filename);
|
|
dl->next = cl.downloadlist;
|
|
cl.downloadlist = dl;
|
|
|
|
if (verbose)
|
|
Con_Printf("Enqued download of \"%s\"\n", filename);
|
|
|
|
return true;
|
|
}
|
|
|
|
void CL_DisenqueDownload(char *filename)
|
|
{
|
|
downloadlist_t *dl, *nxt;
|
|
if(cl.downloadlist) //remove from enqued download list
|
|
{
|
|
if (!strcmp(cl.downloadlist->name, filename))
|
|
{
|
|
dl = cl.downloadlist;
|
|
cl.downloadlist = cl.downloadlist->next;
|
|
Z_Free(dl);
|
|
}
|
|
else
|
|
{
|
|
for (dl = cl.downloadlist->next; dl->next; dl = dl->next)
|
|
{
|
|
if (!strcmp(dl->next->name, filename))
|
|
{
|
|
nxt = dl->next->next;
|
|
Z_Free(dl->next);
|
|
dl->next = nxt;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CL_SendDownloadRequest(char *filename)
|
|
{
|
|
strcpy (cls.downloadname, filename);
|
|
Con_TPrintf (TL_DOWNLOADINGFILE, cls.downloadname);
|
|
|
|
// download to a temp name, and only rename
|
|
// to the real name when done, so if interrupted
|
|
// a runt file wont be left
|
|
COM_StripExtension (cls.downloadname, cls.downloadtempname);
|
|
strcat (cls.downloadtempname, ".tmp");
|
|
|
|
CL_SendClientCommand("download %s", cls.downloadname);
|
|
|
|
//prevent ftp/http from changing stuff
|
|
cls.downloadmethod = DL_QWPENDING;
|
|
cls.downloadpercent = 0;
|
|
|
|
CL_DisenqueDownload(filename);
|
|
}
|
|
|
|
//Do any reloading for the file that just reloaded.
|
|
void CL_FinishDownload(char *filename, char *tempname)
|
|
{
|
|
int i;
|
|
extern int mod_numknown;
|
|
extern model_t mod_known[];
|
|
|
|
COM_RefreshFSCache_f();
|
|
|
|
cls.downloadmethod = DL_NONE;
|
|
|
|
// rename the temp file to it's final name
|
|
if (tempname)
|
|
{
|
|
char oldn[MAX_OSPATH], newn[MAX_OSPATH];
|
|
if (strcmp(tempname, filename))
|
|
{
|
|
if (strncmp(tempname,"skins/",6))
|
|
{
|
|
sprintf (oldn, "%s/%s", com_gamedir, tempname);
|
|
sprintf (newn, "%s/%s", com_gamedir, filename);
|
|
}
|
|
else
|
|
{
|
|
sprintf (oldn, "qw/%s", tempname);
|
|
sprintf (newn, "qw/%s", filename);
|
|
}
|
|
if (rename (oldn, newn))
|
|
Con_TPrintf (TL_RENAMEFAILED);
|
|
}
|
|
}
|
|
|
|
if (!strcmp(filename, "gfx/palette.lmp"))
|
|
{
|
|
Cbuf_AddText("vid_restart\n", RESTRICT_LOCAL);
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < mod_numknown; i++) //go and load this model now.
|
|
{
|
|
if (!strcmp(mod_known[i].name, filename))
|
|
{
|
|
Mod_ForName(mod_known[i].name, false); //throw away result.
|
|
break;
|
|
}
|
|
}
|
|
Skin_FlushSkin(filename);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_CheckOrDownloadFile
|
|
|
|
Returns true if the file exists, otherwise it attempts
|
|
to start a download from the server.
|
|
===============
|
|
*/
|
|
qboolean CL_CheckOrDownloadFile (char *filename, int nodelay)
|
|
{
|
|
if (strstr (filename, ".."))
|
|
{
|
|
Con_TPrintf (TL_NORELATIVEPATHS);
|
|
return true;
|
|
}
|
|
|
|
if (COM_FCheckExists (filename))
|
|
{ // it exists, no need to download
|
|
return true;
|
|
}
|
|
|
|
//ZOID - can't download when recording
|
|
if (cls.demorecording)
|
|
{
|
|
Con_TPrintf (TL_NODOWNLOADINDEMO, filename);
|
|
return true;
|
|
}
|
|
//ZOID - can't download when playback
|
|
if (cls.demoplayback)
|
|
return true;
|
|
|
|
SCR_EndLoadingPlaque(); //release console.
|
|
|
|
if (CL_EnqueDownload(filename, false, false))
|
|
return !nodelay;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
|
|
qboolean CL_CheckMD2Skins (char *name)
|
|
{
|
|
md2_t *pheader;
|
|
qbyte *precache_model;
|
|
int precache_model_skin = 1;
|
|
|
|
// checking for skins in the model
|
|
precache_model = COM_LoadMallocFile (name);
|
|
if (!precache_model) {
|
|
precache_model_skin = 0;
|
|
return false; // couldn't load it
|
|
}
|
|
if (LittleLong(*(unsigned *)precache_model) != MD2IDALIASHEADER) {
|
|
// not an alias model
|
|
BZ_Free(precache_model);
|
|
precache_model = 0;
|
|
precache_model_skin = 0;
|
|
return false;
|
|
}
|
|
pheader = (md2_t *)precache_model;
|
|
if (LittleLong (pheader->version) != MD2ALIAS_VERSION) {
|
|
BZ_Free(precache_model);
|
|
precache_model = 0;
|
|
precache_model_skin = 0;
|
|
return false;
|
|
}
|
|
|
|
pheader = (md2_t *)precache_model;
|
|
|
|
while (precache_model_skin - 1 < LittleLong(pheader->num_skins)) {
|
|
if (!CL_CheckOrDownloadFile((char *)precache_model +
|
|
LittleLong(pheader->ofs_skins) +
|
|
(precache_model_skin - 1)*MD2MAX_SKINNAME, false)) {
|
|
precache_model_skin++;
|
|
|
|
BZ_Free(precache_model);
|
|
precache_model=NULL;
|
|
return true; // started a download
|
|
}
|
|
precache_model_skin++;
|
|
}
|
|
if (precache_model) {
|
|
BZ_Free(precache_model);
|
|
precache_model=NULL;
|
|
}
|
|
precache_model_skin = 0;
|
|
|
|
return false;
|
|
}
|
|
/*
|
|
=================
|
|
Model_NextDownload
|
|
=================
|
|
*/
|
|
void Sound_NextDownload (void);
|
|
void Model_NextDownload (void)
|
|
{
|
|
// char *twf;
|
|
char *s;
|
|
int i;
|
|
extern char gamedirfile[];
|
|
|
|
if (cls.downloadnumber == 0)
|
|
{
|
|
Con_TPrintf (TLC_CHECKINGMODELS);
|
|
cls.downloadnumber = 1;
|
|
|
|
cl.worldmodel = NULL;
|
|
}
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server)
|
|
{
|
|
R_SetSky(cl.skyname, 0, r_origin);
|
|
for (i = 0; i < Q2MAX_IMAGES; i++)
|
|
{
|
|
char picname[256];
|
|
if (!*cl.image_name[i])
|
|
continue;
|
|
sprintf(picname, "pics/%s.pcx", cl.image_name[i]);
|
|
if (!CL_CheckOrDownloadFile(picname, false))
|
|
return;
|
|
}
|
|
if (!CLQ2_RegisterTEntModels())
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
cls.downloadtype = dl_model;
|
|
|
|
for (
|
|
; cl.model_name[cls.downloadnumber][0]
|
|
; cls.downloadnumber++)
|
|
{
|
|
s = cl.model_name[cls.downloadnumber];
|
|
if (s[0] == '*')
|
|
continue; // inline brush model
|
|
|
|
if (!stricmp(COM_FileExtension(s), "dsp")) //doom sprites are weird, and not really downloadable via this system
|
|
continue;
|
|
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server && s[0] == '#') //this is a vweap
|
|
continue;
|
|
#endif
|
|
|
|
if (!CL_CheckOrDownloadFile(s, cls.downloadnumber==1)) //world is required to be loaded.
|
|
return; // started a download
|
|
|
|
if (strstr(s, ".md2"))
|
|
if (CL_CheckMD2Skins(s))
|
|
return;
|
|
}
|
|
|
|
CL_AllowIndependantSendCmd(false); //stop it now, the indep stuff *could* require model tracing.
|
|
|
|
if (cl.playernum[0] == -1)
|
|
{ //q2 cinematic - don't load the models.
|
|
cl.worldmodel = cl.model_precache[1] = Mod_ForName ("", false);
|
|
}
|
|
else if (!cl.worldmodel)
|
|
{
|
|
for (i=1 ; i<MAX_MODELS ; i++)
|
|
{
|
|
if (!cl.model_name[i][0])
|
|
break;
|
|
|
|
Hunk_Check();
|
|
|
|
cl.model_precache[i] = NULL;
|
|
cl.model_precache[i] = Mod_ForName (cl.model_name[i], false);
|
|
|
|
Hunk_Check();
|
|
|
|
if (!cl.model_precache[i] || (i == 1 && (cl.model_precache[i]->type == mod_dummy || cl.model_precache[i]->needload)))
|
|
{
|
|
Con_TPrintf (TL_FILE_X_MISSING, cl.model_name[i]);
|
|
Con_TPrintf (TL_GETACLIENTPACK, gamedirfile);
|
|
CL_Disconnect ();
|
|
return;
|
|
}
|
|
|
|
S_ExtraUpdate();
|
|
}
|
|
|
|
cl.worldmodel = cl.model_precache[1];
|
|
}
|
|
|
|
if (!R_CheckSky())
|
|
return;
|
|
if (!Wad_NextDownload()) //world is required to be loaded.
|
|
return; // started a download
|
|
|
|
{
|
|
extern model_t *loadmodel;
|
|
loadmodel = cl.worldmodel;
|
|
if (!cl.worldmodel)
|
|
Host_EndGame("No worldmodel was loaded\n");
|
|
|
|
if (R_PreNewMap)
|
|
R_PreNewMap();
|
|
|
|
Mod_NowLoadExternal();
|
|
}
|
|
|
|
// all done
|
|
R_NewMap ();
|
|
Hunk_Check (); // make sure nothing is hurt
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server)
|
|
{
|
|
cls.downloadnumber = 0;
|
|
Skin_NextDownload();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// done with modellist, request first of static signon messages
|
|
// CL_SendClientCommand("prespawn %i 0 %i", cl.servercount, cl.worldmodel->checksum2);
|
|
CL_SendClientCommand(prespawn_name, cl.servercount, cl.worldmodel->checksum2);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Sound_NextDownload
|
|
=================
|
|
*/
|
|
void Sound_NextDownload (void)
|
|
{
|
|
char *s;
|
|
int i;
|
|
|
|
if (cls.downloadnumber == 0)
|
|
{
|
|
Con_TPrintf (TLC_CHECKINGSOUNDS);
|
|
cls.downloadnumber = 1;
|
|
}
|
|
|
|
cls.downloadtype = dl_sound;
|
|
for (
|
|
; cl.sound_name[cls.downloadnumber][0]
|
|
; cls.downloadnumber++)
|
|
{
|
|
s = cl.sound_name[cls.downloadnumber];
|
|
if (*s == '*')
|
|
continue;
|
|
if (!CL_CheckOrDownloadFile(va("sound/%s",s), false))
|
|
return; // started a download
|
|
}
|
|
|
|
for (i=1 ; i<MAX_SOUNDS ; i++)
|
|
{
|
|
if (!cl.sound_name[i][0])
|
|
break;
|
|
cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i]);
|
|
|
|
S_ExtraUpdate();
|
|
}
|
|
|
|
// done with sounds, request models now
|
|
memset (cl.model_precache, 0, sizeof(cl.model_precache));
|
|
cl_playerindex = -1;
|
|
cl_h_playerindex = -1;
|
|
cl_spikeindex = -1;
|
|
cl_flagindex = -1;
|
|
cl_rocketindex = -1;
|
|
cl_grenadeindex = -1;
|
|
cl_gib1index = -1;
|
|
cl_gib2index = -1;
|
|
cl_gib3index = -1;
|
|
#ifdef PEXT_LIGHTUPDATES
|
|
cl_lightningindex = -1;
|
|
#endif
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server)
|
|
{
|
|
cls.downloadnumber = 0;
|
|
Model_NextDownload();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// CL_SendClientCommand ("modellist %i 0", cl.servercount);
|
|
CL_SendClientCommand (modellist_name, cl.servercount, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
======================
|
|
CL_RequestNextDownload
|
|
======================
|
|
*/
|
|
void CL_RequestNextDownload (void)
|
|
{
|
|
if (cl.downloadlist)
|
|
{
|
|
if (!COM_FCheckExists (cl.downloadlist->name))
|
|
CL_SendDownloadRequest(cl.downloadlist->name);
|
|
else
|
|
{
|
|
Con_Printf("Already have %s\n", cl.downloadlist->name);
|
|
CL_DisenqueDownload(cl.downloadlist->name);
|
|
}
|
|
return;
|
|
}
|
|
switch (cls.downloadtype)
|
|
{
|
|
case dl_single:
|
|
case dl_singlestuffed:
|
|
break;
|
|
case dl_skin:
|
|
Skin_NextDownload ();
|
|
break;
|
|
case dl_model:
|
|
Model_NextDownload ();
|
|
break;
|
|
case dl_sound:
|
|
Sound_NextDownload ();
|
|
break;
|
|
case dl_none:
|
|
default:
|
|
Con_DPrintf("Unknown download type.\n");
|
|
}
|
|
}
|
|
|
|
int CL_RequestADownloadChunk(void);
|
|
void CL_SendDownloadReq(sizebuf_t *msg)
|
|
{
|
|
if (cl.downloadlist && !cls.downloadmethod)
|
|
{
|
|
CL_RequestNextDownload();
|
|
return;
|
|
}
|
|
|
|
#ifdef PEXT_CHUNKEDDOWNLOADS
|
|
if (cls.downloadmethod == DL_QWCHUNKS)
|
|
{
|
|
int i = CL_RequestADownloadChunk();
|
|
if (i < 0)
|
|
{
|
|
//we can stop downloading now.
|
|
}
|
|
else
|
|
{
|
|
CL_SendClientCommand("nextdl %i\n", i);
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef PEXT_ZLIBDL
|
|
#ifdef _WIN32
|
|
#define ZEXPORT VARGS
|
|
#include "../../zip/zlib.h"
|
|
|
|
//# pragma comment (lib, "zip/zlib.lib")
|
|
#else
|
|
#include <zlib.h>
|
|
#endif
|
|
|
|
char *ZLibDownloadDecode(int *messagesize, char *input, int finalsize)
|
|
{
|
|
char *outbuf = Hunk_TempAlloc(finalsize);
|
|
z_stream zs;
|
|
|
|
*messagesize = (*(short*)input);
|
|
input+=2;
|
|
|
|
if (!*messagesize)
|
|
{
|
|
*messagesize = finalsize+2;
|
|
return input;
|
|
}
|
|
|
|
memset(&zs, 0, sizeof(zs));
|
|
|
|
|
|
zs.next_in = input;
|
|
zs.avail_in = *messagesize; //tell it that it has a lot. Possibly a bad idea.
|
|
zs.total_in = 0;
|
|
|
|
zs.next_out = outbuf;
|
|
zs.avail_out = finalsize; //this is the limiter.
|
|
zs.total_out = 0;
|
|
|
|
zs.data_type = Z_BINARY;
|
|
|
|
inflateInit(&zs);
|
|
inflate(&zs, Z_FINISH); //decompress it in one go.
|
|
inflateEnd(&zs);
|
|
|
|
*messagesize = zs.total_in+2;
|
|
return outbuf;
|
|
}
|
|
#endif
|
|
|
|
void CL_DownloadFailed(char *name)
|
|
{
|
|
//add this to our failed list. (so we don't try downloading it again...)
|
|
downloadlist_t *failed;
|
|
failed = Z_Malloc(sizeof(downloadlist_t));
|
|
failed->next = cl.faileddownloads;
|
|
cl.faileddownloads = failed;
|
|
Q_strncpyz(failed->name, name, sizeof(failed->name));
|
|
|
|
cls.downloadmethod = DL_NONE;
|
|
}
|
|
|
|
float downloadstarttime;
|
|
#ifdef PEXT_CHUNKEDDOWNLOADS
|
|
#define MAXBLOCKS 64 //must be power of 2
|
|
#define DLBLOCKSIZE 1024
|
|
int downloadsize;
|
|
int receivedbytes;
|
|
int recievedblock[MAXBLOCKS];
|
|
int firstblock;
|
|
int blockcycle;
|
|
void CL_ParseChunkedDownload(void)
|
|
{
|
|
qbyte *svname;
|
|
qbyte osname[MAX_OSPATH];
|
|
int totalsize;
|
|
int chunknum;
|
|
char data[DLBLOCKSIZE];
|
|
|
|
chunknum = MSG_ReadLong();
|
|
if (chunknum < 0)
|
|
{
|
|
totalsize = MSG_ReadLong();
|
|
svname = MSG_ReadString();
|
|
if (cls.demoplayback)
|
|
return;
|
|
|
|
if (totalsize < 0)
|
|
{
|
|
if (totalsize == -2)
|
|
Con_Printf("Server permissions deny downloading file %s\n", svname);
|
|
else
|
|
Con_Printf("Couldn't find file %s on the server\n", svname);
|
|
|
|
CL_DownloadFailed(svname);
|
|
|
|
CL_RequestNextDownload();
|
|
return;
|
|
}
|
|
|
|
if (cls.downloadmethod == DL_QWCHUNKS)
|
|
Host_EndGame("Received second download - \"%s\"\n", svname);
|
|
|
|
//start the new download
|
|
cls.downloadmethod = DL_QWCHUNKS;
|
|
cls.downloadpercent = 0;
|
|
downloadsize = totalsize;
|
|
|
|
downloadstarttime = Sys_DoubleTime();
|
|
|
|
strcpy(cls.downloadname, svname);
|
|
COM_StripExtension(svname, cls.downloadtempname);
|
|
COM_DefaultExtension(cls.downloadtempname, ".tmp");
|
|
|
|
if (!strncmp(cls.downloadtempname,"skins/",6))
|
|
sprintf (osname, "qw/%s", cls.downloadtempname); //skins go to quake/qw/skins. never quake/gamedir/skins (blame id)
|
|
else
|
|
sprintf (osname, "%s/%s", com_gamedir, cls.downloadtempname);
|
|
COM_CreatePath (osname);
|
|
cls.downloadqw = fopen (osname, "wb");
|
|
|
|
firstblock = 0;
|
|
receivedbytes = 0;
|
|
blockcycle = -1; //so it requests 0 first. :)
|
|
memset(recievedblock, 0, sizeof(recievedblock));
|
|
return;
|
|
}
|
|
|
|
// Con_Printf("Received dl block %i: ", chunknum);
|
|
|
|
MSG_ReadData(data, DLBLOCKSIZE);
|
|
|
|
if (cls.demoplayback)
|
|
{ //err, yeah, when playing demos we don't actually pay any attention to this.
|
|
return;
|
|
}
|
|
if (chunknum < firstblock)
|
|
{
|
|
// Con_Printf("too old\n", chunknum);
|
|
return;
|
|
}
|
|
if (chunknum-firstblock >= MAXBLOCKS)
|
|
{
|
|
// Con_Printf("^1too new!\n", chunknum);
|
|
return;
|
|
}
|
|
|
|
if (recievedblock[chunknum&(MAXBLOCKS-1)])
|
|
{
|
|
// Con_Printf("duplicated\n", chunknum);
|
|
return;
|
|
}
|
|
// Con_Printf("usable\n", chunknum);
|
|
receivedbytes+=DLBLOCKSIZE;
|
|
recievedblock[chunknum&(MAXBLOCKS-1)] = true;
|
|
|
|
while(recievedblock[firstblock&(MAXBLOCKS-1)])
|
|
{
|
|
recievedblock[firstblock&(MAXBLOCKS-1)] = false;
|
|
firstblock++;
|
|
}
|
|
|
|
fseek(cls.downloadqw, chunknum*DLBLOCKSIZE, SEEK_SET);
|
|
if (downloadsize - chunknum*DLBLOCKSIZE < DLBLOCKSIZE) //final block is actually meant to be smaller than we recieve.
|
|
fwrite(data, 1, downloadsize - chunknum*DLBLOCKSIZE, cls.downloadqw);
|
|
else
|
|
fwrite(data, 1, DLBLOCKSIZE, cls.downloadqw);
|
|
|
|
cls.downloadpercent = receivedbytes/(float)downloadsize*100;
|
|
}
|
|
|
|
int CL_RequestADownloadChunk(void)
|
|
{
|
|
int i;
|
|
int b;
|
|
|
|
if (cls.downloadmethod != DL_QWCHUNKS)
|
|
{
|
|
Con_Printf("download not initiated\n");
|
|
return 0;
|
|
}
|
|
|
|
blockcycle++;
|
|
for (i = 0; i < MAXBLOCKS; i++)
|
|
{
|
|
b = ((i+blockcycle)&(MAXBLOCKS-1))
|
|
+ firstblock;
|
|
if (!recievedblock[b&(MAXBLOCKS-1)]) //don't ask for ones we've already got.
|
|
{
|
|
if (b >= (downloadsize+DLBLOCKSIZE-1)/DLBLOCKSIZE) //don't ask for blocks that are over the size of the file.
|
|
continue;
|
|
// Con_Printf("Requesting block %i\n", b);
|
|
return b;
|
|
}
|
|
}
|
|
|
|
// Con_Printf("^1 EOF?\n");
|
|
|
|
fclose(cls.downloadqw);
|
|
CL_FinishDownload(cls.downloadname, cls.downloadtempname);
|
|
|
|
Con_Printf("Download took %i seconds\n", (int)(Sys_DoubleTime() - downloadstarttime));
|
|
|
|
*cls.downloadname = '\0';
|
|
cls.downloadqw = NULL;
|
|
cls.downloadpercent = 0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
=====================
|
|
CL_ParseDownload
|
|
|
|
A download message has been received from the server
|
|
=====================
|
|
*/
|
|
void CL_ParseDownload (void)
|
|
{
|
|
int size, percent;
|
|
qbyte name[1024];
|
|
|
|
#ifdef PEXT_CHUNKEDDOWNLOADS
|
|
if (cls.fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS)
|
|
{
|
|
CL_ParseChunkedDownload();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// read the data
|
|
size = MSG_ReadShort ();
|
|
percent = MSG_ReadByte ();
|
|
|
|
if (cls.demoplayback)
|
|
{
|
|
if (size > 0)
|
|
msg_readcount += size;
|
|
return; // not in demo playback
|
|
}
|
|
|
|
if (!*cls.downloadname) //huh... that's not right...
|
|
{
|
|
Con_Print("^1Warning: Server sending unknown file.\n");
|
|
strcpy(cls.downloadname, "unknown.txt");
|
|
strcpy(cls.downloadtempname, "unknown.tmp");
|
|
}
|
|
|
|
if (size < 0)
|
|
{
|
|
Con_TPrintf (TL_FILENOTFOUND);
|
|
if (cls.downloadqw)
|
|
{
|
|
Con_TPrintf (TL_CLS_DOWNLOAD_ISSET);
|
|
fclose (cls.downloadqw);
|
|
cls.downloadqw = NULL;
|
|
}
|
|
if (cl.downloadlist && !strcmp(cl.downloadlist->name, cls.downloadname))
|
|
{
|
|
downloadlist_t *next;
|
|
next = cl.downloadlist->next;
|
|
Z_Free(cl.downloadlist);
|
|
cl.downloadlist = next;
|
|
}
|
|
|
|
CL_DownloadFailed(cls.downloadname);
|
|
|
|
CL_RequestNextDownload ();
|
|
return;
|
|
}
|
|
|
|
// open the file if not opened yet
|
|
if (!cls.downloadqw)
|
|
{
|
|
if (strncmp(cls.downloadtempname,"skins/",6))
|
|
sprintf (name, "%s/%s", com_gamedir, cls.downloadtempname);
|
|
else
|
|
sprintf (name, "qw/%s", cls.downloadtempname);
|
|
|
|
COM_CreatePath (name);
|
|
|
|
cls.downloadqw = fopen (name, "wb");
|
|
if (!cls.downloadqw)
|
|
{
|
|
msg_readcount += size;
|
|
Con_TPrintf (TL_FAILEDTOOPEN, cls.downloadtempname);
|
|
CL_RequestNextDownload ();
|
|
return;
|
|
}
|
|
|
|
downloadstarttime = Sys_DoubleTime();
|
|
SCR_EndLoadingPlaque();
|
|
}
|
|
#ifdef PEXT_ZLIBDL
|
|
if (percent >= 101 && percent <= 201)// && cls.fteprotocolextensions & PEXT_ZLIBDL)
|
|
{
|
|
int compsize;
|
|
|
|
percent = percent - 101;
|
|
|
|
fwrite (ZLibDownloadDecode(&compsize, net_message.data + msg_readcount, size), 1, size, cls.download);
|
|
|
|
msg_readcount += compsize;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
fwrite (net_message.data + msg_readcount, 1, size, cls.downloadqw);
|
|
msg_readcount += size;
|
|
}
|
|
|
|
if (cls.downloadmethod == DL_QWPENDING)
|
|
cls.downloadmethod = DL_QW;
|
|
|
|
if (percent != 100)
|
|
{
|
|
// change display routines by zoid
|
|
// request next block
|
|
cls.downloadpercent = percent;
|
|
|
|
CL_SendClientCommand("nextdl");
|
|
}
|
|
else
|
|
{
|
|
fclose (cls.downloadqw);
|
|
|
|
CL_FinishDownload(cls.downloadname, cls.downloadtempname);
|
|
*cls.downloadname = '\0';
|
|
cls.downloadqw = NULL;
|
|
cls.downloadpercent = 0;
|
|
|
|
Con_Printf("Download took %i seconds\n", (int)(Sys_DoubleTime() - downloadstarttime));
|
|
|
|
// get another file if needed
|
|
|
|
CL_RequestNextDownload ();
|
|
}
|
|
}
|
|
|
|
static qbyte *upload_data;
|
|
static int upload_pos;
|
|
static int upload_size;
|
|
|
|
void CL_NextUpload(void)
|
|
{
|
|
qbyte buffer[1024];
|
|
int r;
|
|
int percent;
|
|
int size;
|
|
|
|
if (!upload_data)
|
|
return;
|
|
|
|
r = upload_size - upload_pos;
|
|
if (r > 768)
|
|
r = 768;
|
|
memcpy(buffer, upload_data + upload_pos, r);
|
|
MSG_WriteByte (&cls.netchan.message, clc_upload);
|
|
MSG_WriteShort (&cls.netchan.message, r);
|
|
|
|
upload_pos += r;
|
|
size = upload_size;
|
|
if (!size)
|
|
size = 1;
|
|
percent = upload_pos*100/size;
|
|
MSG_WriteByte (&cls.netchan.message, percent);
|
|
SZ_Write (&cls.netchan.message, buffer, r);
|
|
|
|
Con_DPrintf ("UPLOAD: %6d: %d written\n", upload_pos - r, r);
|
|
|
|
if (upload_pos != upload_size)
|
|
return;
|
|
|
|
Con_TPrintf (TL_UPLOADCOMPLEATE);
|
|
|
|
BZ_Free(upload_data);
|
|
upload_data = 0;
|
|
upload_pos = upload_size = 0;
|
|
}
|
|
|
|
void CL_StartUpload (qbyte *data, int size)
|
|
{
|
|
if (cls.state < ca_onserver)
|
|
return; // gotta be connected
|
|
|
|
// override
|
|
if (upload_data)
|
|
BZ_Free(upload_data);
|
|
|
|
Con_DPrintf("Upload starting of %d...\n", size);
|
|
|
|
upload_data = BZ_Malloc(size);
|
|
memcpy(upload_data, data, size);
|
|
upload_size = size;
|
|
upload_pos = 0;
|
|
|
|
CL_NextUpload();
|
|
}
|
|
|
|
qboolean CL_IsUploading(void)
|
|
{
|
|
if (upload_data)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void CL_StopUpload(void)
|
|
{
|
|
if (upload_data)
|
|
BZ_Free(upload_data);
|
|
upload_data = NULL;
|
|
}
|
|
|
|
/*
|
|
=====================================================================
|
|
|
|
SERVER CONNECTING MESSAGES
|
|
|
|
=====================================================================
|
|
*/
|
|
#ifdef CLIENTONLY
|
|
float nextdemotime;
|
|
#endif
|
|
|
|
/*
|
|
==================
|
|
CL_ParseServerData
|
|
==================
|
|
*/
|
|
void CL_ParseServerData (void)
|
|
{
|
|
int pnum;
|
|
int clnum;
|
|
char *str;
|
|
int protover, svcnt;
|
|
|
|
float maxspeed, entgrav;
|
|
|
|
Con_DPrintf ("Serverdata packet received.\n");
|
|
//
|
|
// wipe the client_state_t struct
|
|
//
|
|
|
|
SCR_BeginLoadingPlaque();
|
|
|
|
// parse protocol version number
|
|
// allow 2.2 and 2.29 demos to play
|
|
#ifdef PROTOCOL_VERSION_FTE
|
|
cls.fteprotocolextensions=0;
|
|
for(;;)
|
|
{
|
|
protover = MSG_ReadLong ();
|
|
if (protover == PROTOCOL_VERSION_FTE)
|
|
{
|
|
cls.fteprotocolextensions = MSG_ReadLong();
|
|
Con_TPrintf (TL_FTEEXTENSIONS, cls.fteprotocolextensions);
|
|
continue;
|
|
}
|
|
if (protover == PROTOCOL_VERSION) //this ends the version info
|
|
break;
|
|
if (cls.demoplayback && (protover == 26 || protover == 27 || protover == 28)) //older versions, maintain demo compatability.
|
|
break;
|
|
Host_EndGame ("Server returned version %i, not %i\nYou probably need to upgrade.\nCheck http://www.quakeworld.net/", protover, PROTOCOL_VERSION);
|
|
}
|
|
#else
|
|
protover = MSG_ReadLong ();
|
|
if (protover != PROTOCOL_VERSION &&
|
|
!(cls.demoplayback && (protover == 26 || protover == 27 || protover == 28)))
|
|
Host_EndGame ("Server returned version %i, not %i\nYou probably need to upgrade.\nCheck http://www.quakeworld.net/", protover, PROTOCOL_VERSION);
|
|
#endif
|
|
|
|
if (cls.fteprotocolextensions & PEXT_FLOATCOORDS)
|
|
{
|
|
sizeofcoord = 4;
|
|
sizeofangle = 1;
|
|
}
|
|
else
|
|
{
|
|
sizeofcoord = 2;
|
|
sizeofangle = 1;
|
|
}
|
|
|
|
svcnt = MSG_ReadLong ();
|
|
|
|
// game directory
|
|
str = MSG_ReadString ();
|
|
|
|
#ifndef CLIENTONLY
|
|
if (!sv.state)
|
|
#endif
|
|
{
|
|
COM_FlushTempoaryPacks();
|
|
COM_Gamedir(str);
|
|
#ifndef CLIENTONLY
|
|
Info_SetValueForStarKey (svs.info, "*gamedir", str, MAX_SERVERINFO_STRING);
|
|
#endif
|
|
COM_FlushFSCache();
|
|
}
|
|
|
|
CL_ClearState ();
|
|
P_NewServer();
|
|
Stats_NewMap();
|
|
cl.servercount = svcnt;
|
|
|
|
cl.teamfortress = !Q_strcasecmp(str, "fortress");
|
|
|
|
if (cl.gamedirchanged)
|
|
{
|
|
cl.gamedirchanged = false;
|
|
#ifndef CLIENTONLY
|
|
if (!sv.state)
|
|
#endif
|
|
Wads_Flush();
|
|
|
|
T_FreeStrings();
|
|
}
|
|
|
|
if (cls.demoplayback == DPB_MVD)
|
|
{
|
|
int i;
|
|
extern float nextdemotime;
|
|
cls.netchan.last_received = nextdemotime = /*olddemotime =*/ MSG_ReadFloat();
|
|
cl.playernum[0] = MAX_CLIENTS - 1;
|
|
cl.spectator = true;
|
|
for (i = 0; i < UPDATE_BACKUP; i++)
|
|
cl.frames[i].playerstate[cl.playernum[0]].pm_type = PM_SPECTATOR;
|
|
|
|
cl.splitclients = 1;
|
|
}
|
|
else
|
|
{
|
|
// parse player slot, high bit means spectator
|
|
pnum = MSG_ReadByte ();
|
|
for (clnum = 0; ; clnum++)
|
|
{
|
|
cl.playernum[clnum] = pnum;
|
|
if (cl.playernum[clnum] & 128)
|
|
{
|
|
cl.spectator = true;
|
|
cl.playernum[clnum] &= ~128;
|
|
}
|
|
|
|
if (!(cls.fteprotocolextensions & PEXT_SPLITSCREEN))
|
|
break;
|
|
|
|
pnum = MSG_ReadByte ();
|
|
|
|
if (pnum == 128)
|
|
break;
|
|
|
|
if (clnum == MAX_SPLITS)
|
|
Host_EndGame("Server sent us too many alternate clients\n");
|
|
}
|
|
cl.splitclients = clnum+1;
|
|
}
|
|
|
|
// get the full level name
|
|
str = MSG_ReadString ();
|
|
Q_strncpyz (cl.levelname, str, sizeof(cl.levelname));
|
|
|
|
// get the movevars
|
|
movevars.gravity = MSG_ReadFloat();
|
|
movevars.stopspeed = MSG_ReadFloat();
|
|
maxspeed = MSG_ReadFloat();
|
|
movevars.spectatormaxspeed = MSG_ReadFloat();
|
|
movevars.accelerate = MSG_ReadFloat();
|
|
movevars.airaccelerate = MSG_ReadFloat();
|
|
movevars.wateraccelerate = MSG_ReadFloat();
|
|
movevars.friction = MSG_ReadFloat();
|
|
movevars.waterfriction = MSG_ReadFloat();
|
|
entgrav = MSG_ReadFloat();
|
|
|
|
for (clnum = 0; clnum < cl.splitclients; clnum++)
|
|
{
|
|
cl.maxspeed[clnum] = maxspeed;
|
|
cl.entgravity[clnum] = entgrav;
|
|
}
|
|
|
|
// seperate the printfs so the server message can have a color
|
|
Con_TPrintf (TLC_LINEBREAK_NEWLEVEL);
|
|
Con_TPrintf (TLC_PC_PS_NL, 2, str);
|
|
|
|
memset(cl.sound_name, 0, sizeof(cl.sound_name));
|
|
#ifdef PEXT_PK3DOWNLOADS
|
|
if (cls.fteprotocolextensions & PEXT_PK3DOWNLOADS) //instead of going for a soundlist, go for the pk3 list instead. The server will make us go for the soundlist after.
|
|
{
|
|
CL_SendClientCommand ("pk3list %i 0", cl.servercount, 0);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// ask for the sound list next
|
|
// CL_SendClientCommand ("soundlist %i 0", cl.servercount);
|
|
CL_SendClientCommand (soundlist_name, cl.servercount, 0);
|
|
}
|
|
|
|
// now waiting for downloads, etc
|
|
cls.state = ca_onserver;
|
|
|
|
#ifdef VM_CG
|
|
CG_Stop();
|
|
#endif
|
|
#ifdef CSQC_DAT
|
|
CSQC_Shutdown(); //revive it when we get the serverinfo saying the checksum.
|
|
#endif
|
|
}
|
|
|
|
void CLQ2_ParseServerData (void)
|
|
{
|
|
char *str;
|
|
int i;
|
|
int svcnt;
|
|
// int cflag;
|
|
|
|
sizeofcoord = 2;
|
|
sizeofangle = 1;
|
|
|
|
Con_DPrintf ("Serverdata packet received.\n");
|
|
//
|
|
// wipe the client_state_t struct
|
|
//
|
|
SCR_BeginLoadingPlaque();
|
|
// CL_ClearState ();
|
|
cls.state = ca_onserver;
|
|
|
|
// parse protocol version number
|
|
i = MSG_ReadLong ();
|
|
// cls.serverProtocol = i;
|
|
|
|
if (i > PROTOCOL_VERSION_Q2 || i < PROTOCOL_VERSION_Q2_MIN)
|
|
Host_EndGame ("Server returned version %i, not %i", i, PROTOCOL_VERSION_Q2);
|
|
|
|
svcnt = MSG_ReadLong ();
|
|
/*cl.attractloop =*/ MSG_ReadByte ();
|
|
|
|
// game directory
|
|
str = MSG_ReadString ();
|
|
// strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1);
|
|
|
|
// set gamedir
|
|
if (!*str)
|
|
COM_Gamedir("baseq2");
|
|
else
|
|
COM_Gamedir(str);
|
|
COM_FlushFSCache();
|
|
// if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string)))
|
|
// Cvar_Set("game", str);
|
|
|
|
CL_ClearState ();
|
|
cl.minpitch = -89;
|
|
cl.maxpitch = 89;
|
|
cl.servercount = svcnt;
|
|
|
|
Stats_NewMap();
|
|
|
|
|
|
// parse player entity number
|
|
cl.playernum[0] = MSG_ReadShort ();
|
|
cl.splitclients = 1;
|
|
cl.spectator = false;
|
|
|
|
// get the full level name
|
|
str = MSG_ReadString ();
|
|
Q_strncpyz (cl.levelname, str, sizeof(cl.levelname));
|
|
|
|
if (cl.playernum[0] == -1)
|
|
{ // playing a cinematic or showing a pic, not a level
|
|
SCR_EndLoadingPlaque();
|
|
if (!Media_PlayFilm(str))
|
|
Con_TPrintf (TLC_NOQ2CINEMATICSSUPPORT, cl.servercount);
|
|
else
|
|
cls.state = ca_active;
|
|
}
|
|
else
|
|
{
|
|
// seperate the printfs so the server message can have a color
|
|
Con_TPrintf (TLC_LINEBREAK_NEWLEVEL);
|
|
Con_TPrintf (TLC_PC_PS_NL, 2, str);
|
|
|
|
Media_PlayFilm("");
|
|
|
|
// need to prep refresh at next oportunity
|
|
//cl.refresh_prepped = false;
|
|
}
|
|
|
|
|
|
if (R_PreNewMap)
|
|
R_PreNewMap();
|
|
}
|
|
|
|
|
|
|
|
#ifdef NQPROT
|
|
//FIXME: move to header
|
|
#define NQ_PROTOCOL_VERSION 15
|
|
void CL_KeepaliveMessage(void){}
|
|
void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution.
|
|
{
|
|
int nummodels, numsounds, i;
|
|
char *str;
|
|
int protover;
|
|
if (developer.value)
|
|
Con_TPrintf (TLC_GOTSVDATAPACKET);
|
|
CL_ClearState ();
|
|
Stats_NewMap();
|
|
|
|
protover = MSG_ReadLong ();
|
|
|
|
sizeofcoord = 2;
|
|
sizeofangle = 1;
|
|
|
|
nq_dp_protocol = 0;
|
|
cls.z_ext = 0;
|
|
|
|
if (protover == 250)
|
|
Host_EndGame ("Nehahra demo net protocol is not supported\n");
|
|
else if (protover == 3502)
|
|
{
|
|
//darkplaces5
|
|
nq_dp_protocol = 5;
|
|
sizeofcoord = 4;
|
|
sizeofangle = 2;
|
|
}
|
|
else if (protover == 3503)
|
|
{
|
|
//darkplaces6 (it's a small difference from dp5)
|
|
nq_dp_protocol = 6;
|
|
sizeofcoord = 4;
|
|
sizeofangle = 2;
|
|
|
|
cls.z_ext = Z_EXT_VIEWHEIGHT;
|
|
}
|
|
else if (protover != NQ_PROTOCOL_VERSION)
|
|
{
|
|
Host_EndGame ("Server returned version %i, not %i\nYou will need to use a different client.", protover, NQ_PROTOCOL_VERSION);
|
|
}
|
|
|
|
if (MSG_ReadByte() > MAX_SCOREBOARD)
|
|
{
|
|
Con_TPrintf (TLC_BAD_MAXCLIENTS);
|
|
return;
|
|
}
|
|
|
|
cl.splitclients = 1;
|
|
|
|
/*cl.gametype =*/ MSG_ReadByte ();
|
|
|
|
str = MSG_ReadString ();
|
|
Q_strncpyz (cl.levelname, str, sizeof(cl.levelname));
|
|
|
|
// seperate the printfs so the server message can have a color
|
|
Con_TPrintf (TLC_LINEBREAK_NEWLEVEL);
|
|
Con_TPrintf (TLC_PC_PS_NL, 2, str);
|
|
|
|
SCR_BeginLoadingPlaque();
|
|
|
|
if (R_PreNewMap)
|
|
R_PreNewMap();
|
|
|
|
memset (cl.model_name, 0, sizeof(cl.model_name));
|
|
for (nummodels=1 ; ; nummodels++)
|
|
{
|
|
str = MSG_ReadString ();
|
|
if (!str[0])
|
|
break;
|
|
if (nummodels==MAX_MODELS)
|
|
{
|
|
Con_TPrintf (TLC_TOOMANYMODELPRECACHES);
|
|
return;
|
|
}
|
|
strcpy (cl.model_name[nummodels], str);
|
|
Mod_TouchModel (str);
|
|
|
|
// cl.model_precache[nummodels] = Mod_ForName (cl.model_name[nummodels], false);
|
|
}
|
|
|
|
memset (cl.sound_name, 0, sizeof(cl.sound_name));
|
|
for (numsounds=1 ; ; numsounds++)
|
|
{
|
|
str = MSG_ReadString ();
|
|
if (!str[0])
|
|
break;
|
|
if (numsounds==MAX_SOUNDS)
|
|
{
|
|
Con_TPrintf (TLC_TOOMANYSOUNDPRECACHES);
|
|
return;
|
|
}
|
|
strcpy (cl.sound_name[numsounds], str);
|
|
S_TouchSound (str);
|
|
|
|
// cl.sound_precache[numsounds] = S_PrecacheSound (cl.sound_name[numsounds]);
|
|
}
|
|
|
|
//
|
|
// now we try to load everything else until a cache allocation fails
|
|
//
|
|
|
|
for (i=1 ; i<nummodels ; i++)
|
|
{
|
|
cl.model_precache[i] = Mod_ForName (cl.model_name[i], i==1);
|
|
// if (!ignorenonprecached.value || i == 1) //need world
|
|
{
|
|
if (cl.model_precache[i] == NULL)
|
|
{
|
|
Host_EndGame("Model %s not found\n", cl.model_name[i]);
|
|
}
|
|
}
|
|
CL_KeepaliveMessage ();
|
|
}
|
|
|
|
S_BeginPrecaching ();
|
|
for (i=1 ; i<numsounds ; i++)
|
|
{
|
|
cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i]);
|
|
CL_KeepaliveMessage ();
|
|
}
|
|
S_EndPrecaching ();
|
|
|
|
cl.worldmodel = cl.model_precache[1];
|
|
|
|
|
|
R_NewMap ();
|
|
|
|
SCR_EndLoadingPlaque();
|
|
|
|
Hunk_Check (); // make sure nothing is hurt
|
|
|
|
cls.state = ca_onserver;
|
|
}
|
|
void CLNQ_SignonReply (void)
|
|
{
|
|
extern cvar_t topcolor;
|
|
extern cvar_t bottomcolor;
|
|
|
|
Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
|
|
|
|
switch (cls.signon)
|
|
{
|
|
case 1:
|
|
CL_SendClientCommand("prespawn");
|
|
break;
|
|
|
|
case 2:
|
|
CL_SendClientCommand("name \"%s\"\n", name.string);
|
|
name.modified = false;
|
|
|
|
CL_SendClientCommand("color %i %i\n", (int)topcolor.value, (int)bottomcolor.value);
|
|
|
|
CL_SendClientCommand("spawn %s", "");
|
|
break;
|
|
|
|
case 3:
|
|
CL_SendClientCommand("begin");
|
|
Cache_Report (); // print remaining memory
|
|
#ifdef VM_CG
|
|
CG_Start();
|
|
#endif
|
|
break;
|
|
|
|
case 4:
|
|
SCR_EndLoadingPlaque (); // allow normal screen updates
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define SU_VIEWHEIGHT (1<<0)
|
|
#define SU_IDEALPITCH (1<<1)
|
|
#define SU_PUNCH1 (1<<2)
|
|
#define SU_PUNCH2 (1<<3)
|
|
#define SU_PUNCH3 (1<<4)
|
|
#define SU_VELOCITY1 (1<<5)
|
|
#define SU_VELOCITY2 (1<<6)
|
|
#define SU_VELOCITY3 (1<<7)
|
|
//define SU_AIMENT (1<<8) AVAILABLE BIT
|
|
#define SU_ITEMS (1<<9)
|
|
#define SU_ONGROUND (1<<10) // no data follows, the bit is it
|
|
#define SU_INWATER (1<<11) // no data follows, the bit is it
|
|
#define SU_WEAPONFRAME (1<<12)
|
|
#define SU_ARMOR (1<<13)
|
|
#define SU_WEAPON (1<<14)
|
|
|
|
#define DPSU_EXTEND1 (1<<15)
|
|
// first extend byte
|
|
#define DPSU_PUNCHVEC1 (1<<16)
|
|
#define DPSU_PUNCHVEC2 (1<<17)
|
|
#define DPSU_PUNCHVEC3 (1<<18)
|
|
#define DPSU_VIEWZOOM (1<<19) // byte factor (0 = 0.0 (not valid), 255 = 1.0)
|
|
#define DPSU_UNUSED20 (1<<20)
|
|
#define DPSU_UNUSED21 (1<<21)
|
|
#define DPSU_UNUSED22 (1<<22)
|
|
#define DPSU_EXTEND2 (1<<23) // another byte to follow, future expansion
|
|
// second extend byte
|
|
#define DPSU_UNUSED24 (1<<24)
|
|
#define DPSU_UNUSED25 (1<<25)
|
|
#define DPSU_UNUSED26 (1<<26)
|
|
#define DPSU_UNUSED27 (1<<27)
|
|
#define DPSU_UNUSED28 (1<<28)
|
|
#define DPSU_UNUSED29 (1<<29)
|
|
#define DPSU_UNUSED30 (1<<30)
|
|
#define DPSU_EXTEND3 (1<<31) // another byte to follow, future expansion
|
|
|
|
|
|
#define DEFAULT_VIEWHEIGHT 22
|
|
void CLNQ_ParseClientdata (int bits)
|
|
{
|
|
int i;
|
|
|
|
bits &= 0xffff;
|
|
|
|
if (bits & DPSU_EXTEND1)
|
|
bits |= (MSG_ReadByte() << 16);
|
|
if (bits & DPSU_EXTEND2)
|
|
bits |= (MSG_ReadByte() << 24);
|
|
|
|
if (bits & SU_VIEWHEIGHT)
|
|
CL_SetStat(0, STAT_VIEWHEIGHT, MSG_ReadChar ());
|
|
else if (nq_dp_protocol != 6)
|
|
CL_SetStat(0, STAT_VIEWHEIGHT, DEFAULT_VIEWHEIGHT);
|
|
|
|
if (bits & SU_IDEALPITCH)
|
|
/*cl.idealpitch =*/ MSG_ReadChar ();
|
|
/*else
|
|
cl.idealpitch = 0;*/
|
|
|
|
// VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
if (bits & (SU_PUNCH1<<i) )
|
|
/*cl.punchangle[i] =*/ nq_dp_protocol?MSG_ReadAngle16():MSG_ReadChar();
|
|
// else
|
|
// cl.punchangle[i] = 0;
|
|
|
|
if (bits & (DPSU_PUNCHVEC1<<i))
|
|
{
|
|
/*cl.punchvector[i] =*/ MSG_ReadCoord();
|
|
}
|
|
// else
|
|
// cl.punchvector[i] = 0;
|
|
|
|
if (bits & (SU_VELOCITY1<<i) )
|
|
{
|
|
if (nq_dp_protocol == 5 || nq_dp_protocol == 6)
|
|
/*cl.simvel[0][i] =*/ MSG_ReadFloat();
|
|
else
|
|
/*cl.mvelocity[0][i] =*/ MSG_ReadChar()/**16*/;
|
|
}
|
|
// else
|
|
// cl.mvelocity[0][i] = 0;
|
|
}
|
|
|
|
if (bits & SU_ITEMS)
|
|
CL_SetStat(0, STAT_ITEMS, MSG_ReadLong());
|
|
|
|
// cl.onground = (bits & SU_ONGROUND) != 0;
|
|
// cl.inwater = (bits & SU_INWATER) != 0;
|
|
|
|
if (nq_dp_protocol == 6)
|
|
{
|
|
}
|
|
else if (nq_dp_protocol == 5)
|
|
{
|
|
CL_SetStat(0, STAT_WEAPONFRAME, (bits & SU_WEAPONFRAME)?(unsigned short)MSG_ReadShort():0);
|
|
CL_SetStat(0, STAT_ARMOR, (bits & SU_ARMOR)?MSG_ReadShort():0);
|
|
CL_SetStat(0, STAT_WEAPON, (bits & SU_WEAPON)?MSG_ReadShort():0);
|
|
|
|
CL_SetStat(0, STAT_HEALTH, MSG_ReadShort());
|
|
|
|
CL_SetStat(0, STAT_AMMO, MSG_ReadShort());
|
|
|
|
CL_SetStat(0, STAT_SHELLS, MSG_ReadShort());
|
|
CL_SetStat(0, STAT_NAILS, MSG_ReadShort());
|
|
CL_SetStat(0, STAT_ROCKETS, MSG_ReadShort());
|
|
CL_SetStat(0, STAT_CELLS, MSG_ReadShort());
|
|
|
|
CL_SetStat(0, STAT_ACTIVEWEAPON, (unsigned short)MSG_ReadShort());
|
|
}
|
|
else
|
|
{
|
|
CL_SetStat(0, STAT_WEAPONFRAME, (bits & SU_WEAPONFRAME)?(unsigned char)MSG_ReadByte():0);
|
|
CL_SetStat(0, STAT_ARMOR, (bits & SU_ARMOR)?MSG_ReadByte():0);
|
|
CL_SetStat(0, STAT_WEAPON, (bits & SU_WEAPON)?MSG_ReadByte():0);
|
|
|
|
CL_SetStat(0, STAT_HEALTH, MSG_ReadShort());
|
|
|
|
CL_SetStat(0, STAT_AMMO, MSG_ReadByte());
|
|
|
|
CL_SetStat(0, STAT_SHELLS, MSG_ReadByte());
|
|
CL_SetStat(0, STAT_NAILS, MSG_ReadByte());
|
|
CL_SetStat(0, STAT_ROCKETS, MSG_ReadByte());
|
|
CL_SetStat(0, STAT_CELLS, MSG_ReadByte());
|
|
|
|
CL_SetStat(0, STAT_ACTIVEWEAPON, MSG_ReadByte());
|
|
}
|
|
|
|
if (bits & DPSU_VIEWZOOM)
|
|
{
|
|
if (nq_dp_protocol == 5 || nq_dp_protocol == 6)
|
|
i = (unsigned short) MSG_ReadShort();
|
|
else
|
|
i = MSG_ReadByte();
|
|
if (i < 2)
|
|
i = 2;
|
|
CL_SetStat(0, STAT_VIEWZOOM, i);
|
|
}
|
|
else if (nq_dp_protocol != 6)
|
|
CL_SetStat(0, STAT_VIEWZOOM, 255);
|
|
}
|
|
#endif
|
|
/*
|
|
==================
|
|
CL_ParseSoundlist
|
|
==================
|
|
*/
|
|
void CL_ParseSoundlist (void)
|
|
{
|
|
int numsounds;
|
|
char *str;
|
|
int n;
|
|
|
|
// precache sounds
|
|
// memset (cl.sound_precache, 0, sizeof(cl.sound_precache));
|
|
|
|
numsounds = MSG_ReadByte();
|
|
|
|
for (;;)
|
|
{
|
|
str = MSG_ReadString ();
|
|
if (!str[0])
|
|
break;
|
|
numsounds++;
|
|
if (numsounds == MAX_SOUNDS)
|
|
Host_EndGame ("Server sent too many sound_precache");
|
|
|
|
// if (strlen(str)>4)
|
|
// if (!strcmp(str+strlen(str)-4, ".mp3")) //don't let the server send us a specific mp3. convert it to wav and this way we know not to look outside the quake path for it.
|
|
// strcpy(str+strlen(str)-4, ".wav");
|
|
|
|
strcpy (cl.sound_name[numsounds], str);
|
|
}
|
|
|
|
n = MSG_ReadByte();
|
|
|
|
if (n)
|
|
{
|
|
// CL_SendClientCommand("soundlist %i %i", cl.servercount, n);
|
|
CL_SendClientCommand(soundlist_name, cl.servercount, n);
|
|
return;
|
|
}
|
|
|
|
cls.downloadnumber = 0;
|
|
cls.downloadtype = dl_sound;
|
|
Sound_NextDownload ();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_ParseModellist
|
|
==================
|
|
*/
|
|
void CL_ParseModellist (qboolean lots)
|
|
{
|
|
int nummodels;
|
|
char *str;
|
|
int n;
|
|
|
|
// precache models and note certain default indexes
|
|
if (lots)
|
|
nummodels = MSG_ReadShort();
|
|
else
|
|
nummodels = MSG_ReadByte();
|
|
|
|
for (;;)
|
|
{
|
|
str = MSG_ReadString ();
|
|
if (!str[0])
|
|
break;
|
|
nummodels++;
|
|
if (nummodels==MAX_MODELS)
|
|
Host_EndGame ("Server sent too many model_precache");
|
|
strcpy (cl.model_name[nummodels], str);
|
|
|
|
if (!strcmp(cl.model_name[nummodels],"progs/spike.mdl"))
|
|
cl_spikeindex = nummodels;
|
|
#ifdef PEXT_LIGHTUPDATES
|
|
if (!strcmp(cl.model_name[nummodels], "progs/zap.mdl"))
|
|
cl_lightningindex = nummodels;
|
|
#endif
|
|
if (!strcmp(cl.model_name[nummodels],"progs/player.mdl"))
|
|
cl_playerindex = nummodels;
|
|
if (!strcmp(cl.model_name[nummodels],"progs/h_player.mdl"))
|
|
cl_h_playerindex = nummodels;
|
|
if (!strcmp(cl.model_name[nummodels],"progs/flag.mdl"))
|
|
cl_flagindex = nummodels;
|
|
|
|
if (!strcmp(cl.model_name[nummodels],"progs/missile.mdl"))
|
|
cl_rocketindex = nummodels;
|
|
if (!strcmp(cl.model_name[nummodels],"progs/grenade.mdl"))
|
|
cl_grenadeindex = nummodels;
|
|
|
|
|
|
if (!strcmp(cl.model_name[nummodels],"progs/gib1.mdl"))
|
|
cl_gib1index = nummodels;
|
|
if (!strcmp(cl.model_name[nummodels],"progs/gib2.mdl"))
|
|
cl_gib2index = nummodels;
|
|
if (!strcmp(cl.model_name[nummodels],"progs/gib3.mdl"))
|
|
cl_gib3index = nummodels;
|
|
}
|
|
|
|
if (nummodels)
|
|
SCR_ImageName(cl.model_name[1]);
|
|
|
|
n = MSG_ReadByte();
|
|
|
|
if (n)
|
|
{
|
|
// CL_SendClientCommand("modellist %i %i", cl.servercount, n);
|
|
CL_SendClientCommand(modellist_name, cl.servercount, (nummodels&0xff00) + n);
|
|
return;
|
|
}
|
|
|
|
cls.downloadnumber = 0;
|
|
cls.downloadtype = dl_model;
|
|
Model_NextDownload ();
|
|
}
|
|
|
|
void CL_ProcessUserInfo (int slot, player_info_t *player);
|
|
void CLQ2_ParseClientinfo(int i, char *s)
|
|
{
|
|
char *skin, *model, *name;
|
|
player_info_t *player;
|
|
//s contains "name\model/skin"
|
|
|
|
player = &cl.players[i];
|
|
|
|
*player->userinfo = '\0';
|
|
|
|
model = strchr(s, '\\');
|
|
if (model)
|
|
{
|
|
*model = '\0';
|
|
model++;
|
|
name = s;
|
|
}
|
|
else
|
|
{
|
|
name = "Unnammed";
|
|
model = "male";
|
|
}
|
|
|
|
skin = strchr(model, '/');
|
|
if (skin)
|
|
{
|
|
*skin = '\0';
|
|
skin++;
|
|
}
|
|
else
|
|
skin = "";
|
|
Info_SetValueForKey(player->userinfo, "name", name, MAX_INFO_STRING);
|
|
Info_SetValueForKey(player->userinfo, "model", model, MAX_INFO_STRING);
|
|
Info_SetValueForKey(player->userinfo, "skin", skin, MAX_INFO_STRING);
|
|
CL_ProcessUserInfo (i, player);
|
|
}
|
|
|
|
void CLQ2_ParseConfigString (void)
|
|
{
|
|
int i;
|
|
char *s;
|
|
// char olds[MAX_QPATH];
|
|
|
|
i = MSG_ReadShort ();
|
|
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;
|
|
|
|
// strcpy (cl.configstrings[i], s);
|
|
|
|
// do something apropriate
|
|
|
|
if (i == Q2CS_SKY)
|
|
{
|
|
Q_strncpyz (cl.skyname, s, sizeof(cl.skyname));
|
|
}
|
|
else if (i == Q2CS_STATUSBAR)
|
|
{
|
|
Q_strncpyz(cl.q2statusbar, s, sizeof(cl.q2statusbar));
|
|
}
|
|
else if (i >= Q2CS_LIGHTS && i < Q2CS_LIGHTS+Q2MAX_LIGHTSTYLES)
|
|
{
|
|
#ifdef PEXT_LIGHTSTYLECOL
|
|
cl_lightstyle[i - Q2CS_LIGHTS].colour = 7; //white
|
|
#endif
|
|
Q_strncpyz (cl_lightstyle[i - Q2CS_LIGHTS].map, s, sizeof(cl_lightstyle[i-Q2CS_LIGHTS].map));
|
|
cl_lightstyle[i - Q2CS_LIGHTS].length = Q_strlen(cl_lightstyle[i - Q2CS_LIGHTS].map);
|
|
|
|
}
|
|
else if (i == Q2CS_CDTRACK)
|
|
{
|
|
// if (cl.refresh_prepped)
|
|
CDAudio_Play (atoi(s), true);
|
|
}
|
|
else if (i >= Q2CS_MODELS && i < Q2CS_MODELS+Q2MAX_MODELS)
|
|
{
|
|
// if (cl.refresh_prepped)
|
|
{
|
|
Q_strncpyz(cl.model_name[i-Q2CS_MODELS], s, MAX_QPATH);
|
|
cl.model_precache[i-Q2CS_MODELS] = Mod_ForName (cl.model_name[i-Q2CS_MODELS], false);
|
|
}
|
|
}
|
|
else if (i >= Q2CS_SOUNDS && i < Q2CS_SOUNDS+Q2MAX_MODELS)
|
|
{
|
|
// if (cl.refresh_prepped)
|
|
Q_strncpyz(cl.sound_name[i-Q2CS_SOUNDS], s, MAX_QPATH);
|
|
cl.sound_precache[i-Q2CS_SOUNDS] = S_PrecacheSound (s);
|
|
}
|
|
else if (i >= Q2CS_IMAGES && i < Q2CS_IMAGES+Q2MAX_MODELS)
|
|
{ //ignore
|
|
Q_strncpyz(cl.image_name[i-Q2CS_IMAGES], s, MAX_QPATH);
|
|
}
|
|
else if (i >= Q2CS_PLAYERSKINS && i < Q2CS_PLAYERSKINS+Q2MAX_CLIENTS)
|
|
{
|
|
// if (cl.refresh_prepped && strcmp(olds, s))
|
|
CLQ2_ParseClientinfo (i-Q2CS_PLAYERSKINS, s);
|
|
}
|
|
|
|
UI_StringChanged(i);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
==================
|
|
CL_ParseBaseline
|
|
==================
|
|
*/
|
|
void CL_ParseBaseline (entity_state_t *es)
|
|
{
|
|
int i;
|
|
|
|
memset(es, 0, sizeof(entity_state_t));
|
|
|
|
es->modelindex = MSG_ReadByte ();
|
|
es->frame = MSG_ReadByte ();
|
|
es->colormap = MSG_ReadByte();
|
|
es->skinnum = MSG_ReadByte();
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
es->origin[i] = MSG_ReadCoord ();
|
|
es->angles[i] = MSG_ReadAngle ();
|
|
}
|
|
#ifdef PEXT_SCALE
|
|
es->scale = 1;
|
|
#endif
|
|
#ifdef PEXT_TRANS
|
|
es->trans = 1;
|
|
#endif
|
|
}
|
|
void CL_ParseBaseline2 (void)
|
|
{
|
|
entity_state_t nullst, es;
|
|
|
|
memset(&nullst, 0, sizeof(entity_state_t));
|
|
memset(&es, 0, sizeof(entity_state_t));
|
|
|
|
CL_ParseDelta(&nullst, &es, MSG_ReadShort(), true);
|
|
memcpy(&cl_baselines[es.number], &es, sizeof(es));
|
|
}
|
|
|
|
void CLQ2_Precache_f (void)
|
|
{
|
|
#ifdef VM_CG
|
|
CG_Start();
|
|
#endif
|
|
|
|
cls.downloadnumber = 0;
|
|
cls.downloadtype = dl_sound;
|
|
|
|
CL_RequestNextDownload();
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=====================
|
|
CL_ParseStatic
|
|
|
|
Static entities are non-interactive world objects
|
|
like torches
|
|
=====================
|
|
*/
|
|
void CL_ParseStatic (int version)
|
|
{
|
|
entity_t *ent;
|
|
int i;
|
|
entity_state_t es, nullstate;
|
|
|
|
if (version == 1)
|
|
{
|
|
CL_ParseBaseline (&es);
|
|
i = cl.num_statics;
|
|
cl.num_statics++;
|
|
}
|
|
else
|
|
{
|
|
memset(&nullstate, 0, sizeof(nullstate));
|
|
CL_ParseDelta(&nullstate, &es, MSG_ReadShort(), true);
|
|
es.number+=MAX_EDICTS;
|
|
|
|
for (i = 0; i < cl.num_statics; i++)
|
|
if (cl_static_entities[i].keynum == es.number)
|
|
{
|
|
R_RemoveEfrags (&cl_static_entities[i]);
|
|
break;
|
|
}
|
|
|
|
if (i == cl.num_statics)
|
|
cl.num_statics++;
|
|
}
|
|
|
|
if (i >= MAX_STATIC_ENTITIES)
|
|
{
|
|
cl.num_statics--;
|
|
Con_Printf ("Too many static entities");
|
|
return;
|
|
}
|
|
ent = &cl_static_entities[i];
|
|
memset(ent, 0, sizeof(*ent));
|
|
|
|
ent->keynum = es.number;
|
|
|
|
// copy it to the current state
|
|
ent->model = cl.model_precache[es.modelindex];
|
|
ent->oldframe = ent->frame = es.frame;
|
|
ent->colormap = vid.colormap;
|
|
ent->skinnum = es.skinnum;
|
|
ent->drawflags = es.drawflags;
|
|
|
|
#ifdef PEXT_SCALE
|
|
ent->scale = es.scale;
|
|
#endif
|
|
#ifdef PEXT_TRANS
|
|
ent->alpha = es.trans;
|
|
#endif
|
|
ent->fatness = es.fatness;
|
|
ent->abslight = es.abslight;
|
|
|
|
VectorCopy (es.origin, ent->origin);
|
|
VectorCopy (es.angles, ent->angles);
|
|
es.angles[0]*=-1;
|
|
AngleVectors(es.angles, ent->axis[0], ent->axis[1], ent->axis[2]);
|
|
VectorInverse(ent->axis[1]);
|
|
|
|
if (!cl.worldmodel)
|
|
{
|
|
Con_TPrintf (TLC_PARSESTATICWITHNOMAP);
|
|
return;
|
|
}
|
|
|
|
R_AddEfrags (ent);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
CL_ParseStaticSound
|
|
===================
|
|
*/
|
|
void CL_ParseStaticSound (void)
|
|
{
|
|
extern cvar_t cl_staticsounds;
|
|
vec3_t org;
|
|
int sound_num, vol, atten;
|
|
int i;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
org[i] = MSG_ReadCoord ();
|
|
sound_num = MSG_ReadByte ();
|
|
vol = MSG_ReadByte ();
|
|
atten = MSG_ReadByte ();
|
|
|
|
if (!cl_staticsounds.value)
|
|
return;
|
|
|
|
S_StaticSound (cl.sound_precache[sound_num], org, vol, atten);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=====================================================================
|
|
|
|
ACTION MESSAGES
|
|
|
|
=====================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
CL_ParseStartSoundPacket
|
|
==================
|
|
*/
|
|
void CL_ParseStartSoundPacket(void)
|
|
{
|
|
vec3_t pos;
|
|
int channel, ent;
|
|
int sound_num;
|
|
int volume;
|
|
float attenuation;
|
|
int i;
|
|
|
|
channel = MSG_ReadShort();
|
|
|
|
if (channel & SND_VOLUME)
|
|
volume = MSG_ReadByte ();
|
|
else
|
|
volume = DEFAULT_SOUND_PACKET_VOLUME;
|
|
|
|
if (channel & SND_ATTENUATION)
|
|
attenuation = MSG_ReadByte () / 64.0;
|
|
else
|
|
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
|
|
|
|
sound_num = MSG_ReadByte ();
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
pos[i] = MSG_ReadCoord ();
|
|
|
|
ent = (channel>>3)&1023;
|
|
channel &= 7;
|
|
|
|
if (ent > MAX_EDICTS)
|
|
Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent);
|
|
|
|
S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation);
|
|
|
|
|
|
TP_CheckPickupSound(cl.sound_name[sound_num], pos);
|
|
}
|
|
|
|
#ifdef Q2CLIENT
|
|
void CLQ2_ParseStartSoundPacket(void)
|
|
{
|
|
vec3_t pos_v;
|
|
float *pos;
|
|
int channel, ent;
|
|
int sound_num;
|
|
float volume;
|
|
float attenuation;
|
|
int flags;
|
|
float ofs;
|
|
|
|
flags = MSG_ReadByte ();
|
|
sound_num = MSG_ReadByte ();
|
|
|
|
if (flags & Q2SND_VOLUME)
|
|
volume = MSG_ReadByte () / 255.0;
|
|
else
|
|
volume = Q2DEFAULT_SOUND_PACKET_VOLUME;
|
|
|
|
if (flags & Q2SND_ATTENUATION)
|
|
attenuation = MSG_ReadByte () / 64.0;
|
|
else
|
|
attenuation = Q2DEFAULT_SOUND_PACKET_ATTENUATION;
|
|
|
|
if (flags & Q2SND_OFFSET)
|
|
ofs = MSG_ReadByte () / 1000.0;
|
|
else
|
|
ofs = 0;
|
|
|
|
if (flags & Q2SND_ENT)
|
|
{ // entity reletive
|
|
channel = MSG_ReadShort();
|
|
ent = channel>>3;
|
|
if (ent > MAX_EDICTS)
|
|
Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent);
|
|
|
|
channel &= 7;
|
|
}
|
|
else
|
|
{
|
|
ent = 0;
|
|
channel = 0;
|
|
}
|
|
|
|
if (flags & Q2SND_POS)
|
|
{ // positioned in space
|
|
MSG_ReadPos (pos_v);
|
|
|
|
pos = pos_v;
|
|
}
|
|
else // use entity number
|
|
{
|
|
CL_GetNumberedEntityInfo(ent, pos_v, NULL);
|
|
pos = pos_v;
|
|
// pos = NULL;
|
|
}
|
|
|
|
if (!cl.sound_precache[sound_num])
|
|
return;
|
|
|
|
S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume, attenuation);
|
|
}
|
|
#endif
|
|
|
|
#ifdef NQPROT
|
|
#define NQSND_VOLUME (1<<0) // a qbyte
|
|
#define NQSND_ATTENUATION (1<<1) // a qbyte
|
|
void CLNQ_ParseStartSoundPacket(void)
|
|
{
|
|
vec3_t pos;
|
|
int channel, ent;
|
|
int sound_num;
|
|
int volume;
|
|
int field_mask;
|
|
float attenuation;
|
|
int i;
|
|
|
|
field_mask = MSG_ReadByte();
|
|
|
|
if (field_mask & NQSND_VOLUME)
|
|
volume = MSG_ReadByte ();
|
|
else
|
|
volume = DEFAULT_SOUND_PACKET_VOLUME;
|
|
|
|
if (field_mask & NQSND_ATTENUATION)
|
|
attenuation = MSG_ReadByte () / 64.0;
|
|
else
|
|
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
|
|
|
|
channel = MSG_ReadShort ();
|
|
sound_num = MSG_ReadByte ();
|
|
|
|
ent = channel >> 3;
|
|
channel &= 7;
|
|
|
|
if (ent > MAX_EDICTS)
|
|
Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent);
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
pos[i] = MSG_ReadCoord ();
|
|
|
|
S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
==================
|
|
CL_ParseClientdata
|
|
|
|
Server information pertaining to this client only, sent every frame
|
|
==================
|
|
*/
|
|
void CL_ParseClientdata (void)
|
|
{
|
|
int i;
|
|
float latency;
|
|
frame_t *frame;
|
|
|
|
// calculate simulated time of message
|
|
oldparsecountmod = parsecountmod;
|
|
|
|
i = cls.netchan.incoming_acknowledged;
|
|
if (cls.demoplayback == DPB_MVD)
|
|
cl.oldparsecount = i - 1;
|
|
cl.parsecount = i;
|
|
i &= UPDATE_MASK;
|
|
parsecountmod = i;
|
|
frame = &cl.frames[i];
|
|
if (cls.demoplayback == DPB_MVD)
|
|
frame->senttime = realtime - host_frametime;
|
|
parsecounttime = cl.frames[i].senttime;
|
|
|
|
frame->receivedtime = realtime;
|
|
|
|
// calculate latency
|
|
latency = frame->receivedtime - frame->senttime;
|
|
|
|
if (latency < 0 || latency > 1.0)
|
|
{
|
|
// Con_Printf ("Odd latency: %5.2f\n", latency);
|
|
}
|
|
else
|
|
{
|
|
// drift the average latency towards the observed latency
|
|
if (latency < cls.latency)
|
|
cls.latency = latency;
|
|
else
|
|
cls.latency += 0.001; // drift up, so correction are needed
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CL_NewTranslation
|
|
=====================
|
|
*/
|
|
void CL_NewTranslation (int slot)
|
|
{
|
|
#ifdef SWQUAKE
|
|
int i, j;
|
|
int top, bottom;
|
|
qbyte *dest, *source;
|
|
#endif
|
|
|
|
char *s;
|
|
player_info_t *player;
|
|
|
|
if (slot >= MAX_CLIENTS)
|
|
Sys_Error ("CL_NewTranslation: slot > MAX_CLIENTS");
|
|
|
|
player = &cl.players[slot];
|
|
|
|
s = Skin_FindName (player);
|
|
COM_StripExtension(s, s);
|
|
if (player->skin && !stricmp(s, player->skin->name))
|
|
player->skin = NULL;
|
|
|
|
|
|
#ifdef RGLQUAKE
|
|
if (qrenderer == QR_OPENGL)
|
|
{ //gl doesn't need to do anything except prevent the sys_error below.
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef SWQUAKE
|
|
if (qrenderer == QR_SOFTWARE)
|
|
{
|
|
top = player->topcolor;
|
|
bottom = player->bottomcolor;
|
|
if (!cl.splitclients && !(cl.fpd & FPD_NO_FORCE_COLOR)) //no colour/skin forcing in splitscreen.
|
|
{
|
|
if (cl.teamplay && !strcmp(player->team, cl.players[cl.playernum[0]].team))
|
|
{
|
|
if (cl_teamtopcolor>=0)
|
|
top = cl_teamtopcolor;
|
|
if (cl_teambottomcolor>=0)
|
|
bottom = cl_teambottomcolor;
|
|
}
|
|
else
|
|
{
|
|
if (cl_enemytopcolor>=0)
|
|
top = cl_enemytopcolor;
|
|
if (cl_enemybottomcolor>=0)
|
|
bottom = cl_enemybottomcolor;
|
|
}
|
|
}
|
|
|
|
|
|
if (player->_topcolor != top ||
|
|
player->_bottomcolor != bottom || !player->skin) {
|
|
player->_topcolor = top;
|
|
player->_bottomcolor = bottom;
|
|
|
|
dest = player->translations;
|
|
source = vid.colormap;
|
|
memcpy (dest, vid.colormap, sizeof(player->translations));
|
|
// top = player->topcolor;
|
|
if (top > 13 || top < 0)
|
|
top = 13;
|
|
top *= 16;
|
|
// bottom = player->bottomcolor;
|
|
if (bottom > 13 || bottom < 0)
|
|
bottom = 13;
|
|
bottom *= 16;
|
|
|
|
for (i=0 ; i<VID_GRADES ; i++, dest += 256, source+=256)
|
|
{
|
|
if (top < 128) // the artists made some backwards ranges. sigh.
|
|
memcpy (dest + TOP_RANGE, source + top, 16);
|
|
else
|
|
for (j=0 ; j<16 ; j++)
|
|
dest[TOP_RANGE+j] = source[top+15-j];
|
|
|
|
if (bottom < 128)
|
|
memcpy (dest + BOTTOM_RANGE, source + bottom, 16);
|
|
else
|
|
for (j=0 ; j<16 ; j++)
|
|
dest[BOTTOM_RANGE+j] = source[bottom+15-j];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
Sys_Error("Bad rendering method in CL_NewTranslation");
|
|
}
|
|
|
|
/*
|
|
==============
|
|
CL_UpdateUserinfo
|
|
==============
|
|
*/
|
|
void CL_ProcessUserInfo (int slot, player_info_t *player)
|
|
{
|
|
Q_strncpyz (player->name, Info_ValueForKey (player->userinfo, "name"), sizeof(player->name));
|
|
Q_strncpyz (player->team, Info_ValueForKey (player->userinfo, "team"), sizeof(player->team));
|
|
player->topcolor = atoi(Info_ValueForKey (player->userinfo, "topcolor"));
|
|
player->bottomcolor = atoi(Info_ValueForKey (player->userinfo, "bottomcolor"));
|
|
if (atoi(Info_ValueForKey (player->userinfo, "*spectator")))
|
|
player->spectator = true;
|
|
else
|
|
player->spectator = false;
|
|
|
|
if (slot == cl.playernum[0] && player->name[0])
|
|
cl.spectator = player->spectator;
|
|
|
|
player->model = NULL;
|
|
|
|
if (cls.state == ca_active)
|
|
Skin_Find (player);
|
|
|
|
Sbar_Changed ();
|
|
CL_NewTranslation (slot);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
CL_UpdateUserinfo
|
|
==============
|
|
*/
|
|
void CL_UpdateUserinfo (void)
|
|
{
|
|
int slot;
|
|
player_info_t *player;
|
|
|
|
slot = MSG_ReadByte ();
|
|
if (slot >= MAX_CLIENTS)
|
|
Host_EndGame ("CL_ParseServerMessage: svc_updateuserinfo > MAX_SCOREBOARD");
|
|
|
|
player = &cl.players[slot];
|
|
player->userid = MSG_ReadLong ();
|
|
Q_strncpyz (player->userinfo, MSG_ReadString(), sizeof(player->userinfo));
|
|
|
|
CL_ProcessUserInfo (slot, player);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
CL_SetInfo
|
|
==============
|
|
*/
|
|
void CL_SetInfo (void)
|
|
{
|
|
int slot;
|
|
player_info_t *player;
|
|
char key[MAX_QWMSGLEN];
|
|
char value[MAX_QWMSGLEN];
|
|
|
|
slot = MSG_ReadByte ();
|
|
if (slot >= MAX_CLIENTS)
|
|
Host_EndGame ("CL_ParseServerMessage: svc_setinfo > MAX_SCOREBOARD");
|
|
|
|
player = &cl.players[slot];
|
|
|
|
Q_strncpyz (key, MSG_ReadString(), sizeof(key));
|
|
Q_strncpyz (value, MSG_ReadString(), sizeof(value));
|
|
|
|
Con_DPrintf("SETINFO %s: %s=%s\n", player->name, key, value);
|
|
|
|
Info_SetValueForStarKey (player->userinfo, key, value, sizeof(player->userinfo));
|
|
|
|
CL_ProcessUserInfo (slot, player);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
CL_ServerInfo
|
|
==============
|
|
*/
|
|
void CL_ServerInfo (void)
|
|
{
|
|
// int slot;
|
|
// player_info_t *player;
|
|
char key[MAX_QWMSGLEN];
|
|
char value[MAX_QWMSGLEN];
|
|
|
|
Q_strncpyz (key, MSG_ReadString(), sizeof(key));
|
|
Q_strncpyz (value, MSG_ReadString(), sizeof(value));
|
|
|
|
Con_DPrintf("SERVERINFO: %s=%s\n", key, value);
|
|
|
|
Info_SetValueForKey (cl.serverinfo, key, value, MAX_SERVERINFO_STRING);
|
|
|
|
CL_CheckServerInfo();
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CL_SetStat
|
|
=====================
|
|
*/
|
|
void CL_SetStat (int pnum, int stat, int value)
|
|
{
|
|
int j;
|
|
if (stat < 0 || stat >= MAX_CL_STATS)
|
|
return;
|
|
// Host_EndGame ("CL_SetStat: %i is invalid", stat);
|
|
|
|
if (cls.demoplayback == DPB_MVD)
|
|
{
|
|
extern int cls_lastto;
|
|
cl.players[cls_lastto].stats[stat]=value;
|
|
if ( spec_track[pnum] != cls_lastto )
|
|
return;
|
|
}
|
|
|
|
if (cl.stats[pnum][stat] != value)
|
|
Sbar_Changed ();
|
|
|
|
if (stat == STAT_ITEMS)
|
|
{ // set flash times
|
|
for (j=0 ; j<32 ; j++)
|
|
if ( (value & (1<<j)) && !(cl.stats[pnum][stat] & (1<<j)))
|
|
cl.item_gettime[pnum][j] = cl.time;
|
|
}
|
|
|
|
if (stat == STAT_VIEWHEIGHT && cls.z_ext & Z_EXT_VIEWHEIGHT)
|
|
cl.viewheight[pnum] = value;
|
|
|
|
if (stat == STAT_TIME && cls.z_ext & Z_EXT_SERVERTIME)
|
|
{
|
|
// cl.servertime_works = true;
|
|
cl.gametime = value * 0.001;
|
|
cl.gametimemark = realtime;
|
|
}
|
|
|
|
cl.stats[pnum][stat] = value;
|
|
|
|
if (pnum == 0)
|
|
TP_StatChanged(stat, value);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
CL_MuzzleFlash
|
|
==============
|
|
*/
|
|
void CL_MuzzleFlash (void)
|
|
{
|
|
vec3_t fv, rv, uv;
|
|
dlight_t *dl=NULL;
|
|
int i;
|
|
player_state_t *pl;
|
|
|
|
packet_entities_t *pack;
|
|
entity_state_t *s1;
|
|
int pnum;
|
|
|
|
extern cvar_t cl_muzzleflash;
|
|
|
|
i = MSG_ReadShort ();
|
|
|
|
//was it us?
|
|
if (i == cl.playernum[0])
|
|
{
|
|
if (!cl_muzzleflash.value)
|
|
return;
|
|
}
|
|
|
|
pack = &cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities;
|
|
|
|
|
|
for (pnum=0 ; pnum<pack->num_entities ; pnum++) //try looking for an entity with that id first
|
|
{
|
|
s1 = &pack->entities[pnum];
|
|
|
|
if (s1->number == i)
|
|
{
|
|
dl = CL_AllocDlight (i);
|
|
VectorCopy (s1->origin, dl->origin);
|
|
break;
|
|
}
|
|
}
|
|
if (pnum==pack->num_entities)
|
|
{ //that ent number doesn't exist, go for a player with that number
|
|
if ((unsigned)(i) <= MAX_CLIENTS && i > 0)
|
|
{
|
|
// don't draw our own muzzle flash in gl if flashblending
|
|
if (i-1 == cl.playernum[0] && r_flashblend.value)
|
|
return;
|
|
|
|
pl = &cl.frames[parsecountmod].playerstate[i-1];
|
|
|
|
dl = CL_AllocDlight (i);
|
|
VectorCopy (pl->origin, dl->origin); //set it's origin
|
|
|
|
AngleVectors (pl->viewangles, fv, rv, uv); //shift it up a little
|
|
VectorMA (dl->origin, 18, fv, dl->origin);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
dl->radius = 200 + (rand()&31);
|
|
dl->minlight = 32;
|
|
dl->die = cl.time + 0.1334;
|
|
dl->color[0] = 0.2;
|
|
dl->color[1] = 0.1;
|
|
dl->color[2] = 0.05;
|
|
|
|
dl->channelfade[0] = 1.5;
|
|
dl->channelfade[1] = 0.75;
|
|
dl->channelfade[2] = 0.375;
|
|
}
|
|
|
|
#ifdef Q2CLIENT
|
|
void Q2S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float timeofs);
|
|
void CLQ2_ParseMuzzleFlash (void)
|
|
{
|
|
vec3_t fv, rv, dummy;
|
|
dlight_t *dl;
|
|
int i, weapon;
|
|
vec3_t org, ang;
|
|
int silenced;
|
|
float volume;
|
|
char soundname[64];
|
|
|
|
i = MSG_ReadShort ();
|
|
if (i < 1 || i >= Q2MAX_EDICTS)
|
|
Host_Error ("CL_ParseMuzzleFlash: bad entity");
|
|
|
|
weapon = MSG_ReadByte ();
|
|
silenced = weapon & Q2MZ_SILENCED;
|
|
weapon &= ~Q2MZ_SILENCED;
|
|
|
|
CL_GetNumberedEntityInfo(i, org, ang);
|
|
|
|
dl = CL_AllocDlight (i);
|
|
VectorCopy (org, dl->origin);
|
|
AngleVectors (ang, fv, rv, dummy);
|
|
VectorMA (dl->origin, 18, fv, dl->origin);
|
|
VectorMA (dl->origin, 16, rv, dl->origin);
|
|
if (silenced)
|
|
dl->radius = 100 + (rand()&31);
|
|
else
|
|
dl->radius = 200 + (rand()&31);
|
|
dl->minlight = 32;
|
|
dl->die = cl.time+0.05; //+ 0.1;
|
|
dl->decay = 1;
|
|
|
|
dl->channelfade[0] = 2;
|
|
dl->channelfade[1] = 2;
|
|
dl->channelfade[2] = 2;
|
|
|
|
if (silenced)
|
|
volume = 0.2;
|
|
else
|
|
volume = 1;
|
|
|
|
|
|
switch (weapon)
|
|
{
|
|
case Q2MZ_BLASTER:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_BLUEHYPERBLASTER:
|
|
dl->color[0] = 0;dl->color[1] = 0;dl->color[2] = 0.2;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_HYPERBLASTER:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_MACHINEGUN:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
_snprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ_SHOTGUN:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/shotgf1b.wav"), volume, ATTN_NORM, 0);
|
|
Q2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound("weapons/shotgr1b.wav"), volume, ATTN_NORM, 0.1);
|
|
break;
|
|
case Q2MZ_SSHOTGUN:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/sshotf1b.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_CHAINGUN1:
|
|
dl->radius = 200 + (rand()&31);
|
|
dl->color[0] = 0.2;dl->color[1] = 0.05;dl->color[2] = 0;
|
|
_snprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_CHAINGUN2:
|
|
dl->radius = 225 + (rand()&31);
|
|
dl->color[0] = 0.2;dl->color[1] = 0.1;dl->color[2] = 0;
|
|
dl->die = cl.time + 0.1; // long delay
|
|
_snprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);
|
|
_snprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0.05);
|
|
break;
|
|
case Q2MZ_CHAINGUN3:
|
|
dl->radius = 250 + (rand()&31);
|
|
dl->color[0] = 0.2;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
dl->die = cl.time + 0.1; // long delay
|
|
_snprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);
|
|
_snprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0.033);
|
|
_snprintf(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0.066);
|
|
break;
|
|
|
|
case Q2MZ_RAILGUN:
|
|
dl->color[0] = 0.1;dl->color[1] = 0.1;dl->color[2] = 0.2;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/railgf1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_ROCKET:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.1;dl->color[2] = 0.04;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/rocklf1a.wav"), volume, ATTN_NORM, 0);
|
|
Q2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound("weapons/rocklr1b.wav"), volume, ATTN_NORM, 0.1);
|
|
break;
|
|
case Q2MZ_GRENADE:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/grenlf1a.wav"), volume, ATTN_NORM, 0);
|
|
Q2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound("weapons/grenlr1b.wav"), volume, ATTN_NORM, 0.1);
|
|
break;
|
|
case Q2MZ_BFG:
|
|
dl->color[0] = 0;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/bfg__f1y.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ_LOGIN:
|
|
dl->color[0] = 0;dl->color[1] = 0.2; dl->color[2] = 0;
|
|
dl->die = cl.time + 1.0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0);
|
|
// CL_LogoutEffect (pl->current.origin, weapon);
|
|
break;
|
|
case Q2MZ_LOGOUT:
|
|
dl->color[0] = 0.2;dl->color[1] = 0; dl->color[2] = 0;
|
|
dl->die = cl.time + 1.0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0);
|
|
// CL_LogoutEffect (pl->current.origin, weapon);
|
|
break;
|
|
case Q2MZ_RESPAWN:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.2; dl->color[2] = 0;
|
|
dl->die = cl.time + 1.0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0);
|
|
// CL_LogoutEffect (pl->current.origin, weapon);
|
|
break;
|
|
// RAFAEL
|
|
case Q2MZ_PHALANX:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.1; dl->color[2] = 0.1;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/plasshot.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
// RAFAEL
|
|
case Q2MZ_IONRIPPER:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.1; dl->color[2] = 0.1;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/rippfire.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
|
|
// ======================
|
|
// PGM
|
|
case Q2MZ_ETF_RIFLE:
|
|
dl->color[0] = 0.18;dl->color[1] = 0.14;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/nail1.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_SHOTGUN2:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/shotg2.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_HEATBEAM:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
dl->die = cl.time + 100;
|
|
// Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/bfg__l1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_BLASTER2:
|
|
dl->color[0] = 0;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
// FIXME - different sound for blaster2 ??
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_TRACKER:
|
|
// negative flashes handled the same in gl/soft until CL_AddDLights
|
|
dl->color[0] = -0.2;dl->color[1] = -0.2;dl->color[2] = -0.2;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/disint2.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_NUKE1:
|
|
dl->color[0] = 0.2;dl->color[1] = 0;dl->color[2] = 0;
|
|
dl->die = cl.time + 100;
|
|
break;
|
|
case Q2MZ_NUKE2:
|
|
dl->color[0] = 0.2;dl->color[1] = 0.2;dl->color[2] = 0;
|
|
dl->die = cl.time + 100;
|
|
break;
|
|
case Q2MZ_NUKE4:
|
|
dl->color[0] = 0;dl->color[1] = 0;dl->color[2] = 0.2;
|
|
dl->die = cl.time + 100;
|
|
break;
|
|
case Q2MZ_NUKE8:
|
|
dl->color[0] = 0;dl->color[1] = 0.2;dl->color[2] = 0.2;
|
|
dl->die = cl.time + 100;
|
|
break;
|
|
// PGM
|
|
// ======================
|
|
}
|
|
}
|
|
|
|
void CLQ2_ParseMuzzleFlash2 (void)
|
|
{
|
|
int ent;
|
|
// vec3_t origin;
|
|
int flash_number;
|
|
// dlight_t *dl;
|
|
// vec3_t forward, right;
|
|
// char soundname[64];
|
|
|
|
ent = MSG_ReadShort ();
|
|
if (ent < 1 || ent >= Q2MAX_EDICTS)
|
|
Host_EndGame ("CL_ParseMuzzleFlash2: bad entity");
|
|
|
|
flash_number = MSG_ReadByte ();
|
|
/*
|
|
// locate the origin
|
|
AngleVectors (cl_entities[ent].current.angles, forward, right, NULL);
|
|
origin[0] = cl_entities[ent].current.origin[0] + forward[0] * monster_flash_offset[flash_number][0] + right[0] * monster_flash_offset[flash_number][1];
|
|
origin[1] = cl_entities[ent].current.origin[1] + forward[1] * monster_flash_offset[flash_number][0] + right[1] * monster_flash_offset[flash_number][1];
|
|
origin[2] = cl_entities[ent].current.origin[2] + forward[2] * monster_flash_offset[flash_number][0] + right[2] * monster_flash_offset[flash_number][1] + monster_flash_offset[flash_number][2];
|
|
|
|
dl = CL_AllocDlight (ent);
|
|
VectorCopy (origin, dl->origin);
|
|
dl->radius = 200 + (rand()&31);
|
|
dl->minlight = 32;
|
|
dl->die = cl.time; // + 0.1;
|
|
|
|
switch (flash_number)
|
|
{
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_1:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_2:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_3:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_4:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_5:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_6:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_7:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_8:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_9:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_10:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_11:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_12:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_13:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
CL_ParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_1:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_2:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_3:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_4:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_5:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_6:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_7:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_8:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
CL_ParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck3.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_GUNNER_MACHINEGUN_1:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_2:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_3:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_4:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_5:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_6:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_7:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_8:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
CL_ParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("gunner/gunatck2.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_ACTOR_MACHINEGUN_1:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_1:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_2:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_3:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_4:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_5:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_6:
|
|
case Q2MZ2_TURRET_MACHINEGUN: // PGM
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
|
|
CL_ParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_BOSS2_MACHINEGUN_L1:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_L2:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_L3:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_L4:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_L5:
|
|
case Q2MZ2_CARRIER_MACHINEGUN_L1: // PMM
|
|
case Q2MZ2_CARRIER_MACHINEGUN_L2: // PMM
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
|
|
CL_ParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NONE, 0);
|
|
break;
|
|
|
|
case Q2MZ2_SOLDIER_BLASTER_1:
|
|
case Q2MZ2_SOLDIER_BLASTER_2:
|
|
case Q2MZ2_SOLDIER_BLASTER_3:
|
|
case Q2MZ2_SOLDIER_BLASTER_4:
|
|
case Q2MZ2_SOLDIER_BLASTER_5:
|
|
case Q2MZ2_SOLDIER_BLASTER_6:
|
|
case Q2MZ2_SOLDIER_BLASTER_7:
|
|
case Q2MZ2_SOLDIER_BLASTER_8:
|
|
case Q2MZ2_TURRET_BLASTER: // PGM
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck2.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_FLYER_BLASTER_1:
|
|
case Q2MZ2_FLYER_BLASTER_2:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("flyer/flyatck3.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_MEDIC_BLASTER_1:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("medic/medatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_HOVER_BLASTER_1:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("hover/hovatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_FLOAT_BLASTER_1:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("floater/fltatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_SOLDIER_SHOTGUN_1:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_2:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_3:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_4:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_5:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_6:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_7:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_8:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_TANK_BLASTER_1:
|
|
case Q2MZ2_TANK_BLASTER_2:
|
|
case Q2MZ2_TANK_BLASTER_3:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_TANK_MACHINEGUN_1:
|
|
case Q2MZ2_TANK_MACHINEGUN_2:
|
|
case Q2MZ2_TANK_MACHINEGUN_3:
|
|
case Q2MZ2_TANK_MACHINEGUN_4:
|
|
case Q2MZ2_TANK_MACHINEGUN_5:
|
|
case Q2MZ2_TANK_MACHINEGUN_6:
|
|
case Q2MZ2_TANK_MACHINEGUN_7:
|
|
case Q2MZ2_TANK_MACHINEGUN_8:
|
|
case Q2MZ2_TANK_MACHINEGUN_9:
|
|
case Q2MZ2_TANK_MACHINEGUN_10:
|
|
case Q2MZ2_TANK_MACHINEGUN_11:
|
|
case Q2MZ2_TANK_MACHINEGUN_12:
|
|
case Q2MZ2_TANK_MACHINEGUN_13:
|
|
case Q2MZ2_TANK_MACHINEGUN_14:
|
|
case Q2MZ2_TANK_MACHINEGUN_15:
|
|
case Q2MZ2_TANK_MACHINEGUN_16:
|
|
case Q2MZ2_TANK_MACHINEGUN_17:
|
|
case Q2MZ2_TANK_MACHINEGUN_18:
|
|
case Q2MZ2_TANK_MACHINEGUN_19:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
CL_ParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Com_sprintf(soundname, sizeof(soundname), "tank/tnkatk2%c.wav", 'a' + rand() % 5);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound(soundname), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_CHICK_ROCKET_1:
|
|
case Q2MZ2_TURRET_ROCKET: // PGM
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("chick/chkatck2.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_TANK_ROCKET_1:
|
|
case Q2MZ2_TANK_ROCKET_2:
|
|
case Q2MZ2_TANK_ROCKET_3:
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_SUPERTANK_ROCKET_1:
|
|
case Q2MZ2_SUPERTANK_ROCKET_2:
|
|
case Q2MZ2_SUPERTANK_ROCKET_3:
|
|
case Q2MZ2_BOSS2_ROCKET_1:
|
|
case Q2MZ2_BOSS2_ROCKET_2:
|
|
case Q2MZ2_BOSS2_ROCKET_3:
|
|
case Q2MZ2_BOSS2_ROCKET_4:
|
|
case Q2MZ2_CARRIER_ROCKET_1:
|
|
// case Q2MZ2_CARRIER_ROCKET_2:
|
|
// case Q2MZ2_CARRIER_ROCKET_3:
|
|
// case Q2MZ2_CARRIER_ROCKET_4:
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/rocket.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_GUNNER_GRENADE_1:
|
|
case Q2MZ2_GUNNER_GRENADE_2:
|
|
case Q2MZ2_GUNNER_GRENADE_3:
|
|
case Q2MZ2_GUNNER_GRENADE_4:
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("gunner/gunatck3.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_GLADIATOR_RAILGUN_1:
|
|
// PMM
|
|
case Q2MZ2_CARRIER_RAILGUN:
|
|
case Q2MZ2_WIDOW_RAIL:
|
|
// pmm
|
|
dl->color[0] = 0.5;dl->color[1] = 0.5;dl->color[2] = 1.0;
|
|
break;
|
|
|
|
// --- Xian's shit starts ---
|
|
case Q2MZ2_MAKRON_BFG:
|
|
dl->color[0] = 0.5;dl->color[1] = 1 ;dl->color[2] = 0.5;
|
|
//Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("makron/bfg_fire.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_MAKRON_BLASTER_1:
|
|
case Q2MZ2_MAKRON_BLASTER_2:
|
|
case Q2MZ2_MAKRON_BLASTER_3:
|
|
case Q2MZ2_MAKRON_BLASTER_4:
|
|
case Q2MZ2_MAKRON_BLASTER_5:
|
|
case Q2MZ2_MAKRON_BLASTER_6:
|
|
case Q2MZ2_MAKRON_BLASTER_7:
|
|
case Q2MZ2_MAKRON_BLASTER_8:
|
|
case Q2MZ2_MAKRON_BLASTER_9:
|
|
case Q2MZ2_MAKRON_BLASTER_10:
|
|
case Q2MZ2_MAKRON_BLASTER_11:
|
|
case Q2MZ2_MAKRON_BLASTER_12:
|
|
case Q2MZ2_MAKRON_BLASTER_13:
|
|
case Q2MZ2_MAKRON_BLASTER_14:
|
|
case Q2MZ2_MAKRON_BLASTER_15:
|
|
case Q2MZ2_MAKRON_BLASTER_16:
|
|
case Q2MZ2_MAKRON_BLASTER_17:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("makron/blaster.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_JORG_MACHINEGUN_L1:
|
|
case Q2MZ2_JORG_MACHINEGUN_L2:
|
|
case Q2MZ2_JORG_MACHINEGUN_L3:
|
|
case Q2MZ2_JORG_MACHINEGUN_L4:
|
|
case Q2MZ2_JORG_MACHINEGUN_L5:
|
|
case Q2MZ2_JORG_MACHINEGUN_L6:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
CL_ParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("boss3/xfire.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_JORG_MACHINEGUN_R1:
|
|
case Q2MZ2_JORG_MACHINEGUN_R2:
|
|
case Q2MZ2_JORG_MACHINEGUN_R3:
|
|
case Q2MZ2_JORG_MACHINEGUN_R4:
|
|
case Q2MZ2_JORG_MACHINEGUN_R5:
|
|
case Q2MZ2_JORG_MACHINEGUN_R6:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
CL_ParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
break;
|
|
|
|
case Q2MZ2_JORG_BFG_1:
|
|
dl->color[0] = 0.5;dl->color[1] = 1 ;dl->color[2] = 0.5;
|
|
break;
|
|
|
|
case Q2MZ2_BOSS2_MACHINEGUN_R1:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_R2:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_R3:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_R4:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_R5:
|
|
case Q2MZ2_CARRIER_MACHINEGUN_R1: // PMM
|
|
case Q2MZ2_CARRIER_MACHINEGUN_R2: // PMM
|
|
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
|
|
CL_ParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
break;
|
|
|
|
// ======
|
|
// ROGUE
|
|
case Q2MZ2_STALKER_BLASTER:
|
|
case Q2MZ2_DAEDALUS_BLASTER:
|
|
case Q2MZ2_MEDIC_BLASTER_2:
|
|
case Q2MZ2_WIDOW_BLASTER:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP1:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP2:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP3:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP4:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP5:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP6:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP7:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP8:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP9:
|
|
case Q2MZ2_WIDOW_BLASTER_100:
|
|
case Q2MZ2_WIDOW_BLASTER_90:
|
|
case Q2MZ2_WIDOW_BLASTER_80:
|
|
case Q2MZ2_WIDOW_BLASTER_70:
|
|
case Q2MZ2_WIDOW_BLASTER_60:
|
|
case Q2MZ2_WIDOW_BLASTER_50:
|
|
case Q2MZ2_WIDOW_BLASTER_40:
|
|
case Q2MZ2_WIDOW_BLASTER_30:
|
|
case Q2MZ2_WIDOW_BLASTER_20:
|
|
case Q2MZ2_WIDOW_BLASTER_10:
|
|
case Q2MZ2_WIDOW_BLASTER_0:
|
|
case Q2MZ2_WIDOW_BLASTER_10L:
|
|
case Q2MZ2_WIDOW_BLASTER_20L:
|
|
case Q2MZ2_WIDOW_BLASTER_30L:
|
|
case Q2MZ2_WIDOW_BLASTER_40L:
|
|
case Q2MZ2_WIDOW_BLASTER_50L:
|
|
case Q2MZ2_WIDOW_BLASTER_60L:
|
|
case Q2MZ2_WIDOW_BLASTER_70L:
|
|
case Q2MZ2_WIDOW_RUN_1:
|
|
case Q2MZ2_WIDOW_RUN_2:
|
|
case Q2MZ2_WIDOW_RUN_3:
|
|
case Q2MZ2_WIDOW_RUN_4:
|
|
case Q2MZ2_WIDOW_RUN_5:
|
|
case Q2MZ2_WIDOW_RUN_6:
|
|
case Q2MZ2_WIDOW_RUN_7:
|
|
case Q2MZ2_WIDOW_RUN_8:
|
|
dl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_WIDOW_DISRUPTOR:
|
|
dl->color[0] = -1;dl->color[1] = -1;dl->color[2] = -1;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/disint2.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_WIDOW_PLASMABEAM:
|
|
case Q2MZ2_WIDOW2_BEAMER_1:
|
|
case Q2MZ2_WIDOW2_BEAMER_2:
|
|
case Q2MZ2_WIDOW2_BEAMER_3:
|
|
case Q2MZ2_WIDOW2_BEAMER_4:
|
|
case Q2MZ2_WIDOW2_BEAMER_5:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_1:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_2:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_3:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_4:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_5:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_6:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_7:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_8:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_9:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_10:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_11:
|
|
dl->radius = 300 + (rand()&100);
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
dl->die = cl.time + 200;
|
|
break;
|
|
// ROGUE
|
|
// ======
|
|
|
|
// --- Xian's shit ends ---
|
|
|
|
//hmm... he must take AGES on the loo.... :p
|
|
|
|
}
|
|
*/
|
|
}
|
|
#endif
|
|
|
|
int getplayerid(char *msg);
|
|
int build_number( void );
|
|
//return if we want to print the message.
|
|
qboolean CL_ParseChat(char *text)
|
|
{
|
|
extern cvar_t cl_chatsound, cl_nofake;
|
|
|
|
char *s;
|
|
s = strchr(text, ':'); //Hmm.. FIXME: Can a player's name contain a ':'?... I think the answer is a yes... Hmmm.. problematic eh?
|
|
if (!s || s[1] != ' ') //wasn't a real chat...
|
|
return true;
|
|
|
|
if (!cls.demoplayback)
|
|
Sys_ServerActivity(); //chat always flashes the screen..
|
|
|
|
//check f_ stuff
|
|
if (!strncmp(s+2, "f_", 2))
|
|
{
|
|
static float versionresponsetime;
|
|
static float modifiedresponsetime;
|
|
static float skinsresponsetime;
|
|
static float serverresponsetime;
|
|
|
|
if (!strncmp(s+2, "f_version", 9) && versionresponsetime < Sys_DoubleTime()) //respond to it.
|
|
{
|
|
ValidationPrintVersion(text);
|
|
versionresponsetime = Sys_DoubleTime() + 5;
|
|
}
|
|
else if (!strncmp(s+2, "f_server", 9) && serverresponsetime < Sys_DoubleTime()) //respond to it.
|
|
{
|
|
Validation_Server();
|
|
serverresponsetime = Sys_DoubleTime() + 5;
|
|
}
|
|
else if (!strncmp(s+2, "f_modified", 10) && modifiedresponsetime < Sys_DoubleTime()) //respond to it.
|
|
{
|
|
Validation_FilesModified();
|
|
modifiedresponsetime = Sys_DoubleTime() + 5;
|
|
}
|
|
else if (!strncmp(s+2, "f_skins", 7) && skinsresponsetime < Sys_DoubleTime()) //respond to it.
|
|
{
|
|
Validation_Skins();
|
|
skinsresponsetime = Sys_DoubleTime() + 5;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Validation_CheckIfResponse(text);
|
|
|
|
|
|
|
|
{
|
|
int flags;
|
|
int offset=0;
|
|
qboolean suppress_talksound;
|
|
char *p;
|
|
|
|
flags = TP_CategorizeMessage (text, &offset);
|
|
|
|
if (flags == 2 && !TP_FilterMessage(text + offset))
|
|
return false;
|
|
|
|
suppress_talksound = false;
|
|
|
|
if (flags == 2 || (!cl.teamplay && flags))
|
|
suppress_talksound = TP_CheckSoundTrigger (text + offset);
|
|
|
|
if (!cl_chatsound.value || // no sound at all
|
|
(cl_chatsound.value == 2 && flags != 2)) // only play sound in mm2
|
|
suppress_talksound = true;
|
|
|
|
if (!suppress_talksound)
|
|
S_LocalSound ("misc/talk.wav");
|
|
|
|
if (cl_nofake.value == 1 || (cl_nofake.value == 2 && flags != 2)) {
|
|
for (p = s; *p; p++)
|
|
if (*p == 13 || (*p == 10 && p[1]))
|
|
*p = ' ';
|
|
}
|
|
|
|
//funky /me stuff
|
|
p = strchr(text, ':');
|
|
if (!strncmp(p, ": /me", 5))
|
|
{
|
|
//shift name right 1 (for the *)
|
|
memmove(text+1, text, p - text);
|
|
*text = '*';
|
|
|
|
memmove(p+1, p+5, strlen(p+5)+1);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
char printtext[2048];
|
|
void CL_ParsePrint(char *msg, int level)
|
|
{
|
|
if (strlen(printtext) + strlen(msg) >= sizeof(printtext))
|
|
{
|
|
Con_Printf("%s", printtext);
|
|
Q_strncpyz(printtext, msg, sizeof(printtext));
|
|
}
|
|
else
|
|
strcat(printtext, msg); //safe due to size on if.
|
|
while((msg = strchr(printtext, '\n')))
|
|
{
|
|
*msg = '\0';
|
|
if (level != PRINT_CHAT)
|
|
Stats_ParsePrintLine(printtext);
|
|
TP_SearchForMsgTriggers(msg, level);
|
|
msg++;
|
|
|
|
memmove(printtext, msg, strlen(msg)+1);
|
|
}
|
|
}
|
|
|
|
char stufftext[4096];
|
|
void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from network segregation.
|
|
{
|
|
strncat(stufftext, msg, sizeof(stufftext)-1);
|
|
while((msg = strchr(stufftext, '\n')))
|
|
{
|
|
*msg = '\0';
|
|
Con_DPrintf("stufftext: %s\n", stufftext);
|
|
if (!strncmp(stufftext, "fullserverinfo ", 15))
|
|
Cmd_ExecuteString(stufftext, RESTRICT_SERVER+destsplit); //do this NOW so that it's done before any models or anything are loaded
|
|
else
|
|
{
|
|
#ifdef CSQC_DAT
|
|
if (!CSQC_StuffCmd(stufftext))
|
|
#endif
|
|
{
|
|
Cbuf_AddText (stufftext, RESTRICT_SERVER+destsplit);
|
|
Cbuf_AddText ("\n", RESTRICT_SERVER+destsplit);
|
|
}
|
|
}
|
|
msg++;
|
|
|
|
memmove(stufftext, msg, strlen(msg)+1);
|
|
}
|
|
}
|
|
|
|
int getplayerid(char *msg)
|
|
{
|
|
int i;
|
|
int namelen;
|
|
char *colon = strstr(msg, ":");
|
|
if (!colon)
|
|
return -1;
|
|
|
|
namelen = colon-msg;
|
|
|
|
for (i=0 ; i<MAX_CLIENTS ; i++)
|
|
{
|
|
if (!strncmp(msg, cl.players[i].name, namelen))
|
|
if (!cl.players[i].name[namelen])
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int getplayerchatcolour(char *msg)
|
|
{
|
|
int id;
|
|
int c;
|
|
id = getplayerid(msg);
|
|
if (id == -1) //not a user/server
|
|
return 1;
|
|
|
|
//primary override.
|
|
msg = Info_ValueForKey(cl.players[id].userinfo, "tc");
|
|
if (*msg)
|
|
{
|
|
c = atoi(msg);
|
|
return c;
|
|
}
|
|
|
|
//override based on team
|
|
if (cl.teamfortress)
|
|
{
|
|
switch (cl.players[id].bottomcolor)
|
|
{ //translate q1 skin colours to console colours
|
|
case 4: //red
|
|
return 1;
|
|
case 13: //blue
|
|
return 5;
|
|
//fixme: add the others
|
|
}
|
|
}
|
|
return cl.players[id].userid;
|
|
}
|
|
|
|
#define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x);
|
|
#define SHOWNET2(x, y) if(cl_shownet.value==2)Con_Printf ("%3i:%3i:%s\n", msg_readcount-1, y, x);
|
|
/*
|
|
=====================
|
|
CL_ParseServerMessage
|
|
=====================
|
|
*/
|
|
char *Translate(char *message);
|
|
int received_framecount;
|
|
void CL_ParseServerMessage (void)
|
|
{
|
|
int cmd;
|
|
char *s;
|
|
int i, j;
|
|
int destsplit;
|
|
|
|
received_framecount = host_framecount;
|
|
cl.last_servermessage = realtime;
|
|
CL_ClearProjectiles ();
|
|
cl.fixangle = false;
|
|
|
|
//
|
|
// if recording demos, copy the message out
|
|
//
|
|
if (cl_shownet.value == 1)
|
|
Con_TPrintf (TL_INT_SPACE,net_message.cursize);
|
|
else if (cl_shownet.value == 2)
|
|
Con_TPrintf (TLC_LINEBREAK_MINUS);
|
|
|
|
|
|
CL_ParseClientdata ();
|
|
|
|
//
|
|
// parse the message
|
|
//
|
|
while (1)
|
|
{
|
|
if (msg_badread)
|
|
{
|
|
Host_EndGame ("CL_ParseServerMessage: Bad server message");
|
|
break;
|
|
}
|
|
|
|
cmd = MSG_ReadByte ();
|
|
|
|
if (cmd == svc_choosesplitclient)
|
|
{
|
|
SHOWNET(svc_strings[cmd]);
|
|
|
|
destsplit = MSG_ReadByte();
|
|
cmd = MSG_ReadByte();
|
|
}
|
|
else
|
|
destsplit = 0;
|
|
|
|
if (cmd == -1)
|
|
{
|
|
msg_readcount++; // so the EOM showner has the right value
|
|
SHOWNET("END OF MESSAGE");
|
|
break;
|
|
}
|
|
|
|
SHOWNET(svc_strings[cmd]);
|
|
|
|
// other commands
|
|
switch (cmd)
|
|
{
|
|
default:
|
|
Host_EndGame ("CL_ParseServerMessage: Illegible server message");
|
|
return;
|
|
|
|
case svc_time:
|
|
cl.gametime = MSG_ReadFloat();
|
|
cl.gametimemark = realtime;
|
|
break;
|
|
|
|
case svc_nop:
|
|
// Con_Printf ("svc_nop\n");
|
|
break;
|
|
|
|
case svc_disconnect:
|
|
if (cls.state == ca_connected)
|
|
{
|
|
Host_EndGame ("Server disconnected\n"
|
|
"Server version may not be compatible");
|
|
}
|
|
else
|
|
Host_EndGame ("Server disconnected");
|
|
break;
|
|
|
|
case svc_print:
|
|
i = MSG_ReadByte ();
|
|
s = MSG_ReadString ();
|
|
if (i == PRINT_CHAT)
|
|
{
|
|
if (CL_ParseChat(s))
|
|
{
|
|
CL_ParsePrint(s, i);
|
|
|
|
if (!cl_standardchat.value)
|
|
Con_TPrintf (TL_CSPECIALPRINT, getplayerchatcolour(s)%6+'1', s); //don't ever print it in white.
|
|
else
|
|
{
|
|
con_ormask = CON_STANDARDMASK;
|
|
Con_TPrintf (TL_ST, s); //Standard text - makes LEDs work in the ocrana (so I'm told) charset.
|
|
con_ormask = 0; //it's a special/wierd characture set.
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CL_ParsePrint(s, i);
|
|
Con_TPrintf (TL_ST, Translate(s));
|
|
}
|
|
break;
|
|
|
|
case svc_centerprint:
|
|
SCR_CenterPrint (destsplit, Translate(MSG_ReadString ()));
|
|
break;
|
|
|
|
case svc_stufftext:
|
|
s = MSG_ReadString ();
|
|
|
|
CL_ParseStuffCmd(s, destsplit);
|
|
break;
|
|
|
|
case svc_damage:
|
|
V_ParseDamage (destsplit);
|
|
break;
|
|
|
|
case svc_serverdata:
|
|
Cbuf_Execute (); // make sure any stuffed commands are done
|
|
CL_ParseServerData ();
|
|
vid.recalc_refdef = true; // leave full screen intermission
|
|
break;
|
|
#ifdef PEXT_SETVIEW
|
|
case svc_setview:
|
|
if (!(cls.fteprotocolextensions & PEXT_SETVIEW))
|
|
Host_EndGame("PEXT_SETVIEW is meant to be disabled\n");
|
|
cl.viewentity[destsplit]=MSG_ReadShort();
|
|
break;
|
|
#endif
|
|
case svc_setangle:
|
|
if (cls.demoplayback == DPB_MVD)
|
|
{
|
|
i = MSG_ReadByte();
|
|
if (i != spec_track[0] || !autocam[0])
|
|
{ //this wasn't for us.
|
|
for (i=0 ; i<3 ; i++)
|
|
MSG_ReadAngle ();
|
|
break;
|
|
}
|
|
cl.fixangle=true;
|
|
for (i=0 ; i<3 ; i++)
|
|
cl.simangles[destsplit][i] = cl.viewangles[destsplit][i] = MSG_ReadAngle ();
|
|
break;
|
|
}
|
|
cl.fixangle=true;
|
|
for (i=0 ; i<3 ; i++)
|
|
cl.viewangles[destsplit][i] = MSG_ReadAngle ();
|
|
// cl.viewangles[PITCH] = cl.viewangles[ROLL] = 0;
|
|
break;
|
|
|
|
case svc_lightstyle:
|
|
i = MSG_ReadByte ();
|
|
if (i >= MAX_LIGHTSTYLES)
|
|
Host_EndGame ("svc_lightstyle > MAX_LIGHTSTYLES");
|
|
#ifdef PEXT_LIGHTSTYLECOL
|
|
cl_lightstyle[i].colour = 7; //white
|
|
#endif
|
|
Q_strncpyz (cl_lightstyle[i].map, MSG_ReadString(), sizeof(cl_lightstyle[i].map));
|
|
cl_lightstyle[i].length = Q_strlen(cl_lightstyle[i].map);
|
|
break;
|
|
#ifdef PEXT_LIGHTSTYLECOL
|
|
case svc_lightstylecol:
|
|
if (!(cls.fteprotocolextensions & PEXT_LIGHTSTYLECOL))
|
|
Host_EndGame("PEXT_LIGHTSTYLECOL is meant to be disabled\n");
|
|
i = MSG_ReadByte ();
|
|
if (i >= MAX_LIGHTSTYLES)
|
|
Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES");
|
|
cl_lightstyle[i].colour = MSG_ReadByte();
|
|
Q_strncpyz (cl_lightstyle[i].map, MSG_ReadString(), sizeof(cl_lightstyle[i].map));
|
|
cl_lightstyle[i].length = Q_strlen(cl_lightstyle[i].map);
|
|
break;
|
|
#endif
|
|
|
|
case svc_sound:
|
|
CL_ParseStartSoundPacket();
|
|
break;
|
|
|
|
case svc_stopsound:
|
|
i = MSG_ReadShort();
|
|
S_StopSound(i>>3, i&7);
|
|
break;
|
|
|
|
case svc_updatefrags:
|
|
Sbar_Changed ();
|
|
i = MSG_ReadByte ();
|
|
if (i >= MAX_CLIENTS)
|
|
Host_EndGame ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD");
|
|
cl.players[i].frags = MSG_ReadShort ();
|
|
break;
|
|
|
|
case svc_updateping:
|
|
i = MSG_ReadByte ();
|
|
if (i >= MAX_CLIENTS)
|
|
Host_EndGame ("CL_ParseServerMessage: svc_updateping > MAX_SCOREBOARD");
|
|
cl.players[i].ping = MSG_ReadShort ();
|
|
break;
|
|
|
|
case svc_updatepl:
|
|
i = MSG_ReadByte ();
|
|
if (i >= MAX_CLIENTS)
|
|
Host_EndGame ("CL_ParseServerMessage: svc_updatepl > MAX_SCOREBOARD");
|
|
cl.players[i].pl = MSG_ReadByte ();
|
|
break;
|
|
|
|
case svc_updateentertime:
|
|
// time is sent over as seconds ago
|
|
i = MSG_ReadByte ();
|
|
if (i >= MAX_CLIENTS)
|
|
Host_EndGame ("CL_ParseServerMessage: svc_updateentertime > MAX_SCOREBOARD");
|
|
cl.players[i].entertime = realtime - MSG_ReadFloat ();
|
|
break;
|
|
|
|
case svc_spawnbaseline:
|
|
i = MSG_ReadShort ();
|
|
CL_ParseBaseline (&cl_baselines[i]);
|
|
break;
|
|
case svc_spawnbaseline2:
|
|
CL_ParseBaseline2 ();
|
|
break;
|
|
case svc_spawnstatic:
|
|
CL_ParseStatic (1);
|
|
break;
|
|
case svc_spawnstatic2:
|
|
CL_ParseStatic (2);
|
|
break;
|
|
case svc_temp_entity:
|
|
#ifdef NQPROT
|
|
CL_ParseTEnt (false);
|
|
#else
|
|
CL_ParseTEnt ();
|
|
#endif
|
|
break;
|
|
case svc_customtempent:
|
|
CL_ParseCustomTEnt();
|
|
break;
|
|
|
|
case svc_particle:
|
|
CLNQ_ParseParticleEffect ();
|
|
break;
|
|
case svc_particle2:
|
|
CL_ParseParticleEffect2 ();
|
|
break;
|
|
case svc_particle3:
|
|
CL_ParseParticleEffect3 ();
|
|
break;
|
|
case svc_particle4:
|
|
CL_ParseParticleEffect4 ();
|
|
break;
|
|
|
|
case svc_killedmonster:
|
|
cl.stats[0][STAT_MONSTERS]++;
|
|
break;
|
|
|
|
case svc_foundsecret:
|
|
cl.stats[0][STAT_SECRETS]++;
|
|
break;
|
|
|
|
case svc_updatestat:
|
|
i = MSG_ReadByte ();
|
|
j = MSG_ReadByte ();
|
|
CL_SetStat (destsplit, i, j);
|
|
break;
|
|
case svc_updatestatlong:
|
|
i = MSG_ReadByte ();
|
|
j = MSG_ReadLong (); //make qbyte if nq compatability?
|
|
CL_SetStat (destsplit, i, j);
|
|
break;
|
|
|
|
case svc_spawnstaticsound:
|
|
CL_ParseStaticSound ();
|
|
break;
|
|
|
|
case svc_cdtrack:
|
|
cl.cdtrack = MSG_ReadByte ();
|
|
CDAudio_Play ((qbyte)cl.cdtrack, true);
|
|
break;
|
|
|
|
case svc_intermission:
|
|
cl.intermission = 1;
|
|
cl.completed_time = realtime;
|
|
vid.recalc_refdef = true; // go to full screen
|
|
for (i=0 ; i<3 ; i++)
|
|
cl.simorg[0][i] = MSG_ReadCoord ();
|
|
for (i=0 ; i<3 ; i++)
|
|
cl.simangles[0][i] = MSG_ReadAngle ();
|
|
VectorCopy (vec3_origin, cl.simvel[0]);
|
|
|
|
VectorCopy (cl.simvel[0], cl.simvel[1]);
|
|
VectorCopy (cl.simangles[0], cl.simangles[1]);
|
|
VectorCopy (cl.simorg[0], cl.simorg[1]);
|
|
break;
|
|
|
|
case svc_finale:
|
|
cl.intermission = 2;
|
|
cl.completed_time = realtime;
|
|
vid.recalc_refdef = true; // go to full screen
|
|
SCR_CenterPrint (destsplit, MSG_ReadString ());
|
|
break;
|
|
|
|
case svc_sellscreen:
|
|
Cmd_ExecuteString ("help", RESTRICT_RCON);
|
|
break;
|
|
|
|
case svc_smallkick:
|
|
cl.punchangle[destsplit] = -2;
|
|
break;
|
|
case svc_bigkick:
|
|
cl.punchangle[destsplit] = -4;
|
|
break;
|
|
|
|
case svc_muzzleflash:
|
|
CL_MuzzleFlash ();
|
|
break;
|
|
|
|
case svc_updateuserinfo:
|
|
CL_UpdateUserinfo ();
|
|
break;
|
|
|
|
case svc_setinfo:
|
|
CL_SetInfo ();
|
|
break;
|
|
|
|
case svc_serverinfo:
|
|
CL_ServerInfo ();
|
|
break;
|
|
|
|
case svc_download:
|
|
CL_ParseDownload ();
|
|
break;
|
|
|
|
case svc_playerinfo:
|
|
CL_ParsePlayerinfo ();
|
|
break;
|
|
|
|
case svc_nails:
|
|
CL_ParseProjectiles (cl_spikeindex, false);
|
|
break;
|
|
case svc_nails2:
|
|
CL_ParseProjectiles (cl_spikeindex, true);
|
|
break;
|
|
|
|
case svc_chokecount: // some preceding packets were choked
|
|
i = MSG_ReadByte ();
|
|
for (j=0 ; j<i ; j++)
|
|
cl.frames[ (cls.netchan.incoming_acknowledged-1-j)&UPDATE_MASK ].receivedtime = -2;
|
|
break;
|
|
|
|
case svc_modellist:
|
|
CL_ParseModellist (false);
|
|
break;
|
|
case svc_modellistshort:
|
|
CL_ParseModellist (true);
|
|
break;
|
|
|
|
case svc_soundlist:
|
|
CL_ParseSoundlist ();
|
|
break;
|
|
|
|
case svc_packetentities:
|
|
CL_ParsePacketEntities (false);
|
|
break;
|
|
|
|
case svc_deltapacketentities:
|
|
CL_ParsePacketEntities (true);
|
|
break;
|
|
|
|
case svc_maxspeed :
|
|
cl.maxspeed[destsplit] = MSG_ReadFloat();
|
|
break;
|
|
|
|
case svc_entgravity :
|
|
cl.entgravity[destsplit] = MSG_ReadFloat();
|
|
break;
|
|
|
|
case svc_setpause:
|
|
cl.paused = MSG_ReadByte ();
|
|
if (cl.paused)
|
|
CDAudio_Pause ();
|
|
else
|
|
CDAudio_Resume ();
|
|
break;
|
|
|
|
#ifdef PEXT_BULLETENS
|
|
case svc_bulletentext:
|
|
if (!(cls.fteprotocolextensions & PEXT_BULLETENS))
|
|
Host_EndGame("PEXT_BULLETENS is meant to be disabled\n");
|
|
Bul_ParseMessage();
|
|
break;
|
|
#endif
|
|
#ifdef PEXT_LIGHTUPDATES
|
|
case svc_lightnings:
|
|
if (!(cls.fteprotocolextensions & PEXT_LIGHTUPDATES))
|
|
Host_EndGame("PEXT_LIGHTUPDATES is meant to be disabled\n");
|
|
CL_ParseProjectiles (cl_lightningindex);
|
|
break;
|
|
#endif
|
|
|
|
case svc_ftesetclientpersist:
|
|
CL_ParseClientPersist();
|
|
break;
|
|
#ifdef Q2BSPS
|
|
case svc_setportalstate:
|
|
i = MSG_ReadByte();
|
|
j = MSG_ReadByte();
|
|
i *= j & 127;
|
|
j &= ~128;
|
|
CMQ2_SetAreaPortalState(i, j!=0);
|
|
break;
|
|
#endif
|
|
|
|
case svc_showpic:
|
|
SCR_ShowPic_Create();
|
|
break;
|
|
case svc_hidepic:
|
|
SCR_ShowPic_Hide();
|
|
break;
|
|
case svc_movepic:
|
|
SCR_ShowPic_Move();
|
|
break;
|
|
case svc_updatepic:
|
|
SCR_ShowPic_Update();
|
|
break;
|
|
|
|
case svc_setattachment:
|
|
CL_ParseAttachment();
|
|
break;
|
|
|
|
case svcqw_effect:
|
|
CL_ParseEffect(false);
|
|
break;
|
|
case svcqw_effect2:
|
|
CL_ParseEffect(true);
|
|
break;
|
|
|
|
#ifdef PEXT_CSQC
|
|
case svc_csqcentities:
|
|
CSQC_ParseEntities();
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef Q2CLIENT
|
|
void CLQ2_ParseServerMessage (void)
|
|
{
|
|
int cmd;
|
|
char *s;
|
|
int i;
|
|
// int j;
|
|
|
|
received_framecount = host_framecount;
|
|
cl.last_servermessage = realtime;
|
|
CL_ClearProjectiles ();
|
|
|
|
//
|
|
// if recording demos, copy the message out
|
|
//
|
|
if (cl_shownet.value == 1)
|
|
Con_TPrintf (TL_INT_SPACE,net_message.cursize);
|
|
else if (cl_shownet.value == 2)
|
|
Con_TPrintf (TLC_LINEBREAK_MINUS);
|
|
|
|
|
|
CL_ParseClientdata ();
|
|
|
|
//
|
|
// parse the message
|
|
//
|
|
while (1)
|
|
{
|
|
if (msg_badread)
|
|
{
|
|
Host_EndGame ("CL_ParseServerMessage: Bad server message");
|
|
break;
|
|
}
|
|
|
|
cmd = MSG_ReadByte ();
|
|
|
|
if (cmd == -1)
|
|
{
|
|
msg_readcount++; // so the EOM showner has the right value
|
|
SHOWNET("END OF MESSAGE");
|
|
break;
|
|
}
|
|
|
|
SHOWNET(va("%i", cmd));
|
|
|
|
// other commands
|
|
switch (cmd)
|
|
{
|
|
default:
|
|
Host_EndGame ("CL_ParseServerMessage: Illegible server message");
|
|
return;
|
|
|
|
//known to game
|
|
case svcq2_muzzleflash:
|
|
CLQ2_ParseMuzzleFlash();
|
|
break;
|
|
case svcq2_muzzleflash2:
|
|
CLQ2_ParseMuzzleFlash2();
|
|
return;
|
|
case svcq2_temp_entity:
|
|
CLQ2_ParseTEnt();
|
|
break;
|
|
case svcq2_layout:
|
|
s = MSG_ReadString ();
|
|
Q_strncpyz (cl.q2layout, s, sizeof(cl.q2layout));
|
|
UI_Q2LayoutChanged();
|
|
break;
|
|
case svcq2_inventory:
|
|
Host_EndGame ("CL_ParseServerMessage: svcq2_inventory not implemented");
|
|
return;
|
|
|
|
// the rest are private to the client and server
|
|
case svcq2_nop: //6
|
|
Host_EndGame ("CL_ParseServerMessage: svcq2_nop not implemented");
|
|
return;
|
|
case svcq2_disconnect:
|
|
if (cls.state == ca_connected)
|
|
Host_EndGame ("Server disconnected\n"
|
|
"Server version may not be compatible");
|
|
else
|
|
Host_EndGame ("Server disconnected");
|
|
return;
|
|
case svcq2_reconnect: //8
|
|
Con_TPrintf (TLC_RECONNECTING);
|
|
CL_SendClientCommand("new");
|
|
break;
|
|
case svcq2_sound: //9 // <see code>
|
|
CLQ2_ParseStartSoundPacket();
|
|
break;
|
|
case svcq2_print: //10 // [qbyte] id [string] null terminated string
|
|
i = MSG_ReadByte ();
|
|
s = MSG_ReadString ();
|
|
if (i == PRINT_CHAT)
|
|
{
|
|
S_LocalSound ("misc/talk.wav");
|
|
con_ormask = 0x8000;
|
|
if (CL_ParseChat(s))
|
|
{
|
|
CL_ParsePrint(s, i);
|
|
Con_TPrintf (TL_CSPECIALPRINT, getplayerchatcolour(s)%6+'1', s);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CL_ParsePrint(s, i);
|
|
Con_TPrintf (TL_ST, Translate(s));
|
|
}
|
|
con_ormask = 0;
|
|
break;
|
|
case svcq2_stufftext: //11 // [string] stuffed into client's console buffer, should be \n terminated
|
|
s = MSG_ReadString ();
|
|
Con_DPrintf ("stufftext: %s\n", s);
|
|
if (!strncmp(s, "precache", 8)) //big major hack. Q2 uses a command that q1 has as a cvar.
|
|
{ //call the q2 precache function.
|
|
CLQ2_Precache_f();
|
|
}
|
|
else
|
|
Cbuf_AddText (s, RESTRICT_SERVER); //don't let the local user cheat
|
|
break;
|
|
case svcq2_serverdata: //12 // [long] protocol ...
|
|
Cbuf_Execute (); // make sure any stuffed commands are done
|
|
CLQ2_ParseServerData ();
|
|
break;
|
|
case svcq2_configstring: //13 // [short] [string]
|
|
CLQ2_ParseConfigString();
|
|
break;
|
|
case svcq2_spawnbaseline://14
|
|
CLQ2_ParseBaseline();
|
|
break;
|
|
case svcq2_centerprint: //15 // [string] to put in center of the screen
|
|
SCR_CenterPrint (0, Translate(MSG_ReadString ()));
|
|
break;
|
|
case svcq2_download: //16 // [short] size [size bytes]
|
|
CL_ParseDownload();
|
|
break;
|
|
case svcq2_playerinfo: //17 // variable
|
|
Host_EndGame ("CL_ParseServerMessage: svcq2_playerinfo not implemented");
|
|
return;
|
|
case svcq2_packetentities://18 // [...]
|
|
Host_EndGame ("CL_ParseServerMessage: svcq2_packetentities not implemented");
|
|
return;
|
|
case svcq2_deltapacketentities://19 // [...]
|
|
Host_EndGame ("CL_ParseServerMessage: svcq2_deltapacketentities not implemented");
|
|
return;
|
|
case svcq2_frame: //20 (the bastard to implement.)
|
|
CLQ2_ParseFrame();
|
|
break;
|
|
}
|
|
}
|
|
CL_SetSolidEntities ();
|
|
}
|
|
#endif
|
|
|
|
#ifdef NQPROT
|
|
void CLNQ_ParseServerMessage (void)
|
|
{
|
|
int cmd;
|
|
char *s;
|
|
int i, j;
|
|
|
|
// received_framecount = host_framecount;
|
|
// cl.last_servermessage = realtime;
|
|
CL_ClearProjectiles ();
|
|
cl.fixangle = false;
|
|
|
|
//
|
|
// if recording demos, copy the message out
|
|
//
|
|
if (cl_shownet.value == 1)
|
|
Con_TPrintf (TL_INT_SPACE,net_message.cursize);
|
|
else if (cl_shownet.value == 2)
|
|
Con_TPrintf (TLC_LINEBREAK_MINUS);
|
|
|
|
|
|
CL_ParseClientdata ();
|
|
|
|
|
|
MSG_BeginReading ();
|
|
//
|
|
// parse the message
|
|
//
|
|
while (1)
|
|
{
|
|
if (msg_badread)
|
|
{
|
|
Host_EndGame ("CL_ParseServerMessage: Bad server message");
|
|
break;
|
|
}
|
|
|
|
cmd = MSG_ReadByte ();
|
|
|
|
if (cmd == -1)
|
|
{
|
|
msg_readcount++; // so the EOM showner has the right value
|
|
SHOWNET("END OF MESSAGE");
|
|
break;
|
|
}
|
|
|
|
if (cmd & 128)
|
|
{
|
|
SHOWNET("fast update");
|
|
CLNQ_ParseEntity(cmd&127);
|
|
continue;
|
|
}
|
|
|
|
SHOWNET2(svc_nqstrings[cmd>(sizeof(svc_nqstrings)/sizeof(char*))?0:cmd], cmd);
|
|
|
|
// other commands
|
|
switch (cmd)
|
|
{
|
|
default:
|
|
Host_EndGame ("CLNQ_ParseServerMessage: Illegible server message (%i)", cmd);
|
|
return;
|
|
|
|
case svc_nop:
|
|
// Con_Printf ("svc_nop\n");
|
|
break;
|
|
|
|
case svc_print:
|
|
s = MSG_ReadString ();
|
|
if (*s == 1 || *s == 2)
|
|
{
|
|
if (CL_ParseChat(s+1))
|
|
{
|
|
CL_ParsePrint(s+1, 3);
|
|
Con_TPrintf (TL_ST, Translate(s));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CL_ParsePrint(s, 3);
|
|
Con_TPrintf (TL_ST, Translate(s));
|
|
}
|
|
con_ormask = 0;
|
|
break;
|
|
|
|
case svc_disconnect:
|
|
// CL_Disconnect();
|
|
break;
|
|
|
|
case svc_centerprint:
|
|
SCR_CenterPrint (0, Translate(MSG_ReadString ()));
|
|
break;
|
|
|
|
case svc_stufftext:
|
|
s = MSG_ReadString ();
|
|
Con_DPrintf ("stufftext: %s\n", s);
|
|
Cbuf_AddText (s, RESTRICT_SERVER); //no cheating here...
|
|
break;
|
|
|
|
case svc_serverdata:
|
|
Cbuf_Execute (); // make sure any stuffed commands are done
|
|
CLNQ_ParseServerData ();
|
|
vid.recalc_refdef = true; // leave full screen intermission
|
|
break;
|
|
|
|
case 54://svc_precache:
|
|
{
|
|
int i = (unsigned short)MSG_ReadShort();
|
|
char *s = MSG_ReadString();
|
|
if (i < 32768)
|
|
{
|
|
if (i >= 1 && i < MAX_MODELS)
|
|
{
|
|
model_t *model;
|
|
CL_CheckOrDownloadFile(s, true);
|
|
model = Mod_ForName(s, i == 1);
|
|
if (!model)
|
|
Con_Printf("svc_precache: Mod_ForName(\"%s\") failed\n", s);
|
|
cl.model_precache[i] = model;
|
|
strcpy (cl.model_name[i], s);
|
|
}
|
|
else
|
|
Con_Printf("svc_precache: model index %i outside range %i...%i\n", i, 1, MAX_MODELS);
|
|
}
|
|
else
|
|
{
|
|
i -= 32768;
|
|
if (i >= 1 && i < MAX_SOUNDS)
|
|
{
|
|
sfx_t *sfx;
|
|
CL_CheckOrDownloadFile(va("sounds/%s", s), true);
|
|
sfx = S_PrecacheSound (s);
|
|
if (!sfx)
|
|
Con_Printf("svc_precache: S_PrecacheSound(\"%s\") failed\n", s);
|
|
cl.sound_precache[i] = sfx;
|
|
strcpy (cl.sound_name[i], s);
|
|
}
|
|
else
|
|
Con_Printf("svc_precache: sound index %i outside range %i...%i\n", i, 1, MAX_SOUNDS);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case svc_cdtrack:
|
|
cl.cdtrack = MSG_ReadByte ();
|
|
MSG_ReadByte ();
|
|
|
|
CDAudio_Play ((qbyte)cl.cdtrack, true);
|
|
break;
|
|
|
|
case svc_setview:
|
|
if (!cl.viewentity[0])
|
|
cl.playernum[0] = (cl.viewentity[0] = MSG_ReadShort())-1;
|
|
else
|
|
cl.viewentity[0]=MSG_ReadShort();
|
|
break;
|
|
|
|
case svc_signonnum:
|
|
i = MSG_ReadByte ();
|
|
|
|
if (i <= cls.signon)
|
|
Host_EndGame ("Received signon %i when at %i", i, cls.signon);
|
|
cls.signon = i;
|
|
CLNQ_SignonReply ();
|
|
break;
|
|
case svc_setpause:
|
|
cl.paused = MSG_ReadByte ();
|
|
if (cl.paused)
|
|
CDAudio_Pause ();
|
|
else
|
|
CDAudio_Resume ();
|
|
break;
|
|
|
|
case svc_spawnstaticsound:
|
|
CL_ParseStaticSound ();
|
|
break;
|
|
|
|
case svc_spawnstatic:
|
|
CL_ParseStatic (1);
|
|
break;
|
|
|
|
case svc_spawnbaseline:
|
|
i = MSG_ReadShort ();
|
|
CL_ParseBaseline (&cl_baselines[i]);
|
|
break;
|
|
|
|
case svc_time:
|
|
received_framecount = host_framecount;
|
|
cl.last_servermessage = realtime;
|
|
cl.gametime = MSG_ReadFloat();
|
|
cl.gametimemark = realtime;
|
|
if (nq_dp_protocol<5)
|
|
{
|
|
cl.validsequence = cls.netchan.incoming_sequence++;
|
|
// cl.frames[(cls.netchan.incoming_sequence-1)&UPDATE_MASK].packet_entities = cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities;
|
|
cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities.num_entities=0;
|
|
}
|
|
break;
|
|
|
|
case svc_updatename:
|
|
Sbar_Changed ();
|
|
i = MSG_ReadByte ();
|
|
strcpy(cl.players[i].name, MSG_ReadString());
|
|
break;
|
|
|
|
case svc_updatefrags:
|
|
Sbar_Changed ();
|
|
i = MSG_ReadByte ();
|
|
cl.players[i].frags = MSG_ReadShort();
|
|
break;
|
|
case svc_updatecolors:
|
|
{
|
|
int a;
|
|
Sbar_Changed ();
|
|
i = MSG_ReadByte ();
|
|
if (i >= MAX_SCOREBOARD)
|
|
Host_Error ("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD");
|
|
a = MSG_ReadByte ();
|
|
//FIXME:!!!!
|
|
|
|
cl.players[i].topcolor = a&0x0f;
|
|
cl.players[i].bottomcolor = (a&0xf0)>>4;
|
|
|
|
if (cls.state == ca_active)
|
|
Skin_Find (&cl.players[i]);
|
|
|
|
Sbar_Changed ();
|
|
CL_NewTranslation (i);
|
|
}
|
|
break;
|
|
case svc_lightstyle:
|
|
i = MSG_ReadByte ();
|
|
if (i >= MAX_LIGHTSTYLES)
|
|
Host_EndGame ("svc_lightstyle > MAX_LIGHTSTYLES");
|
|
#ifdef PEXT_LIGHTSTYLECOL
|
|
cl_lightstyle[i].colour = 7; //white
|
|
#endif
|
|
Q_strncpyz (cl_lightstyle[i].map, MSG_ReadString(), sizeof(cl_lightstyle[i].map));
|
|
cl_lightstyle[i].length = Q_strlen(cl_lightstyle[i].map);
|
|
break;
|
|
|
|
case svc_updatestat:
|
|
i = MSG_ReadByte ();
|
|
j = MSG_ReadLong ();
|
|
CL_SetStat (0, i, j);
|
|
break;
|
|
case 51://svc_updatestat:
|
|
i = MSG_ReadByte ();
|
|
j = MSG_ReadByte ();
|
|
CL_SetStat (0, i, j);
|
|
break;
|
|
case svc_setangle:
|
|
for (i=0 ; i<3 ; i++)
|
|
cl.viewangles[0][i] = MSG_ReadAngle ();
|
|
// cl.viewangles[PITCH] = cl.viewangles[ROLL] = 0;
|
|
break;
|
|
|
|
case svc_clientdata:
|
|
i = MSG_ReadShort ();
|
|
CLNQ_ParseClientdata (i);
|
|
break;
|
|
|
|
case svc_sound:
|
|
CLNQ_ParseStartSoundPacket();
|
|
break;
|
|
|
|
case svc_temp_entity:
|
|
CL_ParseTEnt (true);
|
|
break;
|
|
|
|
case svc_particle:
|
|
CLNQ_ParseParticleEffect ();
|
|
break;
|
|
|
|
case svc_killedmonster:
|
|
cl.stats[0][STAT_MONSTERS]++;
|
|
break;
|
|
|
|
case svc_foundsecret:
|
|
cl.stats[0][STAT_SECRETS]++;
|
|
break;
|
|
|
|
case svc_intermission:
|
|
cl.intermission = 1;
|
|
cl.completed_time = cl.time;
|
|
vid.recalc_refdef = true; // go to full screen
|
|
break;
|
|
|
|
case svc_finale:
|
|
cl.intermission = 2;
|
|
cl.completed_time = cl.time;
|
|
vid.recalc_refdef = true; // go to full screen
|
|
SCR_CenterPrint (0, MSG_ReadString ());
|
|
break;
|
|
|
|
case svc_cutscene:
|
|
cl.intermission = 3;
|
|
cl.completed_time = cl.time;
|
|
vid.recalc_refdef = true; // go to full screen
|
|
SCR_CenterPrint (0, MSG_ReadString ());
|
|
break;
|
|
|
|
case svc_sellscreen: //pantsie
|
|
Cmd_ExecuteString ("help 0", RESTRICT_RCON);
|
|
break;
|
|
|
|
case svc_damage:
|
|
V_ParseDamage (0);
|
|
break;
|
|
|
|
case svcnq_effect:
|
|
CL_ParseEffect(false);
|
|
break;
|
|
case svcnq_effect2:
|
|
CL_ParseEffect(true);
|
|
break;
|
|
|
|
case 57://svc_entities
|
|
if (cls.signon == 4 - 1)
|
|
{ // first update is the final signon stage
|
|
cls.signon = 4;
|
|
CLNQ_SignonReply ();
|
|
}
|
|
//well, it's really any protocol, but we're only going to support version 5.
|
|
CLNQ_ParseDarkPlaces5Entities();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|