Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

839 lines
21 KiB

/***
*
* 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.
*
****/
/*
===== world.cpp ========================================================
precaches and defs for entities and other data that must always be available.
*/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "nodes.h"
#include "soundent.h"
#include "client.h"
#include "decals.h"
#include "skill.h"
#include "effects.h"
#include "player.h"
#include "weapons.h"
#include "gamerules.h"
#include "teamplay_gamerules.h"
#include "physcallback.h"
extern CGraph WorldGraph;
extern CSoundEnt *pSoundEnt;
extern EHANDLE g_pLastSpawn;
DLL_GLOBAL edict_t *g_pBodyQueueHead;
CGlobalState gGlobalState;
extern DLL_GLOBAL int gDisplayTitle;
extern void W_Precache( void );
//
// This must match the list in util.h
//
DLL_DECALLIST gDecals[] = {
{ "{shot1", 0 }, // DECAL_GUNSHOT1
{ "{shot2", 0 }, // DECAL_GUNSHOT2
{ "{shot3", 0 }, // DECAL_GUNSHOT3
{ "{shot4", 0 }, // DECAL_GUNSHOT4
{ "{shot5", 0 }, // DECAL_GUNSHOT5
{ "{lambda01", 0 }, // DECAL_LAMBDA1
{ "{lambda02", 0 }, // DECAL_LAMBDA2
{ "{lambda03", 0 }, // DECAL_LAMBDA3
{ "{lambda04", 0 }, // DECAL_LAMBDA4
{ "{lambda05", 0 }, // DECAL_LAMBDA5
{ "{lambda06", 0 }, // DECAL_LAMBDA6
{ "{scorch1", 0 }, // DECAL_SCORCH1
{ "{scorch2", 0 }, // DECAL_SCORCH2
{ "{blood1", 0 }, // DECAL_BLOOD1
{ "{blood2", 0 }, // DECAL_BLOOD2
{ "{blood3", 0 }, // DECAL_BLOOD3
{ "{blood4", 0 }, // DECAL_BLOOD4
{ "{blood5", 0 }, // DECAL_BLOOD5
{ "{blood6", 0 }, // DECAL_BLOOD6
{ "{yblood1", 0 }, // DECAL_YBLOOD1
{ "{yblood2", 0 }, // DECAL_YBLOOD2
{ "{yblood3", 0 }, // DECAL_YBLOOD3
{ "{yblood4", 0 }, // DECAL_YBLOOD4
{ "{yblood5", 0 }, // DECAL_YBLOOD5
{ "{yblood6", 0 }, // DECAL_YBLOOD6
{ "{break1", 0 }, // DECAL_GLASSBREAK1
{ "{break2", 0 }, // DECAL_GLASSBREAK2
{ "{break3", 0 }, // DECAL_GLASSBREAK3
{ "{bigshot1", 0 }, // DECAL_BIGSHOT1
{ "{bigshot2", 0 }, // DECAL_BIGSHOT2
{ "{bigshot3", 0 }, // DECAL_BIGSHOT3
{ "{bigshot4", 0 }, // DECAL_BIGSHOT4
{ "{bigshot5", 0 }, // DECAL_BIGSHOT5
{ "{spit1", 0 }, // DECAL_SPIT1
{ "{spit2", 0 }, // DECAL_SPIT2
{ "{bproof1", 0 }, // DECAL_BPROOF1
{ "{gargstomp", 0 }, // DECAL_GARGSTOMP1, // Gargantua stomp crack
{ "{smscorch1", 0 }, // DECAL_SMALLSCORCH1, // Small scorch mark
{ "{smscorch2", 0 }, // DECAL_SMALLSCORCH2, // Small scorch mark
{ "{smscorch3", 0 }, // DECAL_SMALLSCORCH3, // Small scorch mark
{ "{mommablob", 0 }, // DECAL_MOMMABIRTH // BM Birth spray
{ "{mommablob", 0 }, // DECAL_MOMMASPLAT // BM Mortar spray?? need decal
};
/*
==============================================================================
BODY QUE
==============================================================================
*/
#define SF_DECAL_NOTINDEATHMATCH 2048
class CDecal : public CBaseEntity
{
public:
void Spawn( void );
void KeyValue( KeyValueData *pkvd );
void EXPORT StaticDecal( void );
void EXPORT TriggerDecal( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
};
LINK_ENTITY_TO_CLASS( infodecal, CDecal )
// UNDONE: These won't get sent to joining players in multi-player
void CDecal::Spawn( void )
{
if( pev->skin < 0 || ( gpGlobals->deathmatch && FBitSet( pev->spawnflags, SF_DECAL_NOTINDEATHMATCH ) ) )
{
REMOVE_ENTITY( ENT( pev ) );
return;
}
if( FStringNull( pev->targetname ) )
{
SetThink( &CDecal::StaticDecal );
// if there's no targetname, the decal will spray itself on as soon as the world is done spawning.
pev->nextthink = gpGlobals->time;
}
else
{
// if there IS a targetname, the decal sprays itself on when it is triggered.
SetThink( &CBaseEntity::SUB_DoNothing );
SetUse( &CDecal::TriggerDecal );
}
}
void CDecal::TriggerDecal( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
// this is set up as a USE function for infodecals that have targetnames, so that the
// decal doesn't get applied until it is fired. (usually by a scripted sequence)
TraceResult trace;
int entityIndex;
UTIL_TraceLine( pev->origin - Vector( 5, 5, 5 ), pev->origin + Vector( 5, 5, 5 ), ignore_monsters, ENT( pev ), &trace );
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_BSPDECAL );
WRITE_COORD( pev->origin.x );
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
WRITE_SHORT( (int)pev->skin );
entityIndex = (short)ENTINDEX( trace.pHit );
WRITE_SHORT( entityIndex );
if( entityIndex )
WRITE_SHORT( (int)VARS( trace.pHit )->modelindex );
MESSAGE_END();
SetThink( &CBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time + 0.1;
}
void CDecal::StaticDecal( void )
{
TraceResult trace;
int entityIndex, modelIndex;
UTIL_TraceLine( pev->origin - Vector( 5, 5, 5 ), pev->origin + Vector( 5, 5, 5 ), ignore_monsters, ENT( pev ), &trace );
entityIndex = (short)ENTINDEX( trace.pHit );
if( entityIndex )
modelIndex = (int)VARS( trace.pHit )->modelindex;
else
modelIndex = 0;
g_engfuncs.pfnStaticDecal( pev->origin, (int)pev->skin, entityIndex, modelIndex );
SUB_Remove();
}
void CDecal::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "texture" ) )
{
pev->skin = DECAL_INDEX( pkvd->szValue );
// Found
if( pev->skin >= 0 )
return;
ALERT( at_console, "Can't find decal %s\n", pkvd->szValue );
}
else
CBaseEntity::KeyValue( pkvd );
}
// Body queue class here.... It's really just CBaseEntity
class CCorpse : public CBaseEntity
{
virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; }
};
LINK_ENTITY_TO_CLASS( bodyque, CCorpse )
static 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_SetOrigin( pevHead, pev->origin );
UTIL_SetSize( pevHead, pev->mins, pev->maxs );
g_pBodyQueueHead = pevHead->owner;
}
CGlobalState::CGlobalState( void )
{
Reset();
}
void CGlobalState::Reset( void )
{
m_pList = NULL;
m_listCount = 0;
}
globalentity_t *CGlobalState::Find( string_t globalname )
{
if( !globalname )
return NULL;
globalentity_t *pTest;
const char *pEntityName = STRING( globalname );
pTest = m_pList;
while( pTest )
{
if( FStrEq( pEntityName, pTest->name ) )
break;
pTest = pTest->pNext;
}
return pTest;
}
// This is available all the time now on impulse 104, remove later
//#ifdef _DEBUG
void CGlobalState::DumpGlobals( void )
{
static char *estates[] = { "Off", "On", "Dead" };
globalentity_t *pTest;
ALERT( at_console, "-- Globals --\n" );
pTest = m_pList;
while( pTest )
{
ALERT( at_console, "%s: %s (%s)\n", pTest->name, pTest->levelName, estates[pTest->state] );
pTest = pTest->pNext;
}
}
//#endif
void CGlobalState::EntityAdd( string_t globalname, string_t mapName, GLOBALESTATE state )
{
ASSERT( !Find( globalname ) );
globalentity_t *pNewEntity = (globalentity_t *)calloc( sizeof( globalentity_t ), 1 );
ASSERT( pNewEntity != NULL );
pNewEntity->pNext = m_pList;
m_pList = pNewEntity;
strcpy( pNewEntity->name, STRING( globalname ) );
strcpy( pNewEntity->levelName, STRING( mapName ) );
pNewEntity->state = state;
m_listCount++;
}
void CGlobalState::EntitySetState( string_t globalname, GLOBALESTATE state )
{
globalentity_t *pEnt = Find( globalname );
if( pEnt )
pEnt->state = state;
}
const globalentity_t *CGlobalState :: EntityFromTable( string_t globalname )
{
globalentity_t *pEnt = Find( globalname );
return pEnt;
}
GLOBALESTATE CGlobalState::EntityGetState( string_t globalname )
{
globalentity_t *pEnt = Find( globalname );
if( pEnt )
return pEnt->state;
return GLOBAL_OFF;
}
// Global Savedata for Delay
TYPEDESCRIPTION CGlobalState::m_SaveData[] =
{
DEFINE_FIELD( CGlobalState, m_listCount, FIELD_INTEGER ),
};
// Global Savedata for Delay
TYPEDESCRIPTION gGlobalEntitySaveData[] =
{
DEFINE_ARRAY( globalentity_t, name, FIELD_CHARACTER, 64 ),
DEFINE_ARRAY( globalentity_t, levelName, FIELD_CHARACTER, 32 ),
DEFINE_FIELD( globalentity_t, state, FIELD_INTEGER ),
};
int CGlobalState::Save( CSave &save )
{
int i;
globalentity_t *pEntity;
if( !save.WriteFields( "GLOBAL", this, m_SaveData, ARRAYSIZE( m_SaveData ) ) )
return 0;
pEntity = m_pList;
for( i = 0; i < m_listCount && pEntity; i++ )
{
if( !save.WriteFields( "GENT", pEntity, gGlobalEntitySaveData, ARRAYSIZE( gGlobalEntitySaveData ) ) )
return 0;
pEntity = pEntity->pNext;
}
return 1;
}
int CGlobalState::Restore( CRestore &restore )
{
int i, listCount;
globalentity_t tmpEntity;
ClearStates();
if( !restore.ReadFields( "GLOBAL", this, m_SaveData, ARRAYSIZE( m_SaveData ) ) )
return 0;
listCount = m_listCount; // Get new list count
m_listCount = 0; // Clear loaded data
for( i = 0; i < listCount; i++ )
{
if( !restore.ReadFields( "GENT", &tmpEntity, gGlobalEntitySaveData, ARRAYSIZE( gGlobalEntitySaveData ) ) )
return 0;
EntityAdd( MAKE_STRING( tmpEntity.name ), MAKE_STRING( tmpEntity.levelName ), tmpEntity.state );
}
return 1;
}
void CGlobalState::EntityUpdate( string_t globalname, string_t mapname )
{
globalentity_t *pEnt = Find( globalname );
if( pEnt )
strcpy( pEnt->levelName, STRING( mapname ) );
}
void CGlobalState::ClearStates( void )
{
globalentity_t *pFree = m_pList;
while( pFree )
{
globalentity_t *pNext = pFree->pNext;
free( pFree );
pFree = pNext;
}
Reset();
}
void SaveGlobalState( SAVERESTOREDATA *pSaveData )
{
CSave saveHelper( pSaveData );
gGlobalState.Save( saveHelper );
}
void RestoreGlobalState( SAVERESTOREDATA *pSaveData )
{
CRestore restoreHelper( pSaveData );
gGlobalState.Restore( restoreHelper );
}
void ResetGlobalState( void )
{
gGlobalState.ClearStates();
gInitHUD = TRUE; // Init the HUD on a new game / load game
}
// moved CWorld class definition to cbase.h
//=======================
// CWorld
//
// This spawns first when each level begins.
//=======================
LINK_ENTITY_TO_CLASS( worldspawn, CWorld )
#define SF_WORLD_DARK 0x0001 // Fade from black at startup
#define SF_WORLD_TITLE 0x0002 // Display game title at startup
#define SF_WORLD_FORCETEAM 0x0004 // Force teams
extern DLL_GLOBAL BOOL g_fGameOver;
float g_flWeaponCheat;
void CWorld::Spawn( void )
{
g_fGameOver = FALSE;
Precache();
}
void CWorld::Precache( void )
{
g_pLastSpawn = NULL;
#if 1
CVAR_SET_STRING( "sv_gravity", "800" ); // 67ft/sec
CVAR_SET_STRING( "sv_stepsize", "18" );
#else
CVAR_SET_STRING( "sv_gravity", "384" ); // 32ft/sec
CVAR_SET_STRING( "sv_stepsize", "24" );
#endif
CVAR_SET_STRING( "room_type", "0" );// clear DSP
// Set up game rules
if( g_pGameRules )
{
delete g_pGameRules;
g_pGameRules = NULL;
}
g_pGameRules = InstallGameRules();
//!!!UNDONE why is there so much Spawn code in the Precache function? I'll just keep it here
///!!!LATER - do we want a sound ent in deathmatch? (sjb)
//pSoundEnt = CBaseEntity::Create( "soundent", g_vecZero, g_vecZero, edict() );
pSoundEnt = GetClassPtr( ( CSoundEnt *)NULL );
pSoundEnt->Spawn();
if( !pSoundEnt )
{
ALERT ( at_console, "**COULD NOT CREATE SOUNDENT**\n" );
}
InitBodyQue();
// init sentence group playback stuff from sentences.txt.
// ok to call this multiple times, calls after first are ignored.
SENTENCEG_Init();
// init texture type array from materials.txt
TEXTURETYPE_Init();
// the area based ambient sounds MUST be the first precache_sounds
// player precaches
W_Precache(); // get weapon precaches
ClientPrecache();
// sounds used from C physics code
PRECACHE_SOUND( "common/null.wav" );// clears sound channels
PRECACHE_SOUND( "items/suitchargeok1.wav" );//!!! temporary sound for respawning weapons.
PRECACHE_SOUND( "items/gunpickup2.wav" );// player picks up a gun.
PRECACHE_SOUND( "common/bodydrop3.wav" );// dead bodies hitting the ground (animation events)
PRECACHE_SOUND( "common/bodydrop4.wav" );
g_Language = (int)CVAR_GET_FLOAT( "sv_language" );
if( g_Language == LANGUAGE_GERMAN )
{
PRECACHE_MODEL( "models/germangibs.mdl" );
}
else
{
PRECACHE_MODEL( "models/hgibs.mdl" );
PRECACHE_MODEL( "models/agibs.mdl" );
}
PRECACHE_SOUND( "weapons/ric1.wav" );
PRECACHE_SOUND( "weapons/ric2.wav" );
PRECACHE_SOUND( "weapons/ric3.wav" );
PRECACHE_SOUND( "weapons/ric4.wav" );
PRECACHE_SOUND( "weapons/ric5.wav" );
//
// Setup light animation tables. 'a' is total darkness, 'z' is maxbright.
//
// 0 normal
LIGHT_STYLE( 0, "m" );
// 1 FLICKER (first variety)
LIGHT_STYLE( 1, "mmnmmommommnonmmonqnmmo" );
// 2 SLOW STRONG PULSE
LIGHT_STYLE( 2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba" );
// 3 CANDLE (first variety)
LIGHT_STYLE( 3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg" );
// 4 FAST STROBE
LIGHT_STYLE( 4, "mamamamamama" );
// 5 GENTLE PULSE 1
LIGHT_STYLE( 5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj" );
// 6 FLICKER (second variety)
LIGHT_STYLE( 6, "nmonqnmomnmomomno" );
// 7 CANDLE (second variety)
LIGHT_STYLE( 7, "mmmaaaabcdefgmmmmaaaammmaamm" );
// 8 CANDLE (third variety)
LIGHT_STYLE( 8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa" );
// 9 SLOW STROBE (fourth variety)
LIGHT_STYLE( 9, "aaaaaaaazzzzzzzz" );
// 10 FLUORESCENT FLICKER
LIGHT_STYLE( 10, "mmamammmmammamamaaamammma" );
// 11 SLOW PULSE NOT FADE TO BLACK
LIGHT_STYLE( 11, "abcdefghijklmnopqrrqponmlkjihgfedcba" );
// 12 UNDERWATER LIGHT MUTATION
// this light only distorts the lightmap - no contribution
// is made to the brightness of affected surfaces
LIGHT_STYLE( 12, "mmnnmmnnnmmnn" );
// styles 32-62 are assigned by the light program for switchable lights
// 63 testing
LIGHT_STYLE( 63, "a" );
for( int i = 0; i < ARRAYSIZE( gDecals ); i++ )
gDecals[i].index = DECAL_INDEX( gDecals[i].name );
// init the WorldGraph.
WorldGraph.InitGraph();
// make sure the .NOD file is newer than the .BSP file.
if( !WorldGraph.CheckNODFile( ( char * )STRING( gpGlobals->mapname ) ) )
{
// NOD file is not present, or is older than the BSP file.
WorldGraph.AllocNodes();
}
else
{
// Load the node graph for this level
if( !WorldGraph.FLoadGraph ( (char *)STRING( gpGlobals->mapname ) ) )
{
// couldn't load, so alloc and prepare to build a graph.
ALERT( at_console, "*Error opening .NOD file\n" );
WorldGraph.AllocNodes();
}
else
{
ALERT( at_console, "\n*Graph Loaded!\n" );
}
}
if( pev->speed > 0 )
CVAR_SET_FLOAT( "sv_zmax", pev->speed );
else
CVAR_SET_FLOAT( "sv_zmax", 4096 );
// g-cont. moved here to right restore global WaveHeight on save\restore level
CVAR_SET_FLOAT( "sv_wateramp", pev->scale );
if( pev->netname )
{
ALERT( at_aiconsole, "Chapter title: %s\n", STRING( pev->netname ) );
CBaseEntity *pEntity = CBaseEntity::Create( "env_message", g_vecZero, g_vecZero, NULL );
if( pEntity )
{
pEntity->SetThink( &CBaseEntity::SUB_CallUseToggle );
pEntity->pev->message = pev->netname;
pev->netname = 0;
pEntity->pev->nextthink = gpGlobals->time + 0.3;
pEntity->pev->spawnflags = SF_MESSAGE_ONCE;
}
}
if( pev->spawnflags & SF_WORLD_DARK )
CVAR_SET_FLOAT( "v_dark", 1.0 );
else
CVAR_SET_FLOAT( "v_dark", 0.0 );
pev->spawnflags &= ~SF_WORLD_DARK; // g-cont. don't apply fade after save\restore
if( pev->spawnflags & SF_WORLD_TITLE )
gDisplayTitle = TRUE; // display the game title if this key is set
else
gDisplayTitle = FALSE;
pev->spawnflags &= ~SF_WORLD_TITLE; // g-cont. don't show logo after save\restore
if( pev->spawnflags & SF_WORLD_FORCETEAM )
{
CVAR_SET_FLOAT( "mp_defaultteam", 1 );
}
else
{
CVAR_SET_FLOAT( "mp_defaultteam", 0 );
}
// g-cont. moved here so cheats will working on restore level
g_flWeaponCheat = CVAR_GET_FLOAT( "sv_cheats" ); // Is the impulse 101 command allowed?
}
//
// Just to ignore the "wad" field.
//
void CWorld::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "skyname" ) )
{
// Sent over net now.
CVAR_SET_STRING( "sv_skyname", pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "sounds" ) )
{
gpGlobals->cdAudioTrack = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq(pkvd->szKeyName, "WaveHeight" ) )
{
// Sent over net now.
pev->scale = atof( pkvd->szValue ) * ( 1.0 / 8.0 );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "MaxRange" ) )
{
pev->speed = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "chaptertitle" ) )
{
pev->netname = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "startdark" ) )
{
// UNDONE: This is a gross hack!!! The CVAR is NOT sent over the client/sever link
// but it will work for single player
int flag = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
if( flag )
pev->spawnflags |= SF_WORLD_DARK;
}
else if( FStrEq(pkvd->szKeyName, "newunit") )
{
// Single player only. Clear save directory if set
if( atoi( pkvd->szValue ) )
CVAR_SET_FLOAT( "sv_newunit", 1 );
pkvd->fHandled = TRUE;
}
else if( FStrEq(pkvd->szKeyName, "gametitle" ) )
{
if( atoi( pkvd->szValue ) )
pev->spawnflags |= SF_WORLD_TITLE;
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "mapteams" ) )
{
pev->team = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "defaultteam" ) )
{
if( atoi( pkvd->szValue ) )
{
pev->spawnflags |= SF_WORLD_FORCETEAM;
}
pkvd->fHandled = TRUE;
}
else
CBaseEntity::KeyValue( pkvd );
}
//
// Xash3D physics interface
//
typedef void (*LINK_ENTITY_FN)( entvars_t *pev );
//
// attempt to create custom entity when default method is failed
// 0 - attempt to create, -1 - reject to create
//
int DispatchCreateEntity( edict_t *pent, const char *szName )
{
/*
#ifdef CREATE_ENTITY_TEST
// quake armor entities. we just replaced it with item_battery...
if( !strcmp( szName, "item_armor1" ) || !strcmp( szName, "item_armor2" ) )
{
LINK_ENTITY_FN SpawnEdict;
// ugly method to get acess with himself exports
SpawnEdict = (LINK_ENTITY_FN)GetProcAddress( GetModuleHandle( "hl" ), "item_battery" );
if( SpawnEdict != NULL ) // found the valid spawn
{
// BUGBUG: old classname hanging in memory
pent->v.classname = ALLOC_STRING( "item_battery" );
//ALERT( at_console, "DispatchCreateEntity: replace %s with %s\n", szName, STRING( pent->v.classname ) );
SpawnEdict( &pent->v );
return 0; // handled
}
}
#endif
*/
return -1;
}
//
// run custom physics for each entity
// return 0 to use built-in engine physic
//
int DispatchPhysicsEntity( edict_t *pEdict )
{
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pEdict );
if( !pEntity )
{
//ALERT( at_console, "skip %s [%i] without private data\n", STRING( pEdict->v.classname ), ENTINDEX( pEdict ) );
return 0; // not initialized
}
// NOTE: at this point pEntity assume to be valid
/*
#ifdef CUSTOM_PHYSICS_TEST
// test alien controller without physics, thinking only
if( FClassnameIs( pEntity->pev, "monster_alien_controller" ) )
{
float thinktime;
thinktime = pEntity->pev->nextthink;
if( thinktime <= 0.0f || thinktime > PHYSICS_TIME() + gpGlobals->frametime )
return 1;
if( thinktime < PHYSICS_TIME() )
thinktime = PHYSICS_TIME(); // don't let things stay in the past.
// it is possible to start that way
// by a trigger with a local time.
pEntity->pev->nextthink = 0.0f;
gpGlobals->time = thinktime;
DispatchThink( pEdict );
#ifdef GRAVITY_TEST
// stupid fake gravity test
pEntity->pev->origin.z -= 1;
LINK_ENTITY( pEdict, true );
#endif
return 1; // handled
}
#endif
*/
return 0;
}
static physics_interface_t gPhysicsInterface =
{
SV_PHYSICS_INTERFACE_VERSION,
DispatchCreateEntity,
DispatchPhysicsEntity,
};
int Server_GetPhysicsInterface( int iVersion, server_physics_api_t *pfuncsFromEngine, physics_interface_t *pFunctionTable )
{
if( !pFunctionTable || !pfuncsFromEngine || iVersion != SV_PHYSICS_INTERFACE_VERSION )
{
return FALSE;
}
// copy new physics interface
memcpy( &g_physfuncs, pfuncsFromEngine, sizeof(server_physics_api_t) );
// fill engine callbacks
memcpy( pFunctionTable, &gPhysicsInterface, sizeof(physics_interface_t) );
return TRUE;
}