mirror of
https://github.com/bearstonem/openmohaa-vr.git
synced 2026-10-28 09:00:29 +01:00
Four interactions that all needed the same thing: the hands' positions and velocities on the other side of the game module boundary. They share the plumbing, so they share a commit. use moves to B on the weapon hand, where the reference puts activate. The objectives list keeps the left hand's button; it had been bound to both, which was a button spent twice on one rarely opened screen. The trace also starts from the head's real position now rather than the body's notional eye - the two come apart exactly when leaning through a doorway to reach the thing on the far side. Weapons live on the body, the way Half-Life: Alyx does it: pistol at the right hip, grenades at the left, rifle and smg over the shoulders. Reaching back to where a weapon came from puts it away, reaching anywhere else swaps - holstering on any zone would have cost two gestures for every swap. Zones are measured from the head and rotated by the head's heading, so they stay on the body rather than wandering as the player turns. Ladders are climbed by pulling on them. MOHAA moves a climbing player by the root motion of the climb animation, with pmove skipped entirely, so this runs after the animation rather than instead of it: the animation still plays and the distance travelled is the distance the hand pulled. Both grips, averaged when both are held, and the grips stop meaning reload and rifle-steady while on a ladder. Grenades leave along the arm. Which instant of a swing to read is the whole problem and took several passes: averaging across the release blends the wind up with the follow through; the peak of the whole swing is usually the *backswing*, which threw grenades over the player's shoulder. What works is the natural gesture's own shape - take the direction the hand had as it let go, then find the fastest sample that agrees with it, which excludes the wind up by construction. Strength is a fraction of the game's own throw so all of MOHAA's grenade tuning survives. The sideways damping is the one deliberate lie. An underhand swing crosses the body, so the hand really is moving sideways at release - measured, lobs drifted left by up to 0.73, more sideways than forward. Damped in proportion to how gentle the throw is, so hard throws keep their true direction. A weapon wheel is included and is not finished: its input works and a tap of the same stick holsters, but the ring never appeared on screen and the drawing was not chased down. It is harmless - a hold selects nothing and does nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2083 lines
43 KiB
C
2083 lines
43 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code 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.
|
|
|
|
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
// sv_game.c -- interface to the game dll
|
|
|
|
#include "server.h"
|
|
|
|
#include "../client/snd_public.h"
|
|
#include "../client/client.h"
|
|
#if defined(USE_OPENXR) && !defined(DEDICATED)
|
|
#include "../vr/vr_common.h"
|
|
#endif
|
|
#include "../corepp/tiki.h"
|
|
#include "../qcommon/localization.h"
|
|
#include "../qcommon/crc.h"
|
|
#include "../qcommon/alias.h"
|
|
|
|
debugline_t *DebugLines;
|
|
int numDebugLines;
|
|
debugstring_t *DebugStrings;
|
|
int numDebugStrings;
|
|
|
|
static int modelUserCount[ MAX_MODELS ];
|
|
|
|
void SV_GameError( const char *string ) {
|
|
Com_Error( ERR_DROP, "%s", string );
|
|
}
|
|
|
|
void SV_GamePrint( const char *string ) {
|
|
Com_Printf( "%s", string );
|
|
}
|
|
|
|
// these functions must be used instead of pointer arithmetic, because
|
|
// the game allocates gentities with private information after the server shared part
|
|
int SV_NumForGentity( gentity_t *ent ) {
|
|
int num;
|
|
|
|
num = ( (byte *)ent - (byte *)sv.gentities ) / sv.gentitySize;
|
|
|
|
return num;
|
|
}
|
|
|
|
gentity_t *SV_GentityNum( int num ) {
|
|
gentity_t *ent;
|
|
|
|
ent = (gentity_t *)((byte *)sv.gentities + sv.gentitySize*(num));
|
|
|
|
return ent;
|
|
}
|
|
|
|
playerState_t *SV_GameClientNum( int num ) {
|
|
playerState_t *ps;
|
|
|
|
ps = (playerState_t *)((byte *)sv.gameClients + sv.gameClientSize*(num));
|
|
|
|
return ps;
|
|
}
|
|
|
|
svEntity_t *SV_SvEntityForGentity( gentity_t *gEnt ) {
|
|
if ( !gEnt || gEnt->s.number < 0 || gEnt->s.number >= MAX_GENTITIES ) {
|
|
Com_Error( ERR_DROP, "SV_SvEntityForGentity: bad gEnt" );
|
|
}
|
|
return &sv.svEntities[ gEnt->s.number ];
|
|
}
|
|
|
|
gentity_t *SV_GEntityForSvEntity( svEntity_t *svEnt ) {
|
|
int num;
|
|
|
|
num = svEnt - sv.svEntities;
|
|
return SV_GentityNum( num );
|
|
}
|
|
|
|
// su44: MoHAA cg messages sending
|
|
typedef struct {
|
|
byte *data;
|
|
int cursize;
|
|
byte *datatypes;
|
|
int dtindex;
|
|
} cgm_t;
|
|
|
|
cgm_t g_CGMessages[MAX_CLIENTS];
|
|
qboolean g_CGMRecieve[MAX_CLIENTS] = { qfalse };
|
|
int nextTime[MAX_CLIENTS] = { 0 };
|
|
|
|
void SV_ClearCGMessage (int iClient)
|
|
{
|
|
g_CGMessages[iClient].cursize = 0;
|
|
g_CGMessages[iClient].dtindex = 0;
|
|
if(g_CGMessages[iClient].data) {
|
|
Z_Free(g_CGMessages[iClient].data);
|
|
g_CGMessages[iClient].data = 0;
|
|
}
|
|
if(g_CGMessages[iClient].datatypes) {
|
|
Z_Free(g_CGMessages[iClient].datatypes);
|
|
g_CGMessages[iClient].datatypes = 0;
|
|
}
|
|
g_CGMRecieve[iClient] = 0;
|
|
}
|
|
|
|
void SV_ClearAllCGMessages ()
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < MAX_CLIENTS; i++,pCGM++) {
|
|
pCGM->cursize = 0;
|
|
pCGM->dtindex = 0;
|
|
if(pCGM->data) {
|
|
Z_Free(pCGM->data);
|
|
pCGM->data = 0;
|
|
}
|
|
if(pCGM->datatypes) {
|
|
Z_Free(pCGM->datatypes);
|
|
pCGM->datatypes = 0;
|
|
}
|
|
g_CGMRecieve[i] = 0;
|
|
}
|
|
}
|
|
|
|
#define CGM_DATA_SIZE 4096
|
|
#define CGM_DATATYPES_SIZE 8192
|
|
|
|
void SV_InitCGMessage (int iClient)
|
|
{
|
|
cgm_t *pCGM;
|
|
|
|
pCGM = &g_CGMessages[iClient];
|
|
|
|
if ( pCGM->data == 0 )
|
|
pCGM->data = Z_Malloc(CGM_DATA_SIZE);
|
|
|
|
if ( pCGM->datatypes == 0 )
|
|
pCGM->datatypes = Z_Malloc(CGM_DATA_SIZE);
|
|
|
|
pCGM->cursize = 0;
|
|
pCGM->dtindex = 0;
|
|
|
|
g_CGMRecieve[iClient] = 0;
|
|
}
|
|
|
|
void SV_InitAllCGMessages ()
|
|
{
|
|
int i;
|
|
|
|
SV_ClearAllCGMessages();
|
|
for ( i = 0; i < svs.iNumClients; ++i )
|
|
SV_InitCGMessage(i);
|
|
}
|
|
|
|
static void MSG_HandleCGMBufferOverflow(cgm_t* pCGM, qboolean isDatum) {
|
|
int index;
|
|
|
|
index = pCGM - g_CGMessages;
|
|
if (!nextTime[index] || nextTime[index] < svs.time)
|
|
{
|
|
if (isDatum) {
|
|
Com_DPrintf("CGM buffer for client %i overflowed number of datum\n", index);
|
|
} else {
|
|
Com_DPrintf("CGM buffer for client %i overflowed size of data\n", index);
|
|
}
|
|
nextTime[index] = svs.time + 5000;
|
|
}
|
|
}
|
|
|
|
static void MSG_WriteCGMBits (cgm_t *pCGM, int value, int bits)
|
|
{
|
|
if (CGM_DATA_SIZE - pCGM->cursize < 4) {
|
|
MSG_HandleCGMBufferOverflow(pCGM, qfalse);
|
|
return;
|
|
}
|
|
if(CGM_DATATYPES_SIZE - pCGM->dtindex < 1) {
|
|
MSG_HandleCGMBufferOverflow(pCGM, qtrue);
|
|
return;
|
|
}
|
|
|
|
if (bits > 32)
|
|
Com_Error(1, "PF_MSG_WriteBits: bad bits %i", bits);
|
|
|
|
if(bits < 0)
|
|
bits = -bits;
|
|
|
|
if(bits <= 8) {
|
|
// append single byte
|
|
pCGM->data[pCGM->cursize] = value;
|
|
pCGM->cursize++;
|
|
} else if(bits <= 16) {
|
|
// append short (two bytes)
|
|
*((short*)(&pCGM->data[pCGM->cursize])) = value;
|
|
pCGM->cursize+=2;
|
|
} else if(bits <= 32) {
|
|
// append integer (4 bytes)
|
|
*((int*)(&pCGM->data[pCGM->cursize])) = value;
|
|
pCGM->cursize+=4;
|
|
}
|
|
// save datatype
|
|
pCGM->datatypes[pCGM->dtindex] = bits;
|
|
pCGM->dtindex++;
|
|
}
|
|
|
|
void PF_MSG_WriteBits (int value, int bits)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
pCGM = g_CGMessages;
|
|
for( i = 0; i < svs.iNumClients; i++, pCGM++ ) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
MSG_WriteCGMBits(pCGM,value,bits);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteChar (int c)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
// write 8 bit character
|
|
MSG_WriteCGMBits(pCGM,c,8);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteByte (int c)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
// write 8 bit unsigned byte
|
|
MSG_WriteCGMBits(pCGM,c,8);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteSVC (int c)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
MSG_WriteCGMBits(pCGM,c,8);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteShort (int c)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
MSG_WriteCGMBits(pCGM,c,16);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteLong (int c)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
MSG_WriteCGMBits(pCGM,c,32);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteFloat (float f)
|
|
{
|
|
union {
|
|
float fl;
|
|
int l;
|
|
} dat;
|
|
cgm_t *pCGM;
|
|
int i;
|
|
dat.fl = f;
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
MSG_WriteCGMBits(pCGM,dat.l,32);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteString (const char *s)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
size_t j, l;
|
|
l = strlen(s);
|
|
l+=1; // include trailing zero
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
for(j = 0; j < l; j++) {
|
|
MSG_WriteCGMBits(pCGM,s[j],8);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteAngle8 (float f)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
MSG_WriteCGMBits(pCGM,f * 256.0 / 360.0,8);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteAngle16 (float f)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
MSG_WriteCGMBits(pCGM,ANGLE2SHORT(f),16);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteCoord (float f)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
|
|
// 19th bit is a sign
|
|
int write = abs((int)(f * 16.f));
|
|
if(f < 0) {
|
|
write |= 262144;
|
|
} else {
|
|
write &= ~262144;
|
|
}
|
|
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
MSG_WriteCGMBits(pCGM, write, 19);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_WriteDir (vec_t *dir)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
byte b;
|
|
b = DirToByte(dir);
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
MSG_WriteCGMBits(pCGM,b,8);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_StartCGM (int type)
|
|
{
|
|
cgm_t *pCGM;
|
|
int i;
|
|
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
if(g_CGMRecieve[i] == 0)
|
|
continue;
|
|
if(pCGM->cursize <= 0) {
|
|
// always put "svc_cgameMessage" byte at the beginning of CGM block
|
|
MSG_WriteCGMBits(pCGM,svc_cgameMessage,8);
|
|
} else {
|
|
// single bit means that there is one more message in block
|
|
MSG_WriteCGMBits(pCGM, 1, 1);
|
|
}
|
|
// write CG message type
|
|
MSG_WriteCGMBits(pCGM, type, 6);
|
|
}
|
|
}
|
|
|
|
void PF_MSG_EndCGM ()
|
|
{
|
|
memset(g_CGMRecieve,0,sizeof(g_CGMRecieve));
|
|
}
|
|
|
|
void PF_MSG_SetClient (int iClient)
|
|
{
|
|
memset(g_CGMRecieve,0,sizeof(g_CGMRecieve));
|
|
if(g_CGMessages[iClient].data && (g_CGMessages[iClient].cursize <= 3967))
|
|
{
|
|
g_CGMRecieve[iClient] = 1;
|
|
}
|
|
}
|
|
|
|
void MSG_SetBroadcastAll()
|
|
{
|
|
cgm_t *pCGM;
|
|
client_t* client;
|
|
int i;
|
|
|
|
pCGM = g_CGMessages;
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++) {
|
|
client = &svs.clients[i];
|
|
|
|
if (!client->gentity || !client->gentity->inuse) {
|
|
continue;
|
|
}
|
|
|
|
if (client->state == CS_FREE) {
|
|
continue;
|
|
}
|
|
|
|
if (!pCGM->data || pCGM->cursize >= 3968) {
|
|
MSG_HandleCGMBufferOverflow(pCGM, qfalse);
|
|
continue;
|
|
}
|
|
|
|
g_CGMRecieve[i] = qtrue;
|
|
}
|
|
}
|
|
|
|
void MSG_SetBroadcastVisible(const vec_t* vPos, const vec_t* vPosB)
|
|
{
|
|
byte *clientpvs;
|
|
int posBcluster;
|
|
int poscluster;
|
|
int posBarea;
|
|
int posarea;
|
|
int posBleaf;
|
|
int posleaf;
|
|
int clientcluster;
|
|
int clientarea;
|
|
int leafnum;
|
|
client_t *pClient;
|
|
int i;
|
|
cgm_t *pCGM;
|
|
|
|
posleaf = CM_PointLeafnum(vPos);
|
|
posarea = CM_LeafArea(posleaf);
|
|
poscluster = CM_LeafCluster(posleaf);
|
|
|
|
if (vPosB) {
|
|
posBleaf = CM_PointLeafnum(vPosB);
|
|
posBarea = CM_LeafArea(posBleaf);
|
|
posBcluster = CM_LeafCluster(posBleaf);
|
|
} else {
|
|
posBarea = 0;
|
|
posBcluster = 0;
|
|
}
|
|
|
|
pCGM = g_CGMessages;
|
|
pClient = svs.clients;
|
|
|
|
for(i = 0; i < svs.iNumClients; i++,pCGM++,pClient++) {
|
|
if (!pClient->gentity || !pClient->gentity->inuse) {
|
|
// The entity can be null on map load
|
|
continue;
|
|
}
|
|
|
|
if(pClient->state == CS_FREE) {
|
|
continue;
|
|
}
|
|
|
|
leafnum = CM_PointLeafnum(pClient->gentity->s.origin);
|
|
clientarea = CM_LeafArea(leafnum);
|
|
clientcluster = CM_LeafCluster(leafnum);
|
|
clientpvs = CM_ClusterPVS(clientcluster);
|
|
|
|
if (!CM_AreasConnected(clientarea, posarea) && (!vPosB || !CM_AreasConnected(clientarea, posBarea))) {
|
|
continue;
|
|
}
|
|
|
|
if (!(clientpvs[poscluster >> 3] & (1 << (poscluster & 7))) && (!vPosB || !(clientpvs[posBcluster >> 3] & (1 << (posBcluster & 7))))) {
|
|
continue;
|
|
}
|
|
|
|
if (!pCGM->data || pCGM->cursize >= 3968) {
|
|
MSG_HandleCGMBufferOverflow(pCGM, qfalse);
|
|
continue;
|
|
}
|
|
|
|
g_CGMRecieve[i] = qtrue;
|
|
}
|
|
}
|
|
void MSG_SetBroadcastHearable ( const vec_t *vPos, const vec_t *vPosB)
|
|
{
|
|
// su44: in MoHAA's, MSG_SetBroadcastAll is used here,
|
|
// but I'd rather use MSG_SetBroadcastVisible with
|
|
// PHS instead of PVS data
|
|
MSG_SetBroadcastAll();
|
|
}
|
|
void SV_WriteCGMToClient (client_t *client, msg_t *msg)
|
|
{
|
|
// byte *pBuffer;
|
|
cgm_t *pCGMMsg;
|
|
// int i;
|
|
int clientNum;
|
|
|
|
clientNum = client - svs.clients;;
|
|
pCGMMsg = &g_CGMessages[clientNum];
|
|
if ( pCGMMsg->data )
|
|
{
|
|
if ( pCGMMsg->cursize > 0 )
|
|
{
|
|
int curDTIndex;
|
|
byte *data;
|
|
// write end of message - single false bit
|
|
MSG_WriteCGMBits(pCGMMsg, 0, 1);
|
|
data = pCGMMsg->data;
|
|
curDTIndex = 0;
|
|
for(curDTIndex = 0; curDTIndex < pCGMMsg->dtindex; curDTIndex++) {
|
|
int bits = pCGMMsg->datatypes[curDTIndex];
|
|
if(bits <= 8) {
|
|
MSG_WriteBits(msg, *data, bits);
|
|
data++;
|
|
} else if(bits <= 16) {
|
|
MSG_WriteBits(msg, *(short*)data, bits);
|
|
data+=2;
|
|
} else if(bits <= 32) {
|
|
MSG_WriteBits(msg, *(int*)data, bits);
|
|
data+=4;
|
|
}
|
|
|
|
}
|
|
pCGMMsg->cursize = 0;
|
|
pCGMMsg->dtindex = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SV_inPVS
|
|
|
|
Also checks portalareas so that doors block sight
|
|
=================
|
|
*/
|
|
qboolean SV_inPVS (const vec3_t p1, const vec3_t p2)
|
|
{
|
|
int leafnum;
|
|
int cluster;
|
|
int area1, area2;
|
|
byte *mask;
|
|
|
|
leafnum = CM_PointLeafnum (p1);
|
|
cluster = CM_LeafCluster (leafnum);
|
|
area1 = CM_LeafArea (leafnum);
|
|
mask = CM_ClusterPVS (cluster);
|
|
|
|
leafnum = CM_PointLeafnum (p2);
|
|
cluster = CM_LeafCluster (leafnum);
|
|
area2 = CM_LeafArea (leafnum);
|
|
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
|
|
return qfalse;
|
|
if (!CM_AreasConnected (area1, area2))
|
|
return qfalse; // a door blocks sight
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
SV_inPVSIgnorePortals
|
|
|
|
Does NOT check portalareas
|
|
=================
|
|
*/
|
|
qboolean SV_inPVSIgnorePortals( const vec3_t p1, const vec3_t p2)
|
|
{
|
|
int leafnum;
|
|
int cluster;
|
|
int area1, area2;
|
|
byte *mask;
|
|
|
|
leafnum = CM_PointLeafnum (p1);
|
|
cluster = CM_LeafCluster (leafnum);
|
|
area1 = CM_LeafArea (leafnum);
|
|
mask = CM_ClusterPVS (cluster);
|
|
|
|
leafnum = CM_PointLeafnum (p2);
|
|
cluster = CM_LeafCluster (leafnum);
|
|
area2 = CM_LeafArea (leafnum);
|
|
|
|
if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )
|
|
return qfalse;
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_NumAnims
|
|
===============
|
|
*/
|
|
int PF_NumAnims( dtiki_t *tiki )
|
|
{
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_NumAnims( tiki );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_NumSurfaces
|
|
===============
|
|
*/
|
|
int PF_NumSurfaces( dtiki_t *tiki )
|
|
{
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_NumSurfaces( tiki );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_NumTags
|
|
===============
|
|
*/
|
|
int PF_NumTags( dtiki_t *tiki )
|
|
{
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_NumTags( tiki );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Cross_Time
|
|
===============
|
|
*/
|
|
float PF_Cross_Time( dtiki_t *tiki, int anim )
|
|
{
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_Anim_CrossblendTime( tiki, anim );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_CalculateBounds
|
|
===============
|
|
*/
|
|
void PF_CalculateBounds( dtiki_t *tiki, float scale, vec3_t mins, vec3_t maxs )
|
|
{
|
|
if (!tiki) {
|
|
return;
|
|
}
|
|
|
|
TIKI_CalculateBounds( tiki, scale, mins, maxs );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_NameForNum
|
|
===============
|
|
*/
|
|
const char *PF_Anim_NameForNum( dtiki_t *tiki, int animnum )
|
|
{
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_Anim_NameForNum( tiki, animnum );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_NumForName
|
|
===============
|
|
*/
|
|
int PF_Anim_NumForName( dtiki_t *tiki, const char *name )
|
|
{
|
|
if (!tiki) {
|
|
return -1;
|
|
}
|
|
|
|
return TIKI_Anim_NumForName( tiki, name );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_Random
|
|
===============
|
|
*/
|
|
int PF_Anim_Random( dtiki_t *tiki, const char *name )
|
|
{
|
|
if (!tiki) {
|
|
return -1;
|
|
}
|
|
|
|
return TIKI_Anim_Random( tiki, name );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_NumFrames
|
|
===============
|
|
*/
|
|
int PF_Anim_NumFrames( dtiki_t *tiki, int animnum )
|
|
{
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_Anim_NumFrames( tiki, animnum );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_Time
|
|
===============
|
|
*/
|
|
float PF_Anim_Time( dtiki_t *tiki, int animnum )
|
|
{
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_Anim_Time( tiki, animnum );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_Frametime
|
|
===============
|
|
*/
|
|
float PF_Anim_Frametime( dtiki_t *tiki, int animnum )
|
|
{
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_Anim_Frametime( tiki, animnum );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_Delta
|
|
===============
|
|
*/
|
|
void PF_Anim_Delta( dtiki_t *tiki, int animnum, vec3_t delta )
|
|
{
|
|
if (!tiki) {
|
|
return;
|
|
}
|
|
|
|
TIKI_Anim_Delta( tiki, animnum, delta );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_AngularDelta
|
|
===============
|
|
*/
|
|
void PF_Anim_AngularDelta(dtiki_t* tiki, int animnum, vec3_t delta)
|
|
{
|
|
if (!tiki) {
|
|
return;
|
|
}
|
|
|
|
TIKI_Anim_AngularDelta(tiki, animnum, delta);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_HasDelta
|
|
===============
|
|
*/
|
|
qboolean PF_Anim_HasDelta( dtiki_t *tiki, int animnum )
|
|
{
|
|
if (!tiki) {
|
|
return qfalse;
|
|
}
|
|
|
|
return TIKI_Anim_HasDelta( tiki, animnum );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_DeltaOverTime
|
|
===============
|
|
*/
|
|
void PF_Anim_DeltaOverTime( dtiki_t *tiki, int iAnimnum, float fTime1, float fTime2, vec3_t vDelta )
|
|
{
|
|
if (!tiki) {
|
|
return;
|
|
}
|
|
|
|
TIKI_Anim_DeltaOverTime( tiki, iAnimnum, fTime1, fTime2, vDelta );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_AngularDeltaOverTime
|
|
===============
|
|
*/
|
|
void PF_Anim_AngularDeltaOverTime(dtiki_t* tiki, int iAnimnum, float fTime1, float fTime2, float * fDelta)
|
|
{
|
|
if (!tiki) {
|
|
return;
|
|
}
|
|
|
|
TIKI_Anim_AngularDeltaOverTime(tiki, iAnimnum, fTime1, fTime2, fDelta);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_Flags
|
|
===============
|
|
*/
|
|
int PF_Anim_Flags( dtiki_t *tiki, int animnum )
|
|
{
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_Anim_Flags( tiki, animnum );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_FlagsSkel
|
|
===============
|
|
*/
|
|
int PF_Anim_FlagsSkel( dtiki_t *tiki, int animnum )
|
|
{
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_Anim_FlagsSkel( tiki, animnum );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_HasCommands
|
|
===============
|
|
*/
|
|
qboolean PF_Anim_HasCommands( dtiki_t *tiki, int animnum )
|
|
{
|
|
if (!tiki) {
|
|
return qfalse;
|
|
}
|
|
|
|
return TIKI_Anim_HasServerCommands( tiki, animnum );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_HasCommands
|
|
===============
|
|
*/
|
|
qboolean PF_Anim_HasCommands_Client(dtiki_t* tiki, int animnum)
|
|
{
|
|
if (!tiki) {
|
|
return qfalse;
|
|
}
|
|
|
|
return TIKI_Anim_HasClientCommands(tiki, animnum);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_ModelTiki
|
|
===============
|
|
*/
|
|
dtiki_t *PF_ModelTiki( const char *name )
|
|
{
|
|
return TIKI_RegisterTikiFlags( name, qtrue );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_ModelTikiAnim
|
|
===============
|
|
*/
|
|
dtikianim_t *PF_ModelTikiAnim( const char *name )
|
|
{
|
|
return TIKI_RegisterTikiAnimFlags( name, qtrue );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_NumHeadModels
|
|
===============
|
|
*/
|
|
int PF_NumHeadModels( const char *model )
|
|
{
|
|
dtikianim_t* tiki = PF_ModelTikiAnim(model);
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_NumHeadModels(tiki);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_GetHeadModel
|
|
===============
|
|
*/
|
|
void PF_GetHeadModel( const char *model, int num, char *name )
|
|
{
|
|
dtikianim_t *tiki = PF_ModelTikiAnim( model );
|
|
if (!tiki) {
|
|
return;
|
|
}
|
|
|
|
TIKI_GetHeadModel(tiki, num, name);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_NumHeadSkins
|
|
===============
|
|
*/
|
|
int PF_NumHeadSkins( const char *model )
|
|
{
|
|
dtikianim_t *tiki = PF_ModelTikiAnim( model );
|
|
if (!tiki) {
|
|
return 0;
|
|
}
|
|
|
|
return TIKI_NumHeadSkins( tiki );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_GetHeadSkin
|
|
===============
|
|
*/
|
|
void PF_GetHeadSkin( const char *model, int num, char *name )
|
|
{
|
|
dtikianim_t *tiki = PF_ModelTikiAnim( model );
|
|
if (!tiki) {
|
|
return;
|
|
}
|
|
|
|
TIKI_GetHeadSkin( tiki, num, name );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Anim_HasClientCommands
|
|
===============
|
|
*/
|
|
qboolean PF_Anim_HasClientCommands( dtiki_t *tiki, int animnum )
|
|
{
|
|
if (!tiki) {
|
|
return qfalse;
|
|
}
|
|
|
|
return TIKI_Anim_HasClientCommands( tiki, animnum );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Frame_Commands
|
|
===============
|
|
*/
|
|
qboolean PF_Frame_Commands( dtiki_t *tiki, int animnum, int framenum, tiki_cmd_t *tiki_cmds )
|
|
{
|
|
if (!tiki) {
|
|
return qfalse;
|
|
}
|
|
|
|
return TIKI_Frame_Commands_Server( tiki, animnum, framenum, tiki_cmds );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Frame_Commands
|
|
===============
|
|
*/
|
|
qboolean PF_Frame_Commands_Client( dtiki_t *tiki, int animnum, int framenum, tiki_cmd_t *tiki_cmds )
|
|
{
|
|
if (!tiki) {
|
|
return qfalse;
|
|
}
|
|
|
|
return TIKI_Frame_Commands_Client( tiki, animnum, framenum, tiki_cmds );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Surface_NameToNum
|
|
===============
|
|
*/
|
|
int PF_Surface_NameToNum( dtiki_t *tiki, const char *name )
|
|
{
|
|
if (!tiki) {
|
|
return -1;
|
|
}
|
|
|
|
return TIKI_Surface_NameToNum( tiki, name );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Surface_NumToName
|
|
===============
|
|
*/
|
|
const char *PF_Surface_NumToName( dtiki_t *tiki, int num )
|
|
{
|
|
if (!tiki) {
|
|
return NULL;
|
|
}
|
|
|
|
return TIKI_Surface_NumToName( tiki, num );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Tag_NameToNum
|
|
===============
|
|
*/
|
|
int PF_Tag_NameToNum( dtiki_t *tiki, const char *name )
|
|
{
|
|
if (!tiki) {
|
|
return -1;
|
|
}
|
|
|
|
return TIKI_Tag_NameToNum( tiki, name );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Tag_NumToName
|
|
===============
|
|
*/
|
|
const char *PF_Tag_NumToName( dtiki_t *tiki, int num )
|
|
{
|
|
if (!tiki) {
|
|
return NULL;
|
|
}
|
|
|
|
return TIKI_Tag_NumToName( tiki, num );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_TIKI_OrientationInternal
|
|
===============
|
|
*/
|
|
orientation_t PF_TIKI_OrientationInternal( dtiki_t *tiki, int entnum, int num, float scale )
|
|
{
|
|
return TIKI_OrientationInternal( tiki, entnum, num, scale );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_TIKI_TransformInternal
|
|
===============
|
|
*/
|
|
void *PF_TIKI_TransformInternal( dtiki_t *tiki, int entnum, int num )
|
|
{
|
|
return TIKI_TransformInternal( tiki, entnum, num );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_TIKI_IsOnGroundInternal
|
|
===============
|
|
*/
|
|
qboolean PF_TIKI_IsOnGroundInternal( dtiki_t *tiki, int entnum, int num, float threshold )
|
|
{
|
|
return TIKI_IsOnGroundInternal( tiki, entnum, num, threshold );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_SetPoseInternal
|
|
===============
|
|
*/
|
|
void PF_SetPoseInternal( dtiki_t *tiki, int entnum, const frameInfo_t *frameInfo, int *bone_tag, vec4_t *bone_quat, float actionWeight )
|
|
{
|
|
assert(tiki);
|
|
TIKI_SetPoseInternal( TIKI_GetSkeletor( tiki, entnum ), frameInfo, bone_tag, bone_quat, actionWeight );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Alias_Add
|
|
|
|
===============
|
|
*/
|
|
qboolean PF_Alias_Add( dtiki_t *pmdl, const char *alias, const char *name, const char *parameters )
|
|
{
|
|
if( !pmdl->a->alias_list ) pmdl->a->alias_list = AliasList_New( pmdl->a->name );
|
|
return Alias_ListAdd( pmdl->a->alias_list, alias, name, parameters );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Alias_FindRandom
|
|
|
|
===============
|
|
*/
|
|
const char *PF_Alias_FindRandom( dtiki_t *tiki, const char *alias, AliasListNode_t **ret )
|
|
{
|
|
AliasList_t* alias_list = NULL;
|
|
|
|
if (tiki) {
|
|
alias_list = tiki->a->alias_list;
|
|
}
|
|
|
|
if (alias_list) {
|
|
return Alias_ListFindRandom(alias_list, alias, ret);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Alias_UpdateDialog
|
|
|
|
===============
|
|
*/
|
|
void PF_Alias_UpdateDialog( dtikianim_t *tiki, const char *alias )
|
|
{
|
|
AliasList_t* alias_list = NULL;
|
|
|
|
if (tiki) {
|
|
alias_list = tiki->alias_list;
|
|
}
|
|
|
|
if (alias_list) {
|
|
Alias_ListUpdateDialog(alias_list, alias);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Alias_Dump
|
|
|
|
===============
|
|
*/
|
|
void PF_Alias_Dump( dtiki_t *tiki )
|
|
{
|
|
AliasList_t* alias_list = NULL;
|
|
|
|
if (tiki) {
|
|
alias_list = tiki->a->alias_list;
|
|
}
|
|
|
|
if (alias_list) {
|
|
Alias_ListDump(tiki->a->alias_list);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Alias_Clear
|
|
|
|
===============
|
|
*/
|
|
void PF_Alias_Clear( dtiki_t *tiki )
|
|
{
|
|
PF_Alias_Dump( tiki );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_NameForNum
|
|
===============
|
|
*/
|
|
const char *PF_NameForNum( dtiki_t *tiki )
|
|
{
|
|
if (!tiki) {
|
|
return NULL;
|
|
}
|
|
|
|
return TIKI_Name( tiki );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_ClearModelUserCounts
|
|
|
|
Clears model's reference
|
|
===============
|
|
*/
|
|
void SV_ClearModelUserCounts()
|
|
{
|
|
memset( modelUserCount, 0, sizeof( modelUserCount ) );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_RegisterTiki
|
|
===============
|
|
*/
|
|
dtiki_t *PF_RegisterTiki( const char *path )
|
|
{
|
|
modelUserCount[ SV_ModelIndex( path ) ]++;
|
|
return TIKI_RegisterTiki( path );
|
|
}
|
|
|
|
static int g_usageIndex = 0;
|
|
|
|
/*
|
|
===============
|
|
PF_setmodel
|
|
===============
|
|
*/
|
|
qboolean PF_setmodel( gentity_t *ent, const char *name )
|
|
{
|
|
dtiki_t *tiki;
|
|
int newModelIndex;
|
|
|
|
g_usageIndex++;
|
|
if( g_usageIndex >= 65535 ) {
|
|
g_usageIndex = 1;
|
|
}
|
|
|
|
ent->s.usageIndex = g_usageIndex;
|
|
|
|
if( !*name )
|
|
{
|
|
tiki = NULL;
|
|
}
|
|
else
|
|
{
|
|
tiki = PF_ModelTiki( name );
|
|
if( !tiki ) {
|
|
return qfalse;
|
|
}
|
|
|
|
newModelIndex = SV_ModelIndex( name );
|
|
if( !newModelIndex ) {
|
|
return qfalse;
|
|
}
|
|
|
|
// don't reference the model again
|
|
if( ent->s.modelindex == newModelIndex ) {
|
|
assert(modelUserCount[ ent->s.modelindex ] > 0);
|
|
return qtrue;
|
|
}
|
|
}
|
|
|
|
if( ent->tiki && ent->s.modelindex )
|
|
{
|
|
assert(modelUserCount[ ent->s.modelindex ] > 0);
|
|
modelUserCount[ ent->s.modelindex ]--;
|
|
if( !modelUserCount[ ent->s.modelindex ] )
|
|
SV_ClearModel( ent->s.modelindex );
|
|
ent->s.modelindex = 0;
|
|
}
|
|
|
|
ent->tiki = tiki;
|
|
ent->r.bmodel = qfalse;
|
|
|
|
if( ent->tiki )
|
|
{
|
|
ent->s.modelindex = newModelIndex;
|
|
modelUserCount[ newModelIndex ]++;
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_clearmodel
|
|
===============
|
|
*/
|
|
void PF_clearmodel( gentity_t *ent )
|
|
{
|
|
PF_setmodel( ent, "" );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_GetSkeletor
|
|
===============
|
|
*/
|
|
void *PF_GetSkeletor( dtiki_t *tiki, int entnum )
|
|
{
|
|
if (!tiki) {
|
|
return NULL;
|
|
}
|
|
|
|
return TIKI_GetSkeletor( tiki, entnum );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_GameSendServerCommand
|
|
|
|
Sends a command string to a client
|
|
===============
|
|
*/
|
|
void SV_GameSendServerCommand( int clientNum, const char *text, ... )
|
|
{
|
|
char tmp[1] = { 0 };
|
|
char* buffer;
|
|
size_t length = 1;
|
|
va_list va;
|
|
|
|
va_start(va, text);
|
|
length = vsnprintf(tmp, 0, text, va);
|
|
va_end(va);
|
|
|
|
if (!length || length == -1) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// allocate variadic arguments on stack
|
|
//
|
|
buffer = (char*)alloca(length + 1);
|
|
|
|
va_start(va, text);
|
|
vsnprintf(buffer, length + 1, text, va);
|
|
va_end(va);
|
|
|
|
if (clientNum == -1) {
|
|
SV_SendServerCommand(NULL, "%s", buffer);
|
|
} else {
|
|
if (clientNum < 0 || clientNum >= sv_maxclients->integer) {
|
|
return;
|
|
}
|
|
SV_SendServerCommand(svs.clients + clientNum, "%s", buffer);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_GameDropClient
|
|
|
|
Disconnects the client with a message
|
|
===============
|
|
*/
|
|
void SV_GameDropClient( int clientNum, const char *reason ) {
|
|
if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
|
|
return;
|
|
}
|
|
SV_DropClient( svs.clients + clientNum, reason );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SV_SetBrushModel
|
|
|
|
sets mins and maxs for inline bmodels
|
|
=================
|
|
*/
|
|
void SV_SetBrushModel( gentity_t *ent, const char *name ) {
|
|
clipHandle_t h;
|
|
vec3_t mins, maxs;
|
|
|
|
if( !name ) {
|
|
Com_Error( ERR_DROP, "SV_SetBrushModel: NULL" );
|
|
}
|
|
|
|
if( name[ 0 ] != '*' ) {
|
|
Com_Error( ERR_DROP, "SV_SetBrushModel: %s isn't a brush model", name );
|
|
}
|
|
|
|
ent->s.modelindex = atoi( name + 1 );
|
|
h = CM_InlineModel( ent->s.modelindex );
|
|
CM_ModelBounds( h, mins, maxs );
|
|
VectorCopy( mins, ent->r.mins );
|
|
VectorCopy( maxs, ent->r.maxs );
|
|
ent->r.bmodel = qtrue;
|
|
|
|
if( !ent->r.contents )
|
|
ent->r.contents = -1; // we don't know exactly what is in the brushes
|
|
|
|
SV_LinkEntity( ent );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
SV_AdjustAreaPortalState
|
|
========================
|
|
*/
|
|
void SV_AdjustAreaPortalState( gentity_t *ent, qboolean open ) {
|
|
svEntity_t *svEnt;
|
|
|
|
svEnt = SV_SvEntityForGentity( ent );
|
|
if( svEnt->areanum2 == -1 ) {
|
|
return;
|
|
}
|
|
CM_AdjustAreaPortalState( svEnt->areanum, svEnt->areanum2, open );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
SV_EntityContact
|
|
========================
|
|
*/
|
|
qboolean SV_EntityContact( vec3_t mins, vec3_t maxs, gentity_t *gEnt )
|
|
{
|
|
clipHandle_t ch;
|
|
trace_t trace;
|
|
|
|
ch = SV_ClipHandleForEntity( gEnt );
|
|
CM_TransformedBoxTrace( &trace, vec3_origin, vec3_origin, mins, maxs, ch, -1, gEnt->s.origin, gEnt->r.currentAngles, qfalse );
|
|
return trace.startsolid;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
SV_GetServerinfo
|
|
|
|
===============
|
|
*/
|
|
void SV_GetServerinfo( char *buffer, int bufferSize ) {
|
|
if( bufferSize < 1 ) {
|
|
Com_Error( ERR_DROP, "SV_GetServerinfo: bufferSize == %i", bufferSize );
|
|
}
|
|
Q_strncpyz( buffer, Cvar_InfoString( CVAR_SERVERINFO ), bufferSize );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_LocateGameData
|
|
|
|
===============
|
|
*/
|
|
void SV_LocateGameData( gentity_t *gEnts, int numGEntities, int sizeofGEntity_t,
|
|
playerState_t *clients, int sizeofGameClient ) {
|
|
sv.gentities = ( gentity_t * )gEnts;
|
|
sv.gentitySize = sizeofGEntity_t;
|
|
sv.num_entities = numGEntities;
|
|
|
|
sv.gameClients = clients;
|
|
sv.gameClientSize = sizeofGameClient;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_SetFarPlane
|
|
===============
|
|
*/
|
|
void SV_SetFarPlane( int farplane )
|
|
{
|
|
if( farplane )
|
|
{
|
|
sv.farplane = ( farplane + 32 ) * ( farplane + 32 );
|
|
}
|
|
else
|
|
{
|
|
sv.farplane = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_SetSkyPortal
|
|
===============
|
|
*/
|
|
void SV_SetSkyPortal( qboolean skyportal )
|
|
{
|
|
sv.skyportal = skyportal;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_GetUsercmd
|
|
===============
|
|
*/
|
|
void SV_GetUsercmd( int clientNum, usercmd_t *cmd ) {
|
|
if( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
|
|
Com_Error( ERR_DROP, "SV_GetUsercmd: bad clientNum:%i", clientNum );
|
|
}
|
|
*cmd = svs.clients[ clientNum ].lastUsercmd;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_centerprintf
|
|
===============
|
|
*/
|
|
void PF_centerprintf( gentity_t *ent, const char *fmt, ... )
|
|
{
|
|
va_list va;
|
|
char msg[ 2048 ];
|
|
|
|
if( ent->s.number >= svs.iNumClients )
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start( va, fmt );
|
|
Q_vsnprintf( msg, sizeof( msg ), fmt, va );
|
|
va_end( va );
|
|
|
|
if( strlen( msg ) > 256 ) {
|
|
Com_DPrintf( "Centerprint text exceeds buffer size\n" );
|
|
}
|
|
|
|
strncpy( svs.clients[ ent->s.number ].stringToPrint, msg, sizeof(svs.clients[ent->s.number].stringToPrint));
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_locationprintf
|
|
===============
|
|
*/
|
|
void PF_locationprintf( gentity_t *ent, int x, int y, const char *fmt, ... )
|
|
{
|
|
va_list va;
|
|
char msg[ 2048 ];
|
|
|
|
if( ent->s.number >= svs.iNumClients )
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_start( va, fmt );
|
|
Q_vsnprintf( msg, sizeof( msg ), fmt, va );
|
|
va_end( va );
|
|
|
|
if( strlen( msg ) > 256 ) {
|
|
Com_DPrintf( "Locationprint text exceeds buffer size\n" );
|
|
}
|
|
|
|
strncpy( svs.clients[ ent->s.number ].stringToPrint, msg, sizeof(svs.clients[ent->s.number].stringToPrint) );
|
|
svs.clients[ ent->s.number ].XOffset = x;
|
|
svs.clients[ ent->s.number ].YOffset = y;
|
|
svs.clients[ ent->s.number ].locprint = qtrue;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
SV_AddGameCommand
|
|
=====================
|
|
*/
|
|
void SV_AddGameCommand( const char *cmdName, xcommand_t function ) {
|
|
Cmd_AddCommand( cmdName, function );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_Malloc
|
|
===============
|
|
*/
|
|
void *SV_Malloc( size_t size )
|
|
{
|
|
return Z_TagMalloc( size, TAG_GAME );
|
|
}
|
|
|
|
void SV_Free( void* ptr )
|
|
{
|
|
Z_Free( ptr );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_ShutdownGameProgs
|
|
|
|
Called every time a map changes
|
|
===============
|
|
*/
|
|
void SV_ShutdownGameProgs( void ) {
|
|
if( !ge ) {
|
|
return;
|
|
}
|
|
|
|
ge->Shutdown();
|
|
Sys_UnloadGame();
|
|
|
|
// Free all memory allocated by the game module
|
|
Z_FreeTags(TAG_GAME);
|
|
|
|
ge = NULL;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_UI_PopMenu
|
|
===============
|
|
*/
|
|
void PF_UI_PopMenu( int iClient, int i )
|
|
{
|
|
SV_GameSendServerCommand( iClient, "stufftext \"popmenu %i\"\n", i );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_UI_ShowMenu
|
|
===============
|
|
*/
|
|
void PF_UI_ShowMenu( int iClient, const char *name, qboolean bForce )
|
|
{
|
|
SV_GameSendServerCommand( iClient, "stufftext \"showmenu %s %i\"\n", name, bForce );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_UI_HideMenu
|
|
===============
|
|
*/
|
|
void PF_UI_HideMenu( int iClient, const char *name, qboolean bForce )
|
|
{
|
|
SV_GameSendServerCommand( iClient, "stufftext \"hidemenu %s %i\"\n", name, bForce );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_UI_PushMenu
|
|
===============
|
|
*/
|
|
void PF_UI_PushMenu( int iClient, const char *name )
|
|
{
|
|
SV_GameSendServerCommand( iClient, "stufftext \"pushmenu %s\"\n", name );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_UI_HideMouse_f
|
|
===============
|
|
*/
|
|
void PF_UI_HideMouse_f( int iClient )
|
|
{
|
|
SV_GameSendServerCommand( iClient, "stufftext \"ui_hidemouse\"\n" );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_UI_ShowMouse_f
|
|
===============
|
|
*/
|
|
void PF_UI_ShowMouse_f( int iClient )
|
|
{
|
|
SV_GameSendServerCommand( iClient, "stufftext \"ui_showmouse\"\n" );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Key_StringToKeynum
|
|
===============
|
|
*/
|
|
int PF_Key_StringToKeynum( const char *str )
|
|
{
|
|
if( com_cl_running->integer ) {
|
|
return PF_Key_StringToKeynum( str );
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Key_KeynumToBindString
|
|
===============
|
|
*/
|
|
const char *PF_Key_KeynumToBindString( int keynum )
|
|
{ if( com_cl_running->integer ) {
|
|
return Key_KeynumToBindString( keynum );
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PF_Key_GetKeysForCommand
|
|
===============
|
|
*/
|
|
void PF_Key_GetKeysForCommand( const char *command, int *key1, int *key2 )
|
|
{
|
|
if( com_cl_running->integer ) {
|
|
Key_GetKeysForCommand( command, key1, key2 );
|
|
} else {
|
|
*key1 = 0;
|
|
*key2 = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_GameKickForReason
|
|
|
|
Kicks the client with a message and reason
|
|
===============
|
|
*/
|
|
void SV_GameKickClientForReason( int clientNum, const char *reason ) {
|
|
if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
|
|
return;
|
|
}
|
|
SV_KickClientForReason( svs.clients + clientNum, reason );
|
|
}
|
|
|
|
unsigned int PF_SV_Client_NumPendingCommands(int clientNum)
|
|
{
|
|
if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
|
|
return 0;
|
|
}
|
|
|
|
return SV_Client_GetNumPendingCommands(svs.clients + clientNum);
|
|
}
|
|
|
|
unsigned int PF_SV_Client_MaxPendingCommands(int clientNum)
|
|
{
|
|
if ( clientNum < 0 || clientNum >= sv_maxclients->integer ) {
|
|
return 0;
|
|
}
|
|
|
|
return SV_Client_GetMaxPendingCommands(svs.clients + clientNum);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SV_VR_GetWeaponAim
|
|
|
|
Hands the weapon hand's aim to the game, so a shot goes where the controller is
|
|
pointing rather than where the player is looking.
|
|
|
|
Single player only, and deliberately so: the angles come from a headset attached
|
|
to this process, and a dedicated server has none. It answers false there and the
|
|
game keeps the view aim it already had.
|
|
==================
|
|
*/
|
|
static qboolean SV_VR_GetWeaponAim( vec3_t out ) {
|
|
#if defined(USE_OPENXR) && !defined(DEDICATED)
|
|
return VR_GetWeaponAim( out );
|
|
#else
|
|
return qfalse;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SV_VR_GetHeadOffset
|
|
|
|
Hands the head's offset within the play space to the game, so that a trace from
|
|
the player's eye starts where the player is actually looking from.
|
|
|
|
Single player only, for the same reason as the aim above: a dedicated server has
|
|
no headset, answers false, and the game keeps the body's eye position.
|
|
==================
|
|
*/
|
|
static qboolean SV_VR_GetHeadOffset( vec3_t out, float *baseYaw ) {
|
|
#if defined(USE_OPENXR) && !defined(DEDICATED)
|
|
return VR_GetHeadOffset( out, baseYaw );
|
|
#else
|
|
return qfalse;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SV_VR_GetClimbPull
|
|
|
|
Hands the ladder drag to the game, which is the only place that can act on it:
|
|
the climb moves the player's origin, and where the player may be moved to is a
|
|
question about the world.
|
|
|
|
Single player only, as above.
|
|
==================
|
|
*/
|
|
static qboolean SV_VR_GetClimbPull( vec3_t out ) {
|
|
#if defined(USE_OPENXR) && !defined(DEDICATED)
|
|
return VR_GetClimbPull( out );
|
|
#else
|
|
return qfalse;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SV_VR_GetThrow
|
|
|
|
Hands the weapon hand's velocity to the game, so a thrown grenade leaves along
|
|
the arm's own swing.
|
|
|
|
Single player only, as with the rest of these.
|
|
==================
|
|
*/
|
|
static qboolean SV_VR_GetThrow( vec3_t velocity, float *baseYaw ) {
|
|
#if defined(USE_OPENXR) && !defined(DEDICATED)
|
|
return VR_GetThrow( velocity, baseYaw );
|
|
#else
|
|
return qfalse;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
===============
|
|
SV_InitGameProgs
|
|
|
|
Called on a normal map change, not on a map_restart
|
|
===============
|
|
*/
|
|
void SV_InitGameProgs( void ) {
|
|
game_import_t import;
|
|
const char *err;
|
|
int i;
|
|
|
|
import.Printf = Com_Printf;
|
|
import.DPrintf = Com_DPrintf;
|
|
import.DPrintf2 = Com_DPrintf2;
|
|
import.DebugPrintf = Com_DebugPrintf;
|
|
import.Error = Com_Error;
|
|
import.GetArchiveFileName = Com_GetArchiveFileName;
|
|
import.Milliseconds = Sys_Milliseconds;
|
|
import.LV_ConvertString = Sys_LV_ConvertString;
|
|
import.CL_LV_ConvertString = Sys_LV_CL_ConvertString;
|
|
|
|
import.SendServerCommand = SV_GameSendServerCommand;
|
|
import.DropClient = SV_GameDropClient;
|
|
|
|
import.MSG_WriteBits = PF_MSG_WriteBits;
|
|
import.MSG_WriteChar = PF_MSG_WriteChar;
|
|
import.MSG_WriteByte = PF_MSG_WriteByte;
|
|
import.MSG_WriteSVC = PF_MSG_WriteSVC;
|
|
import.MSG_WriteShort = PF_MSG_WriteShort;
|
|
import.MSG_WriteLong = PF_MSG_WriteLong;
|
|
import.MSG_WriteFloat = PF_MSG_WriteFloat;
|
|
import.MSG_WriteString = PF_MSG_WriteString;
|
|
import.MSG_WriteAngle8 = PF_MSG_WriteAngle8;
|
|
import.MSG_WriteAngle16 = PF_MSG_WriteAngle16;
|
|
import.MSG_WriteCoord = PF_MSG_WriteCoord;
|
|
import.MSG_WriteDir = PF_MSG_WriteDir;
|
|
import.MSG_StartCGM = PF_MSG_StartCGM;
|
|
import.MSG_EndCGM = PF_MSG_EndCGM;
|
|
import.MSG_SetClient = PF_MSG_SetClient;
|
|
import.SetBroadcastVisible = MSG_SetBroadcastVisible;
|
|
import.SetBroadcastHearable = MSG_SetBroadcastHearable;
|
|
import.SetBroadcastAll = MSG_SetBroadcastAll;
|
|
|
|
import.linkentity = SV_LinkEntity;
|
|
import.unlinkentity = SV_UnlinkEntity;
|
|
|
|
import.AreaEntities = SV_AreaEntities;
|
|
|
|
import.SightTraceEntity = SV_SightTraceEntity;
|
|
import.SightTrace = SV_SightTrace;
|
|
import.trace = SV_Trace;
|
|
import.CM_VisualObfuscation = CM_VisualObfuscation;
|
|
import.GetShader = SV_GetShaderPointer;
|
|
import.pointcontents = SV_PointContents;
|
|
import.PointBrushnum = CM_PointBrushNum;
|
|
import.SetBrushModel = SV_SetBrushModel;
|
|
import.ModelBoundsFromName = CM_ModelBoundsFromName;
|
|
import.ClipToEntity = SV_ClipToEntity;
|
|
import.HitEntity = SV_HitEntity;
|
|
import.setConfigstring = SV_SetConfigstring;
|
|
import.getConfigstring = SV_GetConfigstring;
|
|
import.GetUserinfo = SV_GetUserinfo;
|
|
import.SetUserinfo = SV_SetUserinfo;
|
|
|
|
import.Malloc = SV_Malloc;
|
|
import.Free = SV_Free;
|
|
|
|
import.Cvar_Get = Cvar_Get;
|
|
import.cvar_set = Cvar_Set;
|
|
import.cvar_set2 = Cvar_Set2;
|
|
import.NextCvar = Cvar_Next;
|
|
import.Cvar_CheckRange = Cvar_CheckRange;
|
|
|
|
import.Argc = Cmd_Argc;
|
|
import.Args = Cmd_Args;
|
|
import.Argv = Cmd_Argv;
|
|
|
|
import.AddCommand = SV_AddGameCommand;
|
|
import.SendConsoleCommand = Cbuf_AddText;
|
|
import.ExecuteConsoleCommand = Cbuf_ExecuteText;
|
|
|
|
import.FS_ReadFile = FS_ReadFileEx;
|
|
import.FS_FreeFile = FS_FreeFile;
|
|
import.FS_WriteFile = FS_WriteFile;
|
|
import.FS_FOpenFileWrite = FS_FOpenFileWrite_HomeData;
|
|
import.FS_FOpenFileAppend = FS_FOpenFileAppend_HomeData;
|
|
import.FS_FOpenFile = FS_FOpenFileRead;
|
|
import.FS_PrepFileWrite = FS_PrepFileWrite;
|
|
import.FS_Write = FS_Write;
|
|
import.FS_Read = FS_Read;
|
|
import.FS_FCloseFile = FS_FCloseFile;
|
|
import.FS_Tell = FS_FTell;
|
|
import.FS_Seek = FS_Seek;
|
|
import.FS_Flush = FS_ForceFlush;
|
|
import.FS_FileNewer = FS_FileNewer;
|
|
import.FS_CanonicalFilename = FS_CanonicalFilename;
|
|
import.FS_ListFiles = FS_ListFiles;
|
|
import.FS_FreeFileList = FS_FreeFileList;
|
|
|
|
import.DebugGraph = SCR_DebugGraph;
|
|
import.AdjustAreaPortalState = SV_AdjustAreaPortalState;
|
|
import.AreasConnected = CM_AreasConnected;
|
|
import.AreaForPoint = CM_AreaForPoint;
|
|
import.InPVS = CM_inPVS;
|
|
import.GameDir = FS_Gamedir;
|
|
|
|
import.setmodel = PF_setmodel;
|
|
import.clearmodel = PF_clearmodel;
|
|
import.TIKI_NumAnims = PF_NumAnims;
|
|
import.TIKI_NumSurfaces = PF_NumSurfaces;
|
|
import.TIKI_NumTags = PF_NumTags;
|
|
import.TIKI_CalculateBounds = PF_CalculateBounds;
|
|
import.TIKI_GetSkeletor = PF_GetSkeletor;
|
|
import.Anim_NameForNum = PF_Anim_NameForNum;
|
|
import.Anim_NumForName = PF_Anim_NumForName;
|
|
import.Anim_Random = PF_Anim_Random;
|
|
import.Anim_NumFrames = PF_Anim_NumFrames;
|
|
import.Anim_Time = PF_Anim_Time;
|
|
import.Anim_Frametime = PF_Anim_Frametime;
|
|
import.Anim_CrossTime = PF_Cross_Time;
|
|
import.Anim_Delta = PF_Anim_Delta;
|
|
import.Anim_AngularDelta = PF_Anim_AngularDelta;
|
|
import.Anim_HasDelta = PF_Anim_HasDelta;
|
|
import.Anim_DeltaOverTime = PF_Anim_DeltaOverTime;
|
|
import.Anim_AngularDeltaOverTime = PF_Anim_AngularDeltaOverTime;
|
|
import.Anim_Flags = PF_Anim_Flags;
|
|
import.Anim_FlagsSkel = PF_Anim_FlagsSkel;
|
|
import.Anim_HasCommands = PF_Anim_HasCommands;
|
|
import.Anim_HasCommands_Client = PF_Anim_HasCommands_Client;
|
|
import.NumHeadModels = PF_NumHeadModels;
|
|
import.GetHeadModel = PF_GetHeadModel;
|
|
import.NumHeadSkins = PF_NumHeadSkins;
|
|
import.GetHeadSkin = PF_GetHeadSkin;
|
|
import.Frame_Commands = PF_Frame_Commands;
|
|
import.Frame_Commands_Client = PF_Frame_Commands_Client;
|
|
import.Surface_NameToNum = PF_Surface_NameToNum;
|
|
import.Surface_NumToName = PF_Surface_NumToName;
|
|
import.Tag_NumForName = PF_Tag_NameToNum;
|
|
import.Tag_NameForNum = PF_Tag_NumToName;
|
|
import.TIKI_OrientationInternal = PF_TIKI_OrientationInternal;
|
|
import.TIKI_TransformInternal = PF_TIKI_TransformInternal;
|
|
import.TIKI_IsOnGroundInternal = PF_TIKI_IsOnGroundInternal;
|
|
import.TIKI_SetPoseInternal = PF_SetPoseInternal;
|
|
|
|
import.CM_GetHitLocationInfo = CM_GetHitLocationInfo;
|
|
import.CM_GetHitLocationInfoSecondary = CM_GetHitLocationInfoSecondary;
|
|
|
|
import.Alias_Add = PF_Alias_Add;
|
|
import.Alias_FindRandom = PF_Alias_FindRandom;
|
|
import.Alias_Dump = PF_Alias_Dump;
|
|
import.Alias_Clear = PF_Alias_Clear;
|
|
import.Alias_UpdateDialog = PF_Alias_UpdateDialog;
|
|
import.GlobalAlias_Add = Alias_Add;
|
|
import.GlobalAlias_FindRandom = Alias_FindRandom;
|
|
import.GlobalAlias_Dump = Alias_Dump;
|
|
import.GlobalAlias_Clear = Alias_Clear;
|
|
|
|
import.TIKI_NameForNum = PF_NameForNum;
|
|
import.TIKI_RegisterModel = PF_RegisterTiki;
|
|
import.modeltiki = PF_ModelTiki;
|
|
import.modeltikianim = PF_ModelTikiAnim;
|
|
import.soundindex = SV_SoundIndex;
|
|
import.imageindex = SV_ImageIndex;
|
|
import.itemindex = SV_ItemIndex;
|
|
import.SetLightStyle = SV_SetLightStyle;
|
|
|
|
import.DebugLines = &DebugLines;
|
|
import.numDebugLines = &numDebugLines;
|
|
import.DebugStrings = &DebugStrings;
|
|
import.numDebugStrings = &numDebugStrings;
|
|
|
|
import.CalcCRC = CRC_Block;
|
|
|
|
import.Sound = SV_Sound;
|
|
import.StopSound = SV_StopSound;
|
|
import.S_IsSoundPlaying = S_IsSoundPlaying;
|
|
import.centerprintf = PF_centerprintf;
|
|
import.locationprintf = PF_locationprintf;
|
|
import.LocateGameData = SV_LocateGameData;
|
|
import.SetFarPlane = SV_SetFarPlane;
|
|
import.SetSkyPortal = SV_SetSkyPortal;
|
|
|
|
import.Popmenu = PF_UI_PopMenu;
|
|
import.Showmenu = PF_UI_ShowMenu;
|
|
import.Hidemenu = PF_UI_HideMenu;
|
|
import.Pushmenu = PF_UI_PushMenu;
|
|
import.HideMouseCursor = PF_UI_HideMouse_f;
|
|
import.ShowMouseCursor = PF_UI_ShowMouse_f;
|
|
import.MapTime = CM_MapTime;
|
|
import.LoadResource = UI_LoadResource;
|
|
import.ClearResource = UI_ClearResource;
|
|
|
|
import.Key_StringToKeynum = PF_Key_StringToKeynum;
|
|
import.Key_KeynumToBindString = PF_Key_KeynumToBindString;
|
|
import.Key_GetKeysForCommand = PF_Key_GetKeysForCommand;
|
|
import.ArchiveLevel = SV_ArchiveLevel;
|
|
import.AddSvsTimeFixup = SV_AddSvsTimeFixup;
|
|
import.HudDrawShader = SV_HudDrawShader;
|
|
import.HudDrawAlign = SV_HudDrawAlign;
|
|
import.HudDrawRect = SV_HudDrawRect;
|
|
import.HudDrawVirtualSize = SV_HudDrawVirtualSize;
|
|
import.HudDrawColor = SV_HudDrawColor;
|
|
import.HudDrawAlpha = SV_HudDrawAlpha;
|
|
import.HudDrawString = SV_HudDrawString;
|
|
import.HudDrawFont = SV_HudDrawFont;
|
|
import.SanitizeName = Com_SanitizeName;
|
|
import.fsDebug = fs_debug;
|
|
|
|
// Added in OPM
|
|
import.PrintfClient = SV_PrintfClient;
|
|
import.pvssoundindex = SV_PVSSoundIndex;
|
|
import.KickClientForReason = SV_GameKickClientForReason;
|
|
|
|
import.Cvar_Find = Cvar_FindVar;
|
|
|
|
import.VR_GetWeaponAim = SV_VR_GetWeaponAim;
|
|
import.VR_GetHeadOffset = SV_VR_GetHeadOffset;
|
|
import.VR_GetClimbPull = SV_VR_GetClimbPull;
|
|
import.VR_GetThrow = SV_VR_GetThrow;
|
|
|
|
import.Client_NumPendingCommands = PF_SV_Client_NumPendingCommands;
|
|
import.Client_MaxPendingCommands = PF_SV_Client_MaxPendingCommands;
|
|
|
|
ge = Sys_GetGameAPI( &import );
|
|
|
|
if( !ge ) {
|
|
Com_Error( ERR_DROP, "failed to load game DLL" );
|
|
}
|
|
|
|
if( ge->apiversion != GAME_API_VERSION )
|
|
{
|
|
Com_Error( ERR_DROP, "game is version %i, not %i", ge->apiversion,
|
|
GAME_API_VERSION );
|
|
}
|
|
|
|
ge->Init( svs.startTime, Com_Milliseconds() );
|
|
|
|
err = ge->errorMessage;
|
|
if( err )
|
|
{
|
|
ge->errorMessage = NULL;
|
|
Com_Error(ERR_DROP, "%s", err);
|
|
}
|
|
|
|
for( i = 0; i < svs.iNumClients; i++ )
|
|
{
|
|
svs.clients[ i ].gentity = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
====================
|
|
SV_GameCommand
|
|
|
|
See if the current console command is claimed by the game
|
|
====================
|
|
*/
|
|
qboolean SV_GameCommand( void ) {
|
|
if ( sv.state != SS_GAME ) {
|
|
return qfalse;
|
|
}
|
|
|
|
return ge->ConsoleCommand();
|
|
}
|
|
|