1311 lines
36 KiB
C++
1311 lines
36 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
|
*
|
|
* This product contains software technology licensed from Id
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* Use, distribution, and modification of this source code and/or resulting
|
|
* object code is restricted to non-commercial enhancements to products from
|
|
* Valve LLC. All other use, distribution, or modification is prohibited
|
|
* without written permission from Valve LLC.
|
|
*
|
|
****/
|
|
// Robin, 4-22-98: Moved set_suicide_frame() here from player.cpp to allow us to
|
|
// have one without a hardcoded player.mdl in tf_client.cpp
|
|
|
|
/*
|
|
|
|
===== client.cpp ========================================================
|
|
|
|
client/server game specific stuff
|
|
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "extdll.h"
|
|
#include "utils.h"
|
|
#include "cbase.h"
|
|
#include "saverestore.h"
|
|
#include "shake.h"
|
|
#include "player.h"
|
|
#include "spectator.h"
|
|
#include "client.h"
|
|
#include "gamerules.h"
|
|
#include "game.h"
|
|
#include "soundent.h"
|
|
#include "baseweapon.h"
|
|
#include "defaults.h"
|
|
#include "decals.h"
|
|
#include "nodes.h"
|
|
|
|
extern DLL_GLOBAL ULONG g_ulModelIndexPlayer;
|
|
extern DLL_GLOBAL BOOL g_fGameOver;
|
|
extern CBaseEntity *g_pLastSpawn;
|
|
extern CSoundEnt *pSoundEnt;
|
|
DLL_GLOBAL edict_t *g_pBodyQueueHead;
|
|
DLL_GLOBAL int g_serveractive = 0;
|
|
BOOL MSGSended;
|
|
BOOL NewLevel = FALSE;
|
|
float MsgDelay;
|
|
user_messages_t gmsg;
|
|
void LinkUserMessages( void );
|
|
char text[128];
|
|
extern int g_teamplay;
|
|
|
|
//messages affect only player
|
|
typedef struct _SelAmmo
|
|
{
|
|
BYTE Ammo1Type;
|
|
BYTE Ammo1;
|
|
BYTE Ammo2Type;
|
|
BYTE Ammo2;
|
|
} SelAmmo;
|
|
|
|
/*
|
|
* used by kill command and disconnect command
|
|
* ROBIN: Moved here from player.cpp, to allow multiple player models
|
|
*/
|
|
void set_suicide_frame(entvars_t* pev)
|
|
{
|
|
if (!FStrEq(STRING(pev->model), "models/player.mdl"))
|
|
return; // allready gibbed
|
|
|
|
// pev->frame = $deatha11;
|
|
pev->solid = SOLID_NOT;
|
|
pev->movetype = MOVETYPE_TOSS;
|
|
pev->deadflag = DEAD_DEAD;
|
|
pev->nextthink = -1;
|
|
}
|
|
|
|
void InitBodyQue(void)
|
|
{
|
|
string_t istrClassname = MAKE_STRING("bodyque");
|
|
|
|
g_pBodyQueueHead = CREATE_NAMED_ENTITY( istrClassname );
|
|
entvars_t *pev = VARS(g_pBodyQueueHead);
|
|
|
|
// Reserve 3 more slots for dead bodies
|
|
for ( int i = 0; i < 3; i++ )
|
|
{
|
|
pev->owner = CREATE_NAMED_ENTITY( istrClassname );
|
|
pev = VARS(pev->owner);
|
|
}
|
|
|
|
pev->owner = g_pBodyQueueHead;
|
|
}
|
|
|
|
//
|
|
// make a body que entry for the given ent so the ent can be respawned elsewhere
|
|
//
|
|
// GLOBALS ASSUMED SET: g_eoBodyQueueHead
|
|
//
|
|
void CopyToBodyQue(entvars_t *pev)
|
|
{
|
|
if (pev->effects & EF_NODRAW)
|
|
return;
|
|
|
|
entvars_t *pevHead = VARS(g_pBodyQueueHead);
|
|
|
|
pevHead->angles = pev->angles;
|
|
pevHead->model = pev->model;
|
|
pevHead->modelindex = pev->modelindex;
|
|
pevHead->frame = pev->frame;
|
|
pevHead->colormap = pev->colormap;
|
|
pevHead->movetype = MOVETYPE_TOSS;
|
|
pevHead->velocity = pev->velocity;
|
|
pevHead->flags = 0;
|
|
pevHead->deadflag = pev->deadflag;
|
|
pevHead->renderfx = kRenderFxDeadPlayer;
|
|
pevHead->renderamt = ENTINDEX( ENT( pev ) );
|
|
|
|
pevHead->effects = pev->effects | EF_NOINTERP;
|
|
//pevHead->goalstarttime = pev->goalstarttime;
|
|
//pevHead->goalframe = pev->goalframe;
|
|
//pevHead->goalendtime = pev->goalendtime ;
|
|
|
|
pevHead->sequence = pev->sequence;
|
|
pevHead->animtime = pev->animtime;
|
|
|
|
UTIL_SetEdictOrigin(g_pBodyQueueHead, pev->origin);
|
|
UTIL_SetSize(pevHead, pev->mins, pev->maxs);
|
|
g_pBodyQueueHead = pevHead->owner;
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
ClientConnect
|
|
|
|
called when a player connects to a server
|
|
============
|
|
*/
|
|
BOOL ClientConnect( edict_t *pEntity, const char *userinfo )
|
|
{
|
|
return g_pGameRules->ClientConnected( pEntity, userinfo );
|
|
|
|
// a client connecting during an intermission can cause problems
|
|
// if (intermission_running)
|
|
// ExitIntermission ();
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
ClientDisconnect
|
|
|
|
called when a player disconnects from a server
|
|
|
|
GLOBALS ASSUMED SET: g_fGameOver
|
|
============
|
|
*/
|
|
void ClientDisconnect( edict_t *pEntity )
|
|
{
|
|
if (g_fGameOver)
|
|
return;
|
|
|
|
char text[256];
|
|
sprintf( text, "- %s has left the game\n", STRING(pEntity->v.netname) );
|
|
MESSAGE_BEGIN( MSG_ALL, gmsg.SayText, NULL );
|
|
WRITE_BYTE( ENTINDEX(pEntity) );
|
|
WRITE_STRING( text );
|
|
MESSAGE_END();
|
|
|
|
CSound *pSound;
|
|
pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( pEntity ) );
|
|
{
|
|
// since this client isn't around to think anymore, reset their sound.
|
|
if ( pSound )
|
|
{
|
|
pSound->Reset();
|
|
}
|
|
}
|
|
|
|
// since the edict doesn't get deleted, fix it so it doesn't interfere.
|
|
pEntity->v.takedamage = DAMAGE_NO;// don't attract autoaim
|
|
pEntity->v.solid = SOLID_NOT;// nonsolid
|
|
UTIL_SetEdictOrigin ( pEntity, pEntity->v.origin );
|
|
|
|
g_pGameRules->ClientDisconnected( pEntity );
|
|
}
|
|
|
|
|
|
// called by ClientKill and DeadThink
|
|
void respawn(entvars_t* pev, BOOL fCopyCorpse)
|
|
{
|
|
if (gpGlobals->coop || gpGlobals->deathmatch)
|
|
{
|
|
if ( fCopyCorpse )
|
|
{
|
|
// make a copy of the dead body for appearances sake
|
|
CopyToBodyQue(pev);
|
|
}
|
|
|
|
// respawn player
|
|
GetClassPtr( (CBasePlayer *)pev)->Spawn( );
|
|
}
|
|
else
|
|
{ // restart the entire server
|
|
SERVER_COMMAND("reload\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
ClientKill
|
|
|
|
Player entered the suicide command
|
|
|
|
GLOBALS ASSUMED SET: g_ulModelIndexPlayer
|
|
============
|
|
*/
|
|
void ClientKill( edict_t *pEntity )
|
|
{
|
|
entvars_t *pev = &pEntity->v;
|
|
|
|
CBasePlayer *pl = (CBasePlayer*) CBasePlayer::Instance( pev );
|
|
|
|
if ( pl->m_fNextSuicideTime > gpGlobals->time )
|
|
return; // prevent suiciding too ofter
|
|
|
|
pl->m_fNextSuicideTime = gpGlobals->time + 1; // don't let them suicide for 5 seconds after suiciding
|
|
|
|
// have the player kill themself
|
|
pev->health = 0;
|
|
pl->Killed( pev, GIB_NEVER );
|
|
|
|
// pev->modelindex = g_ulModelIndexPlayer;
|
|
// pev->frags -= 2; // extra penalty
|
|
// respawn( pev );
|
|
}
|
|
|
|
/*
|
|
===========
|
|
ClientPutInServer
|
|
|
|
called each time a player is spawned
|
|
============
|
|
*/
|
|
void ClientPutInServer( edict_t *pEntity )
|
|
{
|
|
CBasePlayer *pPlayer;
|
|
|
|
entvars_t *pev = &pEntity->v;
|
|
|
|
pPlayer = GetClassPtr((CBasePlayer *)pev);
|
|
pPlayer->SetCustomDecalFrames(-1); // Assume none;
|
|
|
|
// Allocate a CBasePlayer for pev, and call spawn
|
|
pPlayer->Spawn() ;
|
|
|
|
// Reset interpolation during first frame
|
|
pPlayer->pev->effects |= EF_NOINTERP;
|
|
}
|
|
|
|
//// HOST_SAY
|
|
// String comes in as
|
|
// say blah blah blah
|
|
// or as
|
|
// blah blah blah
|
|
//
|
|
void Host_Say( edict_t *pEntity, int teamonly )
|
|
{
|
|
CBasePlayer *client;
|
|
int j;
|
|
char *p;
|
|
char text[128];
|
|
char szTemp[256];
|
|
const char *cpSay = "say";
|
|
const char *cpSayTeam = "say_team";
|
|
const char *pcmd = CMD_ARGV(0);
|
|
|
|
// We can get a raw string now, without the "say " prepended
|
|
if ( CMD_ARGC() == 0 )
|
|
return;
|
|
|
|
entvars_t *pev = &pEntity->v;
|
|
CBasePlayer* player = GetClassPtr((CBasePlayer *)pev);
|
|
|
|
//Not yet.
|
|
if ( player->m_flNextChatTime > gpGlobals->time )
|
|
return;
|
|
|
|
if ( !stricmp( pcmd, cpSay) || !stricmp( pcmd, cpSayTeam ) )
|
|
{
|
|
if ( CMD_ARGC() >= 2 )
|
|
{
|
|
p = (char *)CMD_ARGS();
|
|
}
|
|
else
|
|
{
|
|
// say with a blank message, nothing to do
|
|
return;
|
|
}
|
|
}
|
|
else // Raw text, need to prepend argv[0]
|
|
{
|
|
if ( CMD_ARGC() >= 2 )
|
|
{
|
|
sprintf( szTemp, "%s %s", ( char * )pcmd, (char *)CMD_ARGS() );
|
|
}
|
|
else
|
|
{
|
|
// Just a one word command, use the first word...sigh
|
|
sprintf( szTemp, "%s", ( char * )pcmd );
|
|
}
|
|
p = szTemp;
|
|
}
|
|
|
|
// remove quotes if present
|
|
if (*p == '"')
|
|
{
|
|
p++;
|
|
p[strlen(p)-1] = 0;
|
|
}
|
|
|
|
// make sure the text has content
|
|
char *pc;
|
|
for ( pc = p; pc != NULL && *pc != 0; pc++ )
|
|
{
|
|
if ( isprint( *pc ) && !isspace( *pc ) )
|
|
{
|
|
pc = NULL; // we've found an alphanumeric character, so text is valid
|
|
break;
|
|
}
|
|
}
|
|
if ( pc != NULL )
|
|
return; // no character found, so say nothing
|
|
|
|
// turn on color set 2 (color on, no sound)
|
|
if ( teamonly )
|
|
sprintf( text, "%c(TEAM) %s: ", 2, STRING( pEntity->v.netname ) );
|
|
else
|
|
sprintf( text, "%c%s: ", 2, STRING( pEntity->v.netname ) );
|
|
|
|
j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator
|
|
if ( (int)strlen(p) > j )
|
|
p[j] = 0;
|
|
|
|
strcat( text, p );
|
|
strcat( text, "\n" );
|
|
|
|
|
|
player->m_flNextChatTime = gpGlobals->time + CHAT_INTERVAL;
|
|
|
|
// loop through all players
|
|
// Start with the first player.
|
|
// This may return the world in single player if the client types something between levels or during spawn
|
|
// so check it, or it will infinite loop
|
|
|
|
client = NULL;
|
|
while ( ((client = (CBasePlayer*)UTIL_FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) )
|
|
{
|
|
if ( !client->pev )
|
|
continue;
|
|
|
|
if ( client->edict() == pEntity )
|
|
continue;
|
|
|
|
if ( !(client->IsNetClient()) ) // Not a client ? (should never be true)
|
|
continue;
|
|
|
|
if ( teamonly && g_pGameRules->PlayerRelationship(client, CBaseEntity::Instance(pEntity)) != GR_TEAMMATE )
|
|
continue;
|
|
|
|
MESSAGE_BEGIN( MSG_ONE, gmsg.SayText, NULL, client->pev );
|
|
WRITE_BYTE( ENTINDEX(pEntity) );
|
|
WRITE_STRING( text );
|
|
MESSAGE_END();
|
|
|
|
}
|
|
|
|
// print to the sending client
|
|
MESSAGE_BEGIN( MSG_ONE, gmsg.SayText, NULL, &pEntity->v );
|
|
WRITE_BYTE( ENTINDEX(pEntity) );
|
|
WRITE_STRING( text );
|
|
MESSAGE_END();
|
|
|
|
// echo to server console
|
|
g_engfuncs.pfnServerPrint( text );
|
|
|
|
char * temp;
|
|
if ( teamonly )
|
|
temp = "say_team";
|
|
else
|
|
temp = "say";
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
ClientCommand
|
|
called each time a player uses a "cmd" command
|
|
============
|
|
*/
|
|
extern float g_flWeaponCheat;
|
|
|
|
// Use CMD_ARGV, CMD_ARGV, and CMD_ARGC to get pointers the character string command.
|
|
void ClientCommand( edict_t *pEntity )
|
|
{
|
|
const char *pcmd = CMD_ARGV(0);
|
|
const char *pstr;
|
|
|
|
// Is the client spawned yet?
|
|
if( !pEntity->pvPrivateData )
|
|
return;
|
|
|
|
entvars_t *pev = &pEntity->v;
|
|
|
|
if( FStrEq( pcmd, "noclip" ))
|
|
{
|
|
if( pEntity->v.movetype == MOVETYPE_WALK )
|
|
{
|
|
pEntity->v.movetype = MOVETYPE_NOCLIP;
|
|
Msg( "noclip on\n" );
|
|
}
|
|
else
|
|
{
|
|
pEntity->v.movetype = MOVETYPE_WALK;
|
|
Msg( "noclip off\n" );
|
|
}
|
|
}
|
|
else if( FStrEq( pcmd, "god" ))
|
|
{
|
|
pEntity->v.flags = pEntity->v.flags ^ FL_GODMODE;
|
|
|
|
if( !(pEntity->v.flags & FL_GODMODE))
|
|
ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, "godmode OFF\n" );
|
|
else ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, "godmode ON\n" );
|
|
}
|
|
else if ( FStrEq( pcmd, "hud_color") ) //LRC
|
|
{
|
|
if (CMD_ARGC() == 4)
|
|
{
|
|
int col = (atoi(CMD_ARGV(1)) & 255) << 16;
|
|
col += (atoi(CMD_ARGV(2)) & 255) << 8;
|
|
col += (atoi(CMD_ARGV(3)) & 255);
|
|
MESSAGE_BEGIN( MSG_ONE, gmsg.HUDColor, NULL, &pEntity->v );
|
|
WRITE_LONG( col );
|
|
MESSAGE_END();
|
|
}
|
|
else
|
|
{
|
|
ALERT( at_console, "Usage: hud_color <red green blue>\n" );
|
|
}
|
|
}
|
|
else if( FStrEq( pcmd, "fire" )) //LRC - trigger entities manually
|
|
{
|
|
if( g_flWeaponCheat )
|
|
{
|
|
CBaseEntity *pPlayer = CBaseEntity::Instance(pEntity);
|
|
if( CMD_ARGC() > 1 )
|
|
{
|
|
UTIL_FireTargets( CMD_ARGV( 1 ), pPlayer, pPlayer, USE_TOGGLE );
|
|
}
|
|
else
|
|
{
|
|
TraceResult tr;
|
|
UTIL_MakeVectors( pev->viewangles );
|
|
UTIL_TraceLine( pev->origin + pev->view_ofs,
|
|
pev->origin + pev->view_ofs + gpGlobals->v_forward * 1000,
|
|
dont_ignore_monsters, pEntity, &tr );
|
|
|
|
if( tr.pHit )
|
|
{
|
|
CBaseEntity *pHitEnt = CBaseEntity::Instance(tr.pHit);
|
|
if( pHitEnt )
|
|
{
|
|
pHitEnt->Use( pPlayer, pPlayer, USE_TOGGLE, 0 );
|
|
ALERT( at_console, "Fired %s \"%s\"\n", STRING( pHitEnt->pev->classname ), STRING( pHitEnt->pev->targetname ));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( FStrEq( pcmd, "debug" ))
|
|
{
|
|
if( g_flWeaponCheat )
|
|
{
|
|
CBaseEntity *pPlayer = CBaseEntity::Instance( pEntity );
|
|
if( CMD_ARGC() > 1 )
|
|
{
|
|
UTIL_FireTargets( CMD_ARGV( 1 ), pPlayer, pPlayer, USE_SHOWINFO );
|
|
}
|
|
else
|
|
{
|
|
TraceResult tr;
|
|
UTIL_MakeVectors( pev->viewangles );
|
|
UTIL_TraceLine( pev->origin + pev->view_ofs,
|
|
pev->origin + pev->view_ofs + gpGlobals->v_forward * 1000,
|
|
dont_ignore_monsters, pEntity, &tr );
|
|
|
|
if( tr.pHit )
|
|
{
|
|
CBaseEntity *pHitEnt = CBaseEntity::Instance(tr.pHit);
|
|
if( pHitEnt )
|
|
{
|
|
pHitEnt->Use( pPlayer, pPlayer, USE_SHOWINFO, 0 );
|
|
ALERT( at_console, "Fired %s \"%s\"\n", STRING( pHitEnt->pev->classname), STRING(pHitEnt->pev->targetname));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( FStrEq(pcmd, "say" ) )
|
|
{
|
|
Host_Say( pEntity, 0 );
|
|
}
|
|
else if ( FStrEq(pcmd, "game_over" ) )
|
|
{
|
|
if(IsMultiplayer()) g_pGameRules->EndMultiplayerGame(); //loading next map
|
|
// FIXME: return to main menu here
|
|
}
|
|
else if( FStrEq( pcmd, "gametitle" ))
|
|
{
|
|
MESSAGE_BEGIN( MSG_ONE, gmsg.ShowGameTitle, NULL, ENT( pev ));
|
|
WRITE_BYTE( 0 );
|
|
MESSAGE_END();
|
|
}
|
|
else if( FStrEq( pcmd, "intermission" ))
|
|
{
|
|
MESSAGE_BEGIN( MSG_ONE, gmsg.Intermission, NULL, ENT( pev ));
|
|
MESSAGE_END();
|
|
}
|
|
else if( FStrEq( pcmd, "fullupdate" ))
|
|
{
|
|
GetClassPtr((CBasePlayer *)pev)->ForceClientDllUpdate();
|
|
}
|
|
else if( FStrEq( pcmd, "give" ))
|
|
{
|
|
if ( g_flWeaponCheat != 0.0)
|
|
{
|
|
int iszItem = ALLOC_STRING( CMD_ARGV(1) ); // Make a copy of the classname
|
|
GetClassPtr((CBasePlayer *)pev)->GiveNamedItem( STRING(iszItem) );
|
|
}
|
|
}
|
|
|
|
else if ( FStrEq(pcmd, "drop" ) )
|
|
{
|
|
// player is dropping an item.
|
|
GetClassPtr((CBasePlayer *)pev)->DropPlayerItem((char *)CMD_ARGV(1));
|
|
}
|
|
else if( FStrEq( pcmd, "fov" ))
|
|
{
|
|
if( g_flWeaponCheat && CMD_ARGC() > 1 )
|
|
{
|
|
GetClassPtr((CBasePlayer *)pev)->pev->fov = atof( CMD_ARGV( 1 ));
|
|
}
|
|
else
|
|
{
|
|
ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, UTIL_VarArgs( "\"fov\" is \"%g\"\n", (int)GetClassPtr((CBasePlayer *)pev)->pev->fov ));
|
|
}
|
|
}
|
|
else if ( FStrEq(pcmd, "use" ) )
|
|
{
|
|
GetClassPtr((CBasePlayer *)pev)->SelectItem((char *)CMD_ARGV(1));
|
|
}
|
|
else if (((pstr = strstr(pcmd, "weapon_")) != NULL) && (pstr == pcmd))
|
|
{
|
|
GetClassPtr((CBasePlayer *)pev)->SelectItem(pcmd);
|
|
}
|
|
else if( FStrEq( pcmd, "lastinv" ))
|
|
{
|
|
GetClassPtr((CBasePlayer *)pev)->SelectLastItem();
|
|
}
|
|
else if( FStrEq( pcmd, "spectate" )) // added for proxy support
|
|
{
|
|
CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev);
|
|
|
|
edict_t *pentSpawnSpot = g_pGameRules->GetPlayerSpawnSpot( pPlayer );
|
|
pPlayer->StartObserver( pev->origin, VARS(pentSpawnSpot)->angles);
|
|
}
|
|
else if ( g_pGameRules->ClientCommand( GetClassPtr((CBasePlayer *)pev), pcmd ) )
|
|
{
|
|
// MenuSelect returns true only if the command is properly handled, so don't print a warning
|
|
}
|
|
else
|
|
{
|
|
// tell the user they entered an unknown command
|
|
char command[128];
|
|
|
|
// check the length of the command (prevents crash)
|
|
// max total length is 192 ...and we're adding a string below ("Unknown command: %s\n")
|
|
strncpy( command, pcmd, 127 );
|
|
command[127] = '\0';
|
|
|
|
// tell the user they entered an unknown command
|
|
ClientPrint( &pEntity->v, HUD_PRINTCONSOLE, UTIL_VarArgs( "Unknown command: %s\n", command ) );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
ClientUserInfoChanged
|
|
|
|
called after the player changes
|
|
userinfo - gives dll a chance to modify it before
|
|
it gets sent into the rest of the engine.
|
|
========================
|
|
*/
|
|
void ClientUserInfoChanged( edict_t *pEntity, char *infobuffer )
|
|
{
|
|
// Is the client spawned yet?
|
|
if ( !pEntity->pvPrivateData )
|
|
return;
|
|
|
|
// msg everyone if someone changes their name, and it isn't the first time (changing no name to current name)
|
|
if ( pEntity->v.netname && STRING(pEntity->v.netname)[0] != 0 && !FStrEq( STRING(pEntity->v.netname), g_engfuncs.pfnInfoKeyValue( infobuffer, "name" )) )
|
|
{
|
|
char sName[256];
|
|
char *pName = g_engfuncs.pfnInfoKeyValue( infobuffer, "name" );
|
|
strncpy( sName, pName, sizeof(sName) - 1 );
|
|
sName[ sizeof(sName) - 1 ] = '\0';
|
|
|
|
// First parse the name and remove any %'s
|
|
for ( char *pApersand = sName; pApersand != NULL && *pApersand != 0; pApersand++ )
|
|
{
|
|
// Replace it with a space
|
|
if ( *pApersand == '%' )
|
|
*pApersand = ' ';
|
|
}
|
|
|
|
// Set the name
|
|
g_engfuncs.pfnSetClientKeyValue( ENTINDEX(pEntity), infobuffer, "name", sName );
|
|
|
|
char text[256];
|
|
sprintf( text, "* %s changed name to %s\n", STRING(pEntity->v.netname), g_engfuncs.pfnInfoKeyValue( infobuffer, "name" ) );
|
|
MESSAGE_BEGIN( MSG_ALL, gmsg.SayText, NULL );
|
|
WRITE_BYTE( ENTINDEX(pEntity) );
|
|
WRITE_STRING( text );
|
|
MESSAGE_END();
|
|
}
|
|
g_pGameRules->ClientUserInfoChanged( GetClassPtr((CBasePlayer *)&pEntity->v), infobuffer );
|
|
}
|
|
|
|
void ServerDeactivate( void )
|
|
{
|
|
// make sure they reinitialise the World in the next server
|
|
g_pWorld = NULL;
|
|
|
|
// It's possible that the engine will call this function more times than is necessary
|
|
// Therefore, only run it one time for each call to ServerActivate
|
|
if ( g_serveractive != 1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
g_serveractive = 0;
|
|
|
|
// Peform any shutdown operations here...
|
|
//
|
|
}
|
|
|
|
void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
|
|
{
|
|
int i;
|
|
CBaseEntity *pClass;
|
|
|
|
// Every call to ServerActivate should be matched by a call to ServerDeactivate
|
|
g_serveractive = 1;
|
|
// Clients have not been initialized yet
|
|
for ( i = 0; i < edictCount; i++ )
|
|
{
|
|
if ( pEdictList[i].free )
|
|
continue;
|
|
|
|
// Clients aren't necessarily initialized until ClientPutInServer()
|
|
if ( i < clientMax || !pEdictList[i].pvPrivateData )
|
|
continue;
|
|
|
|
pClass = CBaseEntity::Instance( &pEdictList[i] );
|
|
// Activate this entity if it's got a class & isn't dormant
|
|
if ( pClass && !(pClass->pev->flags & FL_DORMANT) )
|
|
{
|
|
pClass->SetupPhysics();
|
|
pClass->Activate();
|
|
}
|
|
else Msg( "Can't instance %s\n", STRING(pEdictList[i].v.classname) );
|
|
}
|
|
|
|
// Link user messages here to make sure first client can get them...
|
|
NewLevel = FALSE;
|
|
LinkUserMessages();
|
|
}
|
|
|
|
// a cached version of gpGlobals->frametime. The engine sets frametime to 0 if the player is frozen... so we just cache it in prethink,
|
|
// allowing it to be restored later and used by CheckDesiredList.
|
|
float cached_frametime = 0.0f;
|
|
|
|
/*
|
|
================
|
|
PlayerPreThink
|
|
|
|
Called every frame before physics are run
|
|
================
|
|
*/
|
|
void PlayerPreThink( edict_t *pEntity )
|
|
{
|
|
entvars_t *pev = &pEntity->v;
|
|
CBasePlayer *pPlayer = (CBasePlayer *)GET_PRIVATE(pEntity);
|
|
|
|
if (pPlayer)pPlayer->PreThink( );
|
|
cached_frametime = gpGlobals->frametime;
|
|
}
|
|
|
|
/*
|
|
================
|
|
PlayerPostThink
|
|
|
|
Called every frame after physics are run
|
|
================
|
|
*/
|
|
void PlayerPostThink( edict_t *pEntity )
|
|
{
|
|
entvars_t *pev = &pEntity->v;
|
|
CBasePlayer *pPlayer = (CBasePlayer *)GET_PRIVATE(pEntity);
|
|
|
|
if (pPlayer)pPlayer->PostThink( );
|
|
|
|
// use the old frametime, even if the engine has reset it
|
|
gpGlobals->frametime = cached_frametime;
|
|
}
|
|
|
|
void BuildLevelList( void )
|
|
{
|
|
// retrieve the pointer to the save data
|
|
SAVERESTOREDATA *pSaveData = (SAVERESTOREDATA *)gpGlobals->pSaveData;
|
|
|
|
if ( pSaveData )
|
|
pSaveData->connectionCount = BuildChangeList( pSaveData->levelList, MAX_LEVEL_CONNECTIONS );
|
|
}
|
|
|
|
//=======================================================================
|
|
// Build ChangeLevel List
|
|
//=======================================================================
|
|
int AddTransitionToList( LEVELLIST *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark )
|
|
{
|
|
int i;
|
|
|
|
if ( !pLevelList || !pMapName || !pLandmarkName || !pentLandmark ) return 0;
|
|
|
|
for ( i = 0; i < listCount; i++ )
|
|
{
|
|
if ( pLevelList[i].pentLandmark == pentLandmark && strcmp( pLevelList[i].mapName, pMapName ) == 0 )
|
|
return 0;
|
|
}
|
|
strcpy( pLevelList[listCount].mapName, pMapName );
|
|
strcpy( pLevelList[listCount].landmarkName, pLandmarkName );
|
|
pLevelList[listCount].pentLandmark = pentLandmark;
|
|
pLevelList[listCount].vecLandmarkOrigin = VARS(pentLandmark)->origin;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int BuildChangeList( LEVELLIST *pLevelList, int maxList )
|
|
{
|
|
edict_t *pentLandmark;
|
|
int i, count;
|
|
|
|
count = 0;
|
|
|
|
// Find all of the possible level changes on this BSP
|
|
CBaseEntity *pChangelevel = UTIL_FindEntityByClassname( NULL, "trigger_changelevel" );
|
|
|
|
if ( !pChangelevel ) return NULL;
|
|
|
|
while ( pChangelevel )
|
|
{
|
|
// Find the corresponding landmark
|
|
pentLandmark = UTIL_FindLandmark( pChangelevel->pev->message );
|
|
if ( pentLandmark )
|
|
{
|
|
// Build a list of unique transitions
|
|
DevMsg("Map name %s, landmark name %s\n", STRING(pChangelevel->pev->netname), STRING(pChangelevel->pev->message));
|
|
if ( AddTransitionToList( pLevelList, count, (char *)STRING(pChangelevel->pev->netname), (char *)STRING(pChangelevel->pev->message), pentLandmark ))
|
|
{
|
|
count++;
|
|
if ( count >= maxList )break;//List is FULL!!!
|
|
}
|
|
}
|
|
pChangelevel = UTIL_FindEntityByClassname( pChangelevel, "trigger_changelevel" );
|
|
}
|
|
|
|
if ( gpGlobals->pSaveData && ((SAVERESTOREDATA *)gpGlobals->pSaveData)->pTable )
|
|
{
|
|
CSave saveHelper( (SAVERESTOREDATA *)gpGlobals->pSaveData );
|
|
|
|
for ( i = 0; i < count; i++ )
|
|
{
|
|
int j, entityCount = 0;
|
|
CBaseEntity *pEntList[ MAX_TRANSITION_ENTITY ];
|
|
int entityFlags[ MAX_TRANSITION_ENTITY ];
|
|
|
|
// Follow the linked list of entities in the PVS of the transition landmark
|
|
edict_t *pent = UTIL_EntitiesInPVS( pLevelList[i].pentLandmark );
|
|
|
|
// Build a list of valid entities in this linked list (we're going to use pent->v.chain again)
|
|
while ( !FNullEnt( pent ) )
|
|
{
|
|
CBaseEntity *pEntity = CBaseEntity::Instance(pent);
|
|
if ( pEntity )
|
|
{
|
|
int caps = pEntity->ObjectCaps();
|
|
if ( !(caps & FCAP_DONT_SAVE) )
|
|
{
|
|
int flags = 0;
|
|
|
|
// If this entity can be moved or is global, mark it
|
|
if ( caps & FCAP_ACROSS_TRANSITION ) flags |= FENTTABLE_MOVEABLE;
|
|
if ( pEntity->pev->globalname && !pEntity->IsDormant() ) flags |= FENTTABLE_GLOBAL;
|
|
if ( flags )
|
|
{
|
|
pEntList[ entityCount ] = pEntity;
|
|
entityFlags[ entityCount ] = flags;
|
|
entityCount++;
|
|
if ( entityCount > MAX_TRANSITION_ENTITY ) Msg("ERROR: Too many entities across a transition!" );
|
|
}
|
|
}
|
|
}
|
|
pent = pent->v.chain;
|
|
}
|
|
|
|
for ( j = 0; j < entityCount; j++ )
|
|
{
|
|
// Check to make sure the entity isn't screened out by a trigger_transition
|
|
if ( entityFlags[j] && UTIL_FindTransition( pEntList[j], pLevelList[i].landmarkName ) )
|
|
{
|
|
// Mark entity table with 1<<i
|
|
int index = saveHelper.EntityIndex( pEntList[j] );
|
|
// Flag it with the level number
|
|
saveHelper.EntityFlagsSet( index, entityFlags[j] | (1<<i) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//=======================================================================
|
|
// send messages - update all messages, at spawned player
|
|
//=======================================================================
|
|
void ServerPostActivate( void )
|
|
{
|
|
edict_t *pEdict = INDEXENT( 0 );
|
|
CBasePlayer *plr = (CBasePlayer*)UTIL_PlayerByIndex( 1 );
|
|
CBaseEntity *pClass;
|
|
|
|
if( !pEdict || MSGSended ) return; // player spawned ?
|
|
// NOTE: Time to affect is obsolete delay before sending message
|
|
// Tune multiplayer time if need
|
|
|
|
if( MsgDelay > gpGlobals->time ) return;
|
|
if( plr && !plr->m_fInitHUD && !gInitHUD )
|
|
{
|
|
for ( int i = 0; i < gpGlobals->maxEntities; i++, pEdict++ )
|
|
{
|
|
if( pEdict->free ) continue;
|
|
pClass = CBaseEntity::Instance( pEdict );
|
|
if( pClass && !( pClass->pev->flags & FL_DORMANT ))
|
|
{
|
|
pClass->PostActivate();
|
|
}
|
|
}
|
|
MSGSended = TRUE;//messages sucessfully sended
|
|
}
|
|
}
|
|
|
|
//
|
|
// GLOBALS ASSUMED SET: g_ulFrameCount
|
|
//
|
|
void StartFrame( void )
|
|
{
|
|
// Msg(" frametime %g\n", gpGlobals->frametime );
|
|
if ( g_pGameRules )g_pGameRules->Think();
|
|
if ( g_fGameOver ) return;
|
|
|
|
gpGlobals->teamplay = (CVAR_GET_FLOAT( "mp_teamplay" ) == 1.0f) ? TRUE : FALSE;
|
|
g_ulFrameCount++;
|
|
|
|
ServerPostActivate(); // called once
|
|
PhysicsFrame();
|
|
PhysicsPostFrame();
|
|
|
|
}
|
|
|
|
void EndFrame( void )
|
|
{
|
|
}
|
|
|
|
int ServerClassifyEdict( edict_t *pentToClassify )
|
|
{
|
|
if( !pentToClassify ) return ED_SPAWNED;
|
|
|
|
CBaseEntity *pClass;
|
|
|
|
pClass = CBaseEntity::Instance( pentToClassify );
|
|
if( !pClass ) return ED_SPAWNED;
|
|
|
|
const char *classname = STRING( pClass->pev->classname );
|
|
|
|
if( !strnicmp( "worldspawn", classname, 10 ))
|
|
{
|
|
return ED_WORLDSPAWN;
|
|
}
|
|
|
|
// first pass: determine type by explicit parms
|
|
if( pClass->pev->solid == SOLID_TRIGGER )
|
|
{
|
|
if( !stricmp( classname, "trigger_teleport" ))
|
|
return ED_AMBIENT;
|
|
else return ED_TRIGGER; // never sending to client
|
|
}
|
|
else if( pClass->pev->movetype == MOVETYPE_PHYSIC )
|
|
return ED_RIGIDBODY;
|
|
else if( pClass->pev->solid == SOLID_BSP || pClass->pev->origin == g_vecZero )
|
|
{
|
|
if( pClass->pev->movetype == MOVETYPE_CONVEYOR )
|
|
return ED_MOVER;
|
|
else if( pClass->pev->flags & FL_WORLDBRUSH )
|
|
return ED_BSPBRUSH;
|
|
else if( pClass->pev->movetype == MOVETYPE_PUSH )
|
|
return ED_MOVER;
|
|
else if( pClass->pev->movetype == MOVETYPE_NONE )
|
|
return ED_BSPBRUSH;
|
|
}
|
|
else if( pClass->pev->flags & FL_MONSTER )
|
|
return ED_MONSTER;
|
|
else if( pClass->pev->flags & FL_CLIENT )
|
|
return ED_CLIENT;
|
|
else if( !pClass->pev->modelindex && !pClass->pev->aiment )
|
|
{
|
|
if( pClass->pev->noise || pClass->pev->noise1 || pClass->pev->noise2 || pClass->pev->noise3 )
|
|
return ED_AMBIENT;
|
|
return ED_STATIC; // never sending to client
|
|
}
|
|
|
|
// mark as normal
|
|
if( pClass->pev->modelindex || pClass->pev->noise )
|
|
return ED_NORMAL;
|
|
|
|
// fail to classify :-(
|
|
return ED_SPAWNED;
|
|
}
|
|
|
|
void UpdateEntityState( entity_state_t *to, edict_t *from, int baseline )
|
|
{
|
|
int i;
|
|
|
|
if( !to || !from ) return;
|
|
|
|
CBaseEntity *pNet;
|
|
|
|
pNet = CBaseEntity::Instance( from );
|
|
if( !pNet ) return;
|
|
|
|
// setup some special edict flags (engine will clearing them after the current frame)
|
|
if( to->modelindex != pNet->pev->modelindex )
|
|
to->ed_flags |= ESF_NODELTA;
|
|
|
|
// always set nodelta's for baseline
|
|
if( baseline ) to->ed_flags |= ESF_NODELTA;
|
|
|
|
// copy progs values to state
|
|
to->solid = (solid_t)pNet->pev->solid;
|
|
|
|
to->origin = pNet->pev->origin;
|
|
to->angles = pNet->pev->angles;
|
|
to->modelindex = pNet->pev->modelindex;
|
|
to->health = pNet->pev->health;
|
|
to->skin = pNet->pev->skin; // studio model skin
|
|
to->body = pNet->pev->body; // studio model submodel
|
|
to->effects = pNet->pev->effects; // shared client and render flags
|
|
to->renderfx = pNet->pev->renderfx; // renderer flags
|
|
to->rendermode = pNet->pev->rendermode; // rendering mode
|
|
to->renderamt = pNet->pev->renderamt; // alpha value
|
|
to->animtime = (int)(1000.0 * pNet->pev->animtime) * 0.001; // sequence time
|
|
to->scale = pNet->pev->scale; // shared client and render flags
|
|
to->movetype = (movetype_t)pNet->pev->movetype;
|
|
to->frame = pNet->pev->frame; // any model current frame
|
|
to->contents = pNet->pev->contents; // physic contents
|
|
to->framerate = pNet->pev->framerate;
|
|
to->flags = pNet->pev->flags;
|
|
to->rendercolor = pNet->pev->rendercolor;
|
|
|
|
// studio model sequence
|
|
if( pNet->pev->sequence != -1 ) to->sequence = pNet->pev->sequence;
|
|
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
// copy blendings and bone ctrlrs
|
|
to->blending[i] = pNet->pev->blending[i];
|
|
to->controller[i] = pNet->pev->controller[i];
|
|
}
|
|
|
|
if( to->ed_type == ED_MOVER || to->ed_type == ED_BSPBRUSH )
|
|
{
|
|
// these needs to right calculate direction of scroll texture
|
|
to->velocity = pNet->pev->movedir;
|
|
}
|
|
if( to->ed_type == ED_CLIENT )
|
|
{
|
|
if( pNet->pev->fixangle )
|
|
{
|
|
to->ed_flags |= ESF_NO_PREDICTION;
|
|
pNet->pev->fixangle = 0;
|
|
}
|
|
|
|
if( pNet->pev->teleport_time )
|
|
{
|
|
to->ed_flags |= ESF_NO_PREDICTION;
|
|
to->ed_flags |= ESF_NODELTA;
|
|
pNet->pev->teleport_time = 0.0f;
|
|
}
|
|
|
|
if( pNet->pev->viewmodel )
|
|
to->viewmodel = MODEL_INDEX( STRING( pNet->pev->viewmodel ));
|
|
else to->viewmodel = 0;
|
|
|
|
if( pNet->pev->aiment )
|
|
to->aiment = ENTINDEX( pNet->pev->aiment );
|
|
else to->aiment = 0;
|
|
|
|
to->viewoffset = pNet->pev->view_ofs;
|
|
to->viewangles = pNet->pev->viewangles;
|
|
to->punch_angles = pNet->pev->punchangle;
|
|
|
|
// playermodel sequence, that will be playing on a client
|
|
to->gaitsequence = pNet->pev->gaitsequence;
|
|
if( pNet->pev->weaponmodel != iStringNull )
|
|
to->weaponmodel = MODEL_INDEX( STRING( pNet->pev->weaponmodel ));
|
|
else to->weaponmodel = 0;
|
|
to->weapons = pNet->pev->weapons;
|
|
|
|
// clamp fov
|
|
if( pNet->pev->fov < 0.0 ) pNet->pev->fov = 0.0;
|
|
if( pNet->pev->fov > 160 ) pNet->pev->fov = 160;
|
|
to->fov = pNet->pev->fov;
|
|
}
|
|
else if( to->ed_type == ED_AMBIENT )
|
|
{
|
|
if( pNet->pev->solid == SOLID_TRIGGER )
|
|
{
|
|
Vector midPoint;
|
|
|
|
// NOTE: no reason to compute this shit on the client - save bandwidth
|
|
midPoint = pNet->pev->mins + pNet->pev->maxs * 0.5f;
|
|
to->origin += midPoint;
|
|
}
|
|
}
|
|
else if( to->ed_type == ED_MOVER )
|
|
{
|
|
// FIXME: send mins\maxs for sound spatialization and entity prediction ?
|
|
}
|
|
}
|
|
|
|
void ClientPrecache( void )
|
|
{
|
|
// Material System!!! move this in next versions
|
|
PRECACHE_SOUND("player/pl_step1.wav"); // walk on concrete
|
|
PRECACHE_SOUND("player/pl_step2.wav");
|
|
PRECACHE_SOUND("player/pl_step3.wav");
|
|
PRECACHE_SOUND("player/pl_step4.wav");
|
|
|
|
PRECACHE_SOUND("common/npc_step1.wav"); // NPC walk on concrete
|
|
PRECACHE_SOUND("common/npc_step2.wav");
|
|
PRECACHE_SOUND("common/npc_step3.wav");
|
|
PRECACHE_SOUND("common/npc_step4.wav");
|
|
|
|
PRECACHE_SOUND("player/pl_metal1.wav"); // walk on metal
|
|
PRECACHE_SOUND("player/pl_metal2.wav");
|
|
PRECACHE_SOUND("player/pl_metal3.wav");
|
|
PRECACHE_SOUND("player/pl_metal4.wav");
|
|
|
|
PRECACHE_SOUND("player/pl_dirt1.wav"); // walk on dirt
|
|
PRECACHE_SOUND("player/pl_dirt2.wav");
|
|
PRECACHE_SOUND("player/pl_dirt3.wav");
|
|
PRECACHE_SOUND("player/pl_dirt4.wav");
|
|
|
|
PRECACHE_SOUND("player/pl_duct1.wav"); // walk in duct
|
|
PRECACHE_SOUND("player/pl_duct2.wav");
|
|
PRECACHE_SOUND("player/pl_duct3.wav");
|
|
PRECACHE_SOUND("player/pl_duct4.wav");
|
|
|
|
PRECACHE_SOUND("player/pl_grate1.wav"); // walk on grate
|
|
PRECACHE_SOUND("player/pl_grate2.wav");
|
|
PRECACHE_SOUND("player/pl_grate3.wav");
|
|
PRECACHE_SOUND("player/pl_grate4.wav");
|
|
|
|
PRECACHE_SOUND("player/pl_slosh1.wav"); // walk in shallow water
|
|
PRECACHE_SOUND("player/pl_slosh2.wav");
|
|
PRECACHE_SOUND("player/pl_slosh3.wav");
|
|
PRECACHE_SOUND("player/pl_slosh4.wav");
|
|
|
|
PRECACHE_SOUND("player/pl_tile1.wav"); // walk on tile
|
|
PRECACHE_SOUND("player/pl_tile2.wav");
|
|
PRECACHE_SOUND("player/pl_tile3.wav");
|
|
PRECACHE_SOUND("player/pl_tile4.wav");
|
|
PRECACHE_SOUND("player/pl_tile5.wav");
|
|
|
|
PRECACHE_SOUND("player/pl_swim1.wav"); // breathe bubbles
|
|
PRECACHE_SOUND("player/pl_swim2.wav");
|
|
PRECACHE_SOUND("player/pl_swim3.wav");
|
|
PRECACHE_SOUND("player/pl_swim4.wav");
|
|
|
|
PRECACHE_SOUND("player/pl_ladder1.wav"); // climb ladder rung
|
|
PRECACHE_SOUND("player/pl_ladder2.wav");
|
|
PRECACHE_SOUND("player/pl_ladder3.wav");
|
|
PRECACHE_SOUND("player/pl_ladder4.wav");
|
|
|
|
PRECACHE_SOUND("player/pl_wade1.wav"); // wade in water
|
|
PRECACHE_SOUND("player/pl_wade2.wav");
|
|
PRECACHE_SOUND("player/pl_wade3.wav");
|
|
PRECACHE_SOUND("player/pl_wade4.wav");
|
|
}
|
|
|
|
/*
|
|
===============
|
|
GetGameDescription
|
|
|
|
Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2
|
|
===============
|
|
*/
|
|
const char *GetGameDescription( void )
|
|
{
|
|
char token[256];
|
|
char szbuffer[128];
|
|
char *pfile = (char *)LOAD_FILE( "gameinfo.txt", NULL );
|
|
|
|
if( pfile )
|
|
{
|
|
while( pfile )
|
|
{
|
|
if( !stricmp( token, "title" ))
|
|
{
|
|
pfile = COM_ParseFile(pfile, token);
|
|
sprintf( szbuffer, "%s ", token );
|
|
strcat( text, szbuffer );
|
|
}
|
|
else if( !stricmp( token, "version" ))
|
|
{
|
|
pfile = COM_ParseFile(pfile, token);
|
|
strcat( text, token );
|
|
}
|
|
pfile = COM_ParseFile(pfile, token);
|
|
}
|
|
COM_FreeFile( pfile );
|
|
return text;
|
|
}
|
|
return "Half-Life";
|
|
}
|
|
|
|
//=======================================================================
|
|
// Link User Messages - register new client messages here
|
|
//=======================================================================
|
|
void LinkUserMessages( void )
|
|
{
|
|
// already taken care of?
|
|
if( gmsg.SelAmmo ) return;
|
|
|
|
memset( &gmsg, 0, sizeof( gmsg ));
|
|
|
|
//messages affect only player
|
|
gmsg.SelAmmo = REG_USER_MSG("SelAmmo", sizeof(SelAmmo));
|
|
gmsg.Intermission = REG_USER_MSG( "Intermission", 0 );
|
|
gmsg.CurWeapon = REG_USER_MSG("CurWeapon", 3);
|
|
gmsg.GeigerRange = REG_USER_MSG("Geiger", 1);
|
|
gmsg.Flashlight = REG_USER_MSG("Flashlight", 2);
|
|
gmsg.FlashBattery = REG_USER_MSG("FlashBat", 1);
|
|
gmsg.Health = REG_USER_MSG( "Health", 1 );
|
|
gmsg.Damage = REG_USER_MSG( "Damage", 18 );
|
|
gmsg.Battery = REG_USER_MSG( "Battery", 2);
|
|
gmsg.Train = REG_USER_MSG( "Train", 1);
|
|
gmsg.SayText = REG_USER_MSG( "SayText", -1 );
|
|
gmsg.TextMsg = REG_USER_MSG( "TextMsg", -1 );
|
|
gmsg.WeaponList = REG_USER_MSG("WeaponList", -1);
|
|
gmsg.ResetHUD = REG_USER_MSG("ResetHUD", 1);
|
|
gmsg.InitHUD = REG_USER_MSG("InitHUD", 0 );
|
|
gmsg.HUDColor = REG_USER_MSG( "HUDColor", 4 );
|
|
gmsg.DeathMsg = REG_USER_MSG( "DeathMsg", -1 );
|
|
gmsg.ScoreInfo = REG_USER_MSG( "ScoreInfo", 9 );
|
|
gmsg.TeamInfo = REG_USER_MSG( "TeamInfo", -1 );
|
|
gmsg.TeamScore = REG_USER_MSG( "TeamScore", -1 );
|
|
gmsg.GameMode = REG_USER_MSG( "GameMode", 1 );
|
|
gmsg.MOTD = REG_USER_MSG( "MOTD", -1 );
|
|
gmsg.ServerName = REG_USER_MSG( "ServerName", -1 );
|
|
gmsg.AmmoPickup = REG_USER_MSG( "AmmoPickup", 2 );
|
|
gmsg.WeapPickup = REG_USER_MSG( "WeapPickup", 1 );
|
|
gmsg.ItemPickup = REG_USER_MSG( "ItemPickup", -1 );
|
|
gmsg.RoomType = REG_USER_MSG( "RoomType", 2 );
|
|
gmsg.HideWeapon = REG_USER_MSG( "HideWeapon", 1 );
|
|
gmsg.WeaponAnim = REG_USER_MSG( "WeaponAnim", 3 );
|
|
gmsg.ShowMenu = REG_USER_MSG( "ShowMenu", -1 );
|
|
gmsg.Shake = REG_USER_MSG("ScreenShake", 13 );
|
|
gmsg.Fade = REG_USER_MSG("ScreenFade", sizeof(ScreenFade));
|
|
gmsg.AmmoX = REG_USER_MSG("AmmoX", 2);
|
|
gmsg.TeamNames = REG_USER_MSG( "TeamNames", -1 );
|
|
gmsg.StatusText = REG_USER_MSG("StatusText", -1);
|
|
gmsg.StatusValue = REG_USER_MSG("StatusValue", 3);
|
|
gmsg.SetBody = REG_USER_MSG("SetBody", 1);
|
|
gmsg.SetSkin = REG_USER_MSG("SetSkin", 1);
|
|
gmsg.ZoomHUD = REG_USER_MSG("ZoomHUD", 1);
|
|
gmsg.WarHUD = REG_USER_MSG("WarHUD", 1);
|
|
|
|
// entity messages
|
|
gmsg.StatusIcon = REG_USER_MSG("StatusIcon", -1);
|
|
gmsg.CamData = REG_USER_MSG("CamData", -1);
|
|
gmsg.Fsound = REG_USER_MSG("Fsound", -1);
|
|
gmsg.RainData = REG_USER_MSG("RainData", 16);
|
|
gmsg.AddScreen = REG_USER_MSG( "AddScreen", 1);
|
|
gmsg.AddPortal = REG_USER_MSG( "AddPortal", 1);
|
|
gmsg.HudText = REG_USER_MSG( "HudText", -1 );
|
|
gmsg.ShowGameTitle = REG_USER_MSG("GameTitle", 1);
|
|
gmsg.TempEntity = REG_USER_MSG( "TempEntity", -1);
|
|
gmsg.SetFog = REG_USER_MSG("SetFog", 7 );
|
|
gmsg.SetSky = REG_USER_MSG( "SetSky", 13 );
|
|
gmsg.Particle = REG_USER_MSG( "Particle", -1);
|
|
gmsg.Beams = REG_USER_MSG( "Beams", -1 );
|
|
gmsg.AddMirror = REG_USER_MSG( "AddMirror", 2);
|
|
}
|
|
|
|
/*
|
|
================================
|
|
AllowLagCompensation
|
|
|
|
The game .dll should return 1 if lag compensation should be allowed ( could also just set
|
|
the sv_unlag cvar.
|
|
Most games right now should return 0, until client-side weapon prediction code is written
|
|
and tested for them ( note you can predict weapons, but not do lag compensation, too,
|
|
if you want.
|
|
================================
|
|
*/
|
|
int AllowLagCompensation( void )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
InitWorld
|
|
|
|
Called ever time when worldspawn will precached
|
|
================
|
|
*/
|
|
|
|
void InitWorld( void )
|
|
{
|
|
int i;
|
|
|
|
CVAR_SET_STRING("sv_gravity", "800"); // 67ft/sec
|
|
CVAR_SET_STRING("sv_stepsize", "18");
|
|
CVAR_SET_STRING("room_type", "0");// clear DSP
|
|
|
|
g_pLastSpawn = NULL;
|
|
|
|
// Set up game rules
|
|
if (g_pGameRules) delete g_pGameRules;
|
|
g_pGameRules = InstallGameRules( );
|
|
|
|
if ( WorldGraph.m_fGraphPresent && !WorldGraph.m_fGraphPointersSet )
|
|
{
|
|
if ( !WorldGraph.FSetGraphPointers()) Msg( "Graph pointers were not set!\n");
|
|
else Msg( "**Graph Pointers Set!\n" );
|
|
}
|
|
|
|
UTIL_PrecacheResourse(); //precache all resource
|
|
|
|
pSoundEnt = GetClassPtr( ( CSoundEnt *)NULL );
|
|
pSoundEnt->Spawn();
|
|
if ( !pSoundEnt ) Msg( "Couldn create soundent!\n" );
|
|
|
|
InitBodyQue();
|
|
|
|
SENTENCEG_Init();
|
|
TEXTURETYPE_Init();
|
|
|
|
MSGSended = FALSE;
|
|
if(IsMultiplayer()) MsgDelay = 0.5 + gpGlobals->time;
|
|
else MsgDelay = 0.03 + gpGlobals->time;
|
|
|
|
ClientPrecache();
|
|
|
|
// Setup light animation tables. 'a' is total darkness, 'z' is maxbright.
|
|
for (i = 0; i <= 13; i++) LIGHT_STYLE(i, (char*)STRING(GetStdLightStyle(i)));
|
|
for (i = 0; i < DECAL_COUNT; i++ ) gDecals[i].index = DECAL_INDEX( gDecals[i].name );
|
|
|
|
// init the WorldGraph.
|
|
WorldGraph.InitGraph();
|
|
|
|
if ( !WorldGraph.CheckNODFile ( ( char * )STRING( gpGlobals->mapname ))) WorldGraph.AllocNodes();
|
|
else
|
|
{
|
|
if ( !WorldGraph.FLoadGraph ( (char *)STRING( gpGlobals->mapname )))WorldGraph.AllocNodes();
|
|
else Msg("\n*Graph Loaded!\n" );
|
|
}
|
|
|
|
CVAR_SET_FLOAT( "sv_zmax", MAP_SIZE );
|
|
} |