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.
860 lines
22 KiB
860 lines
22 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 CBaseEntity *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( &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( &SUB_DoNothing ); |
|
SetUse( &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( &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 = 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( &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; |
|
} |