2004-08-23 00:15:46 +00:00
|
|
|
#include "qwsvdef.h"
|
|
|
|
|
|
|
|
#define Q2NUM_FOR_EDICT(ent) (((char *)ent - (char *)ge->edicts) / ge->edict_size)
|
|
|
|
|
|
|
|
#ifndef Q2SERVER
|
|
|
|
qboolean SVQ2_InitGameProgs(void)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
game_export_t *ge;
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-11-17 17:31:21 +00:00
|
|
|
void Sys_UnloadGame (void);
|
|
|
|
void *Sys_GetGameAPI (void *parms);
|
|
|
|
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
PF_Unicast
|
|
|
|
|
|
|
|
Sends the contents of the mutlicast buffer to a single client
|
|
|
|
===============
|
|
|
|
*/
|
2005-03-18 06:13:11 +00:00
|
|
|
static void VARGS PFQ2_Unicast (q2edict_t *ent, qboolean reliable)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
int p;
|
|
|
|
client_t *client;
|
|
|
|
|
|
|
|
if (!ent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
p = Q2NUM_FOR_EDICT(ent);
|
|
|
|
if (p < 1 || p > MAX_CLIENTS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
client = svs.clients + (p-1);
|
|
|
|
|
|
|
|
if (client->state < cs_connected)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (reliable)
|
2005-07-14 01:57:34 +00:00
|
|
|
SZ_Write (&client->netchan.message, sv.q2multicast.data, sv.q2multicast.cursize);
|
2004-08-23 00:15:46 +00:00
|
|
|
else
|
2005-07-14 01:57:34 +00:00
|
|
|
SZ_Write (&client->datagram, sv.q2multicast.data, sv.q2multicast.cursize);
|
2004-08-23 00:15:46 +00:00
|
|
|
|
2005-07-14 01:57:34 +00:00
|
|
|
SZ_Clear (&sv.q2multicast);
|
2004-08-23 00:15:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
PF_dprintf
|
|
|
|
|
|
|
|
Debug print to server console
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
static void VARGS PFQ2_dprintf (char *fmt, ...)
|
|
|
|
{
|
|
|
|
char msg[1024];
|
|
|
|
va_list argptr;
|
|
|
|
|
|
|
|
va_start (argptr,fmt);
|
|
|
|
vsprintf (msg, fmt, argptr);
|
|
|
|
va_end (argptr);
|
|
|
|
|
|
|
|
Con_Printf ("%s", msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
PF_cprintf
|
|
|
|
|
|
|
|
Print to a single client
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
static void VARGS PFQ2_cprintf (q2edict_t *ent, int level, char *fmt, ...)
|
|
|
|
{
|
|
|
|
char msg[1024];
|
|
|
|
va_list argptr;
|
|
|
|
int n=0;
|
|
|
|
|
|
|
|
if (ent)
|
|
|
|
{
|
|
|
|
n = Q2NUM_FOR_EDICT(ent);
|
|
|
|
if (n < 1 || n > MAX_CLIENTS)
|
|
|
|
{
|
|
|
|
Sys_Error ("cprintf to a non-client");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (svs.clients[n-1].state < cs_connected)
|
|
|
|
{
|
|
|
|
Sys_Error ("cprintf to a disconnected client");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
va_start (argptr,fmt);
|
|
|
|
vsprintf (msg, fmt, argptr);
|
|
|
|
va_end (argptr);
|
|
|
|
|
|
|
|
if (ent)
|
|
|
|
SV_ClientPrintf (svs.clients+(n-1), level, "%s", msg);
|
|
|
|
else
|
|
|
|
Con_Printf ("%s", msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
PF_centerprintf
|
|
|
|
|
|
|
|
centerprint to a single client
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
static void VARGS PFQ2_centerprintf (q2edict_t *ent, char *fmt, ...)
|
|
|
|
{
|
|
|
|
char msg[1024];
|
|
|
|
va_list argptr;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
n = Q2NUM_FOR_EDICT(ent);
|
|
|
|
if (n < 1 || n > MAX_CLIENTS)
|
|
|
|
return; // Com_Error (ERR_DROP, "centerprintf to a non-client");
|
|
|
|
|
|
|
|
if (svs.clients[n-1].state < cs_connected)
|
|
|
|
return;
|
|
|
|
|
|
|
|
va_start (argptr,fmt);
|
|
|
|
vsprintf (msg, fmt, argptr);
|
|
|
|
va_end (argptr);
|
|
|
|
|
2005-07-14 01:57:34 +00:00
|
|
|
MSG_WriteByte (&sv.q2multicast,svcq2_centerprint);
|
|
|
|
MSG_WriteString (&sv.q2multicast,msg);
|
2004-08-23 00:15:46 +00:00
|
|
|
PFQ2_Unicast (ent, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
PF_error
|
|
|
|
|
|
|
|
Abort the server with a game error
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
static void VARGS PFQ2_error (char *fmt, ...)
|
|
|
|
{
|
|
|
|
char msg[1024];
|
|
|
|
va_list argptr;
|
|
|
|
|
|
|
|
va_start (argptr,fmt);
|
|
|
|
vsprintf (msg, fmt, argptr);
|
|
|
|
va_end (argptr);
|
|
|
|
|
2005-01-07 02:34:59 +00:00
|
|
|
SV_Error("Game Error: %s", msg);
|
2004-08-23 00:15:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
PF_Configstring
|
|
|
|
|
|
|
|
===============
|
|
|
|
*/
|
2005-03-18 06:13:11 +00:00
|
|
|
static void VARGS PFQ2_Configstring (int i, char *val)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
if (i < 0 || i >= Q2MAX_CONFIGSTRINGS)
|
|
|
|
Sys_Error ("configstring: bad index %i\n", i);
|
|
|
|
|
|
|
|
if (!val)
|
|
|
|
val = "";
|
|
|
|
|
2006-02-17 02:51:59 +00:00
|
|
|
strcpy(sv.strings.configstring[i], val);
|
2005-03-20 02:57:11 +00:00
|
|
|
|
|
|
|
if (i == Q2CS_NAME)
|
|
|
|
Q_strncpyz(sv.mapname, val, sizeof(sv.name));
|
|
|
|
|
|
|
|
/*
|
2004-08-23 00:15:46 +00:00
|
|
|
//work out range
|
|
|
|
if (i >= Q2CS_LIGHTS && i < Q2CS_LIGHTS+Q2MAX_LIGHTSTYLES)
|
|
|
|
{
|
|
|
|
j = i - Q2CS_LIGHTS;
|
|
|
|
if (j < MAX_LIGHTSTYLES)
|
|
|
|
{
|
|
|
|
if (sv.lightstyles[j])
|
|
|
|
Z_Free(sv.lightstyles[j]);
|
|
|
|
sv.lightstyles[j] = Z_Malloc(strlen(val)+1);
|
|
|
|
strcpy(sv.lightstyles[j], val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (i >= Q2CS_MODELS && i < Q2CS_MODELS+Q2MAX_MODELS)
|
|
|
|
{
|
|
|
|
Q_strncpyS(sv.model_precache[i-Q2CS_MODELS], val, MAX_QPATH-1);
|
|
|
|
}
|
|
|
|
else if (i >= Q2CS_SOUNDS && i < Q2CS_SOUNDS+Q2MAX_SOUNDS)
|
|
|
|
{
|
|
|
|
Q_strncpyS(sv.sound_precache[i-Q2CS_SOUNDS], val, MAX_QPATH-1);
|
|
|
|
}
|
|
|
|
else if (i >= Q2CS_IMAGES && i < Q2CS_IMAGES+Q2MAX_IMAGES)
|
|
|
|
{
|
|
|
|
Q_strncpyS(sv.image_precache[i-Q2CS_IMAGES], val, MAX_QPATH-1);
|
|
|
|
}
|
|
|
|
else if (i == Q2CS_STATUSBAR)
|
|
|
|
{
|
|
|
|
if (sv.statusbar)
|
|
|
|
Z_Free(sv.statusbar);
|
|
|
|
sv.statusbar = Z_Malloc(strlen(val)+1);
|
|
|
|
strcpy(sv.statusbar, val);
|
|
|
|
}
|
|
|
|
else if (i == Q2CS_NAME)
|
|
|
|
{
|
2005-01-07 02:34:59 +00:00
|
|
|
Q_strncpyz(sv.mapname, val, sizeof(sv.name));
|
2004-08-23 00:15:46 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Con_Printf("Ignoring configstring %i\n", i);
|
|
|
|
}
|
2005-03-20 02:57:11 +00:00
|
|
|
*/
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
if (sv.state != ss_loading)
|
|
|
|
{ // send the update to everyone
|
2005-07-14 01:57:34 +00:00
|
|
|
SZ_Clear (&sv.q2multicast);
|
|
|
|
MSG_WriteChar (&sv.q2multicast, svcq2_configstring);
|
|
|
|
MSG_WriteShort (&sv.q2multicast, i);
|
|
|
|
MSG_WriteString (&sv.q2multicast, val);
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
SV_Multicast (vec3_origin, MULTICAST_ALL_R);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-03-20 02:57:11 +00:00
|
|
|
static int SVQ2_FindIndex (char *name, int start, int max, qboolean create)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
int i;
|
2005-03-20 02:57:11 +00:00
|
|
|
int stringlength = MAX_QPATH;
|
2006-02-17 02:51:59 +00:00
|
|
|
char *strings = sv.strings.configstring[start];
|
2005-03-20 02:57:11 +00:00
|
|
|
strings += stringlength;
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
if (!name || !name[0])
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i=1 ; i<max && strings[0] ; i++, strings+=stringlength)
|
|
|
|
if (!strcmp(strings, name))
|
|
|
|
return i;
|
|
|
|
|
|
|
|
if (!create)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (i == max)
|
|
|
|
Sys_Error ("*Index: overflow");
|
|
|
|
|
|
|
|
PFQ2_Configstring(start + i, name);
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
static int VARGS SVQ2_ModelIndex (char *name)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
2005-03-20 02:57:11 +00:00
|
|
|
return SVQ2_FindIndex (name, Q2CS_MODELS, Q2MAX_MODELS, true);
|
2004-08-23 00:15:46 +00:00
|
|
|
}
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
static int VARGS SVQ2_SoundIndex (char *name)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
2005-03-20 02:57:11 +00:00
|
|
|
return SVQ2_FindIndex (name, Q2CS_SOUNDS, Q2MAX_SOUNDS, true);
|
2004-08-23 00:15:46 +00:00
|
|
|
}
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
static int VARGS SVQ2_ImageIndex (char *name)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
2005-03-20 02:57:11 +00:00
|
|
|
return SVQ2_FindIndex (name, Q2CS_IMAGES, Q2MAX_IMAGES, true);
|
2004-08-23 00:15:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
PF_setmodel
|
|
|
|
|
|
|
|
Also sets mins and maxs for inline bmodels
|
|
|
|
=================
|
|
|
|
*/
|
2005-03-18 06:13:11 +00:00
|
|
|
static void VARGS PFQ2_setmodel (q2edict_t *ent, char *name)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
model_t *mod;
|
|
|
|
|
|
|
|
if (!name)
|
2006-07-21 23:38:24 +00:00
|
|
|
{
|
2007-09-23 15:28:06 +00:00
|
|
|
Con_Printf (CON_ERROR "ERROR: PF_setmodel: NULL\n");
|
2006-07-21 23:38:24 +00:00
|
|
|
ent->s.modelindex = 0;
|
|
|
|
return;
|
|
|
|
}
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
i = SVQ2_ModelIndex (name);
|
|
|
|
|
|
|
|
// ent->model = name;
|
|
|
|
ent->s.modelindex = i;
|
|
|
|
|
|
|
|
// if it is an inline model, get the size information for it
|
|
|
|
if (name[0] == '*')
|
|
|
|
{
|
|
|
|
mod = Mod_FindName (name);
|
|
|
|
VectorCopy (mod->mins, ent->mins);
|
|
|
|
VectorCopy (mod->maxs, ent->maxs);
|
2009-11-04 21:16:50 +00:00
|
|
|
WorldQ2_LinkEdict (&sv.world, ent);
|
2004-08-23 00:15:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
static qboolean PFQ2_Q1BSP_AreasConnected (int area1, int area2)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static qboolean CMQ2_Q1BSP_SetAreaPortalState (int portalnum, qboolean open)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}*/
|
|
|
|
|
2005-07-14 01:57:34 +00:00
|
|
|
static void VARGS PFQ2_WriteChar (int c) {MSG_WriteChar (&sv.q2multicast, c);}
|
|
|
|
static void VARGS PFQ2_WriteByte (int c) {MSG_WriteByte (&sv.q2multicast, c);}
|
|
|
|
static void VARGS PFQ2_WriteShort (int c) {MSG_WriteShort (&sv.q2multicast, c);}
|
|
|
|
static void VARGS PFQ2_WriteLong (int c) {MSG_WriteLong (&sv.q2multicast, c);}
|
|
|
|
static void VARGS PFQ2_WriteFloat (float f) {MSG_WriteFloat (&sv.q2multicast, f);}
|
|
|
|
static void VARGS PFQ2_WriteString (char *s) {MSG_WriteString (&sv.q2multicast, s);}
|
|
|
|
static void VARGS PFQ2_WriteAngle (float f) {MSG_WriteAngle (&sv.q2multicast, f);}
|
|
|
|
static void VARGS PFQ2_WritePos (vec3_t pos) { MSG_WriteCoord (&sv.q2multicast, pos[0]);
|
|
|
|
MSG_WriteCoord (&sv.q2multicast, pos[1]);
|
|
|
|
MSG_WriteCoord (&sv.q2multicast, pos[2]);
|
2004-08-23 00:15:46 +00:00
|
|
|
}
|
2005-07-14 01:57:34 +00:00
|
|
|
static void VARGS PFQ2_WriteDir (vec3_t dir) {MSG_WriteDir (&sv.q2multicast, dir);}
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
PF_inPVS
|
|
|
|
|
|
|
|
Also checks portalareas so that doors block sight
|
|
|
|
=================
|
|
|
|
*/
|
2005-03-18 06:13:11 +00:00
|
|
|
static qboolean VARGS PFQ2_inPVS (vec3_t p1, vec3_t p2)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
int leafnum;
|
|
|
|
int cluster;
|
|
|
|
int area1, area2;
|
|
|
|
qbyte *mask;
|
|
|
|
|
2009-11-04 21:16:50 +00:00
|
|
|
leafnum = CM_PointLeafnum (sv.world.worldmodel, p1);
|
|
|
|
cluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
|
|
|
|
area1 = CM_LeafArea (sv.world.worldmodel, leafnum);
|
|
|
|
mask = CM_ClusterPVS (sv.world.worldmodel, cluster, NULL, 0);
|
2005-08-26 22:56:51 +00:00
|
|
|
|
2009-11-04 21:16:50 +00:00
|
|
|
leafnum = CM_PointLeafnum (sv.world.worldmodel, p2);
|
|
|
|
cluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
|
|
|
|
area2 = CM_LeafArea (sv.world.worldmodel, leafnum);
|
2004-08-23 00:15:46 +00:00
|
|
|
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
|
|
|
|
return false;
|
2009-11-04 21:16:50 +00:00
|
|
|
if (!CM_AreasConnected (sv.world.worldmodel, area1, area2))
|
2004-08-23 00:15:46 +00:00
|
|
|
return false; // a door blocks sight
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
PF_inPHS
|
|
|
|
|
|
|
|
Also checks portalareas so that doors block sound
|
|
|
|
=================
|
|
|
|
*/
|
2005-03-18 06:13:11 +00:00
|
|
|
static qboolean VARGS PFQ2_inPHS (vec3_t p1, vec3_t p2)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
int leafnum;
|
|
|
|
int cluster;
|
|
|
|
int area1, area2;
|
|
|
|
qbyte *mask;
|
|
|
|
|
2009-11-04 21:16:50 +00:00
|
|
|
leafnum = CM_PointLeafnum (sv.world.worldmodel, p1);
|
|
|
|
cluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
|
|
|
|
area1 = CM_LeafArea (sv.world.worldmodel, leafnum);
|
|
|
|
mask = CM_ClusterPHS (sv.world.worldmodel, cluster);
|
2004-08-23 00:15:46 +00:00
|
|
|
|
2009-11-04 21:16:50 +00:00
|
|
|
leafnum = CM_PointLeafnum (sv.world.worldmodel, p2);
|
|
|
|
cluster = CM_LeafCluster (sv.world.worldmodel, leafnum);
|
|
|
|
area2 = CM_LeafArea (sv.world.worldmodel, leafnum);
|
2004-08-23 00:15:46 +00:00
|
|
|
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
|
|
|
|
return false; // more than one bounce away
|
2009-11-04 21:16:50 +00:00
|
|
|
if (!CM_AreasConnected (sv.world.worldmodel, area1, area2))
|
2004-08-23 00:15:46 +00:00
|
|
|
return false; // a door blocks hearing
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2005-08-26 22:56:51 +00:00
|
|
|
qboolean VARGS PFQ2_AreasConnected(int area1, int area2)
|
|
|
|
{
|
2009-11-04 21:16:50 +00:00
|
|
|
return CM_AreasConnected(sv.world.worldmodel, area1, area2);
|
2005-08-26 22:56:51 +00:00
|
|
|
}
|
|
|
|
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
#define Q2SND_VOLUME (1<<0) // a byte
|
|
|
|
#define Q2SND_ATTENUATION (1<<1) // a byte
|
|
|
|
#define Q2SND_POS (1<<2) // three coordinates
|
|
|
|
#define Q2SND_ENT (1<<3) // a short 0-2: channel, 3-12: entity
|
|
|
|
#define Q2SND_OFFSET (1<<4) // a byte, msec offset from frame start
|
|
|
|
|
|
|
|
#define Q2DEFAULT_SOUND_PACKET_VOLUME 1.0
|
|
|
|
#define Q2DEFAULT_SOUND_PACKET_ATTENUATION 1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define Q2ATTN_NONE 0 // full volume the entire level
|
|
|
|
#define Q2ATTN_NORM 1/*
|
|
|
|
#define Q2CHAN_AUTO 0
|
|
|
|
#define Q2CHAN_WEAPON 1
|
|
|
|
#define Q2CHAN_VOICE 2
|
|
|
|
#define Q2CHAN_ITEM 3
|
|
|
|
#define Q2CHAN_BODY 4*/
|
2005-09-07 14:55:25 +00:00
|
|
|
#define Q2CHAN_NO_PHS_ADD 8
|
2004-08-23 00:15:46 +00:00
|
|
|
#define Q2CHAN_RELIABLE 16
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
void VARGS SVQ2_StartSound (vec3_t origin, q2edict_t *entity, int channel,
|
2004-08-23 00:15:46 +00:00
|
|
|
int soundindex, float volume,
|
|
|
|
float attenuation, float timeofs)
|
|
|
|
{
|
|
|
|
int sendchan;
|
|
|
|
int flags;
|
|
|
|
int i;
|
|
|
|
int ent;
|
|
|
|
vec3_t origin_v;
|
|
|
|
qboolean use_phs;
|
|
|
|
|
|
|
|
if (volume < 0 || volume > 1.0)
|
|
|
|
Sys_Error ("SV_StartSound: volume = %f", volume);
|
|
|
|
|
|
|
|
if (attenuation < 0 || attenuation > 4)
|
|
|
|
Sys_Error ("SV_StartSound: attenuation = %f", attenuation);
|
|
|
|
|
|
|
|
// if (channel < 0 || channel > 15)
|
|
|
|
// Sys_Error ("SV_StartSound: channel = %i", channel);
|
|
|
|
|
|
|
|
if (timeofs < 0 || timeofs > 0.255)
|
|
|
|
Sys_Error ("SV_StartSound: timeofs = %f", timeofs);
|
|
|
|
|
|
|
|
ent = Q2NUM_FOR_EDICT(entity);
|
|
|
|
|
2005-09-07 14:55:25 +00:00
|
|
|
if (channel & Q2CHAN_NO_PHS_ADD) // no PHS flag
|
2004-08-23 00:15:46 +00:00
|
|
|
use_phs = false;
|
|
|
|
else
|
|
|
|
use_phs = true;
|
|
|
|
|
|
|
|
sendchan = (ent<<3) | (channel&7);
|
|
|
|
|
|
|
|
flags = 0;
|
|
|
|
if (volume != Q2DEFAULT_SOUND_PACKET_VOLUME)
|
|
|
|
flags |= Q2SND_VOLUME;
|
|
|
|
if (attenuation != Q2DEFAULT_SOUND_PACKET_ATTENUATION)
|
|
|
|
flags |= Q2SND_ATTENUATION;
|
|
|
|
|
|
|
|
// the client doesn't know that bmodels have weird origins
|
|
|
|
// the origin can also be explicitly set
|
|
|
|
if ( (entity->svflags & SVF_NOCLIENT)
|
|
|
|
|| (entity->solid == Q2SOLID_BSP)
|
|
|
|
|| origin )
|
|
|
|
flags |= Q2SND_POS;
|
|
|
|
|
|
|
|
// always send the entity number for channel overrides
|
|
|
|
flags |= Q2SND_ENT;
|
|
|
|
|
|
|
|
if (timeofs)
|
|
|
|
flags |= Q2SND_OFFSET;
|
|
|
|
|
|
|
|
// use the entity origin unless it is a bmodel or explicitly specified
|
|
|
|
if (!origin)
|
|
|
|
{
|
|
|
|
origin = origin_v;
|
|
|
|
if (entity->solid == Q2SOLID_BSP)
|
|
|
|
{
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
|
|
origin_v[i] = entity->s.origin[i]+0.5*(entity->mins[i]+entity->maxs[i]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VectorCopy (entity->s.origin, origin_v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-14 01:57:34 +00:00
|
|
|
MSG_WriteByte (&sv.q2multicast, svcq2_sound);
|
|
|
|
MSG_WriteByte (&sv.q2multicast, flags);
|
|
|
|
MSG_WriteByte (&sv.q2multicast, soundindex);
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
if (flags & Q2SND_VOLUME)
|
2005-07-14 01:57:34 +00:00
|
|
|
MSG_WriteByte (&sv.q2multicast, volume*255);
|
2004-08-23 00:15:46 +00:00
|
|
|
if (flags & Q2SND_ATTENUATION)
|
2005-07-14 01:57:34 +00:00
|
|
|
MSG_WriteByte (&sv.q2multicast, attenuation*64);
|
2004-08-23 00:15:46 +00:00
|
|
|
if (flags & Q2SND_OFFSET)
|
2005-07-14 01:57:34 +00:00
|
|
|
MSG_WriteByte (&sv.q2multicast, timeofs*1000);
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
if (flags & Q2SND_ENT)
|
2005-07-14 01:57:34 +00:00
|
|
|
MSG_WriteShort (&sv.q2multicast, sendchan);
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
if (flags & Q2SND_POS)
|
|
|
|
{
|
2005-07-14 01:57:34 +00:00
|
|
|
MSG_WriteCoord (&sv.q2multicast, origin[0]);
|
|
|
|
MSG_WriteCoord (&sv.q2multicast, origin[1]);
|
|
|
|
MSG_WriteCoord (&sv.q2multicast, origin[2]);
|
2004-08-23 00:15:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if the sound doesn't attenuate,send it to everyone
|
|
|
|
// (global radio chatter, voiceovers, etc)
|
|
|
|
if (attenuation == Q2ATTN_NONE)
|
|
|
|
use_phs = false;
|
|
|
|
|
|
|
|
if (channel & Q2CHAN_RELIABLE)
|
|
|
|
{
|
|
|
|
if (use_phs)
|
|
|
|
SV_Multicast (origin, MULTICAST_PHS_R);
|
|
|
|
else
|
|
|
|
SV_Multicast (origin, MULTICAST_ALL_R);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (use_phs)
|
|
|
|
SV_Multicast (origin, MULTICAST_PHS);
|
|
|
|
else
|
|
|
|
SV_Multicast (origin, MULTICAST_ALL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
static void VARGS PFQ2_StartSound (q2edict_t *entity, int channel, int sound_num, float volume,
|
2004-08-23 00:15:46 +00:00
|
|
|
float attenuation, float timeofs)
|
|
|
|
{
|
|
|
|
if (!entity)
|
|
|
|
return;
|
|
|
|
SVQ2_StartSound (NULL, entity, channel, sound_num, volume, attenuation, timeofs);
|
|
|
|
}
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
static q2trace_t VARGS SVQ2_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, q2edict_t *passedict, int contentmask)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
q2trace_t ret;
|
|
|
|
trace_t tr;
|
|
|
|
static vec3_t nullvec;
|
|
|
|
if (!mins)
|
|
|
|
mins = nullvec;
|
|
|
|
if (!maxs)
|
|
|
|
maxs = nullvec;
|
2009-11-04 21:16:50 +00:00
|
|
|
tr = WorldQ2_Move(&sv.world, start, mins, maxs, end, contentmask, passedict);
|
2004-08-23 00:15:46 +00:00
|
|
|
memcpy(&ret, &tr, sizeof(q2trace_t));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
static int VARGS SVQ2_PointContents (vec3_t p)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
q2trace_t tr = SVQ2_Trace(p, NULL, NULL, p, NULL, ~0);
|
|
|
|
return tr.contents;
|
|
|
|
// return CM_PointContents(p, 0);
|
|
|
|
}
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
static cvar_t *VARGS Q2Cvar_Get (char *var_name, char *value, int flags)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
return Cvar_Get(var_name, value, flags, "Quake2 game variables");
|
|
|
|
}
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
cvar_t *VARGS Q2Cvar_Set (char *var_name, char *value)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
cvar_t *var = Cvar_FindVar(var_name);
|
|
|
|
if (!var)
|
|
|
|
{
|
|
|
|
Con_Printf("Q2Cvar_Set: variable %s not found\n", var_name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return Cvar_Set(var, value);
|
|
|
|
}
|
2005-03-18 06:13:11 +00:00
|
|
|
cvar_t *VARGS Q2Cvar_ForceSet (char *var_name, char *value)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
cvar_t *var = Cvar_FindVar(var_name);
|
|
|
|
if (!var)
|
|
|
|
{
|
2009-10-06 00:37:47 +00:00
|
|
|
Con_Printf("Q2Cvar_Set: variable %s not found\n", var_name);
|
2004-08-23 00:15:46 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return Cvar_ForceSet(var, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
SV_ShutdownGameProgs
|
|
|
|
|
|
|
|
Called when either the entire server is being killed, or
|
|
|
|
it is changing to a different game directory.
|
|
|
|
===============
|
|
|
|
*/
|
2005-03-18 06:13:11 +00:00
|
|
|
void VARGS SVQ2_ShutdownGameProgs (void)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
if (!ge)
|
|
|
|
return;
|
|
|
|
ge->Shutdown ();
|
|
|
|
Sys_UnloadGame ();
|
|
|
|
ge = NULL;
|
|
|
|
}
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
static void VARGS AddCommandString(char *command)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
Cbuf_AddText(command, RESTRICT_LOCAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
SV_InitGameProgs
|
|
|
|
|
|
|
|
Init the game subsystem for a new map
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
void VARGS Q2SCR_DebugGraph(float value, int color)
|
2004-08-23 00:15:46 +00:00
|
|
|
{return;}
|
|
|
|
|
2009-11-04 21:16:50 +00:00
|
|
|
static void VARGS SVQ2_LinkEdict (q2edict_t *ent)
|
|
|
|
{
|
|
|
|
WorldQ2_LinkEdict(&sv.world, ent);
|
|
|
|
}
|
|
|
|
static void VARGS SVQ2_UnlinkEdict (q2edict_t *ent)
|
|
|
|
{
|
|
|
|
WorldQ2_UnlinkEdict(&sv.world, ent);
|
|
|
|
}
|
|
|
|
static int VARGS SVQ2_AreaEdicts (vec3_t mins, vec3_t maxs, q2edict_t **list, int maxcount, int areatype)
|
|
|
|
{
|
|
|
|
return WorldQ2_AreaEdicts(&sv.world, mins, maxs, list, maxcount, areatype);
|
|
|
|
}
|
|
|
|
|
2010-07-11 02:22:39 +00:00
|
|
|
static model_t *SVQ2_GetCModel(world_t *w, int modelindex)
|
|
|
|
{
|
|
|
|
if ((unsigned int)modelindex < MAX_MODELS)
|
|
|
|
return sv.models[modelindex];
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SVQ2_InitWorld(void)
|
|
|
|
{
|
|
|
|
sv.world.GetCModel = SVQ2_GetCModel;
|
|
|
|
}
|
|
|
|
|
2004-08-23 00:15:46 +00:00
|
|
|
qboolean SVQ2_InitGameProgs(void)
|
|
|
|
{
|
2005-03-18 06:13:11 +00:00
|
|
|
volatile static game_import_t import; //volatile because msvc sucks
|
2004-08-23 00:15:46 +00:00
|
|
|
if (COM_CheckParm("-noq2dll"))
|
|
|
|
{
|
|
|
|
SVQ2_ShutdownGameProgs();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// unload anything we have now
|
2009-11-04 21:16:50 +00:00
|
|
|
if (sv.world.worldmodel->fromgame == fg_quake || sv.world.worldmodel->fromgame == fg_halflife) //we don't support q1 or hl maps yet... If ever.
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
SVQ2_ShutdownGameProgs();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ge)
|
2010-07-11 02:22:39 +00:00
|
|
|
{
|
|
|
|
SVQ2_InitWorld();
|
2004-08-23 00:15:46 +00:00
|
|
|
return true;
|
2010-07-11 02:22:39 +00:00
|
|
|
}
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
// calc the imports.
|
|
|
|
import.multicast = SV_Multicast;
|
|
|
|
import.unicast = PFQ2_Unicast;
|
|
|
|
import.bprintf = SV_BroadcastPrintf;
|
|
|
|
import.dprintf = PFQ2_dprintf;
|
|
|
|
import.cprintf = PFQ2_cprintf;
|
|
|
|
import.centerprintf = PFQ2_centerprintf;
|
|
|
|
import.error = PFQ2_error;
|
|
|
|
|
|
|
|
import.linkentity = SVQ2_LinkEdict;
|
|
|
|
import.unlinkentity = SVQ2_UnlinkEdict;
|
|
|
|
import.BoxEdicts = SVQ2_AreaEdicts;
|
|
|
|
import.trace = SVQ2_Trace;
|
|
|
|
import.pointcontents = SVQ2_PointContents;
|
|
|
|
import.setmodel = PFQ2_setmodel;
|
|
|
|
import.inPVS = PFQ2_inPVS;
|
|
|
|
import.inPHS = PFQ2_inPHS;
|
|
|
|
import.Pmove = Q2_Pmove;
|
|
|
|
|
|
|
|
import.modelindex = SVQ2_ModelIndex;
|
|
|
|
import.soundindex = SVQ2_SoundIndex;
|
|
|
|
import.imageindex = SVQ2_ImageIndex;
|
|
|
|
|
|
|
|
import.configstring = PFQ2_Configstring;
|
|
|
|
import.sound = PFQ2_StartSound;
|
|
|
|
import.positioned_sound = SVQ2_StartSound;
|
|
|
|
|
|
|
|
import.WriteChar = PFQ2_WriteChar;
|
|
|
|
import.WriteByte = PFQ2_WriteByte;
|
|
|
|
import.WriteShort = PFQ2_WriteShort;
|
|
|
|
import.WriteLong = PFQ2_WriteLong;
|
|
|
|
import.WriteFloat = PFQ2_WriteFloat;
|
|
|
|
import.WriteString = PFQ2_WriteString;
|
|
|
|
import.WritePosition = PFQ2_WritePos;
|
|
|
|
import.WriteDir = PFQ2_WriteDir;
|
|
|
|
import.WriteAngle = PFQ2_WriteAngle;
|
|
|
|
|
|
|
|
import.TagMalloc = Z_TagMalloc;
|
2008-05-09 14:22:37 +00:00
|
|
|
import.TagFree = Z_TagFree;
|
2004-08-23 00:15:46 +00:00
|
|
|
import.FreeTags = Z_FreeTags;
|
|
|
|
|
|
|
|
import.cvar = Q2Cvar_Get;
|
|
|
|
import.cvar_set = Q2Cvar_Set;
|
|
|
|
import.cvar_forceset = Q2Cvar_ForceSet;
|
|
|
|
|
|
|
|
import.argc = Cmd_Argc;
|
|
|
|
import.argv = Cmd_Argv;
|
|
|
|
import.args = Cmd_Args;
|
|
|
|
import.AddCommandString = AddCommandString;
|
|
|
|
|
|
|
|
import.DebugGraph = Q2SCR_DebugGraph;
|
|
|
|
import.SetAreaPortalState = CMQ2_SetAreaPortalState;
|
2005-08-26 22:56:51 +00:00
|
|
|
import.AreasConnected = PFQ2_AreasConnected;
|
2004-08-23 00:15:46 +00:00
|
|
|
|
2009-11-04 21:16:50 +00:00
|
|
|
if (sv.world.worldmodel->fromgame == fg_quake || sv.world.worldmodel->fromgame == fg_halflife)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
/*
|
|
|
|
import.linkentity = SVQ2_Q1BSP_LinkEdict;
|
|
|
|
import.unlinkentity = SVQ2_Q1BSP_UnlinkEdict;
|
|
|
|
import.BoxEdicts = SVQ2_Q1BSP_AreaEdicts;
|
|
|
|
import.trace = SVQ2_Q1BSP_Trace;
|
|
|
|
import.pointcontents = SVQ2_Q1BSP_PointContents;
|
|
|
|
import.setmodel = PFQ2_Q1BSP_setmodel;
|
|
|
|
import.inPVS = PFQ2_Q1BSP_inPVS;
|
|
|
|
import.inPHS = PFQ2_Q1BSP_inPHS;
|
|
|
|
import.Pmove = Q2_Pmove;
|
|
|
|
|
|
|
|
import.AreasConnected = PFQ2_Q1BSP_AreasConnected;
|
|
|
|
import.SetAreaPortalState = CMQ2_Q1BSP_SetAreaPortalState;
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2005-03-18 06:13:11 +00:00
|
|
|
ge = (game_export_t *)Sys_GetGameAPI ((game_import_t*)&import);
|
2004-08-23 00:15:46 +00:00
|
|
|
|
|
|
|
if (!ge)
|
|
|
|
return false;
|
2005-03-07 08:58:26 +00:00
|
|
|
if (ge->apiversion != Q2GAME_API_VERSION)
|
2004-08-23 00:15:46 +00:00
|
|
|
{
|
2005-03-07 08:58:26 +00:00
|
|
|
Con_Printf("game is version %i, not %i", ge->apiversion, Q2GAME_API_VERSION);
|
2010-03-14 14:35:56 +00:00
|
|
|
Sys_UnloadGame ();
|
2004-08-23 00:15:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-07-11 02:22:39 +00:00
|
|
|
SVQ2_InitWorld();
|
2004-08-23 00:15:46 +00:00
|
|
|
ge->Init ();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|