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.
771 lines
21 KiB
771 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. |
|
* |
|
****/ |
|
|
|
#include "extdll.h" |
|
#include "util.h" |
|
#include "cbase.h" |
|
#include "saverestore.h" |
|
#include "client.h" |
|
#include "decals.h" |
|
#include "gamerules.h" |
|
#include "game.h" |
|
|
|
void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd ); |
|
|
|
extern "C" void PM_Move ( struct playermove_s *ppmove, int server ); |
|
extern "C" void PM_Init ( struct playermove_s *ppmove ); |
|
extern "C" char PM_FindTextureType( const char *name ); |
|
|
|
extern Vector VecBModelOrigin( entvars_t* pevBModel ); |
|
extern DLL_GLOBAL Vector g_vecAttackDir; |
|
extern DLL_GLOBAL int g_iSkillLevel; |
|
|
|
static DLL_FUNCTIONS gFunctionTable = |
|
{ |
|
GameDLLInit, //pfnGameInit |
|
DispatchSpawn, //pfnSpawn |
|
DispatchThink, //pfnThink |
|
DispatchUse, //pfnUse |
|
DispatchTouch, //pfnTouch |
|
DispatchBlocked, //pfnBlocked |
|
DispatchKeyValue, //pfnKeyValue |
|
DispatchSave, //pfnSave |
|
DispatchRestore, //pfnRestore |
|
DispatchObjectCollsionBox, //pfnAbsBox |
|
|
|
SaveWriteFields, //pfnSaveWriteFields |
|
SaveReadFields, //pfnSaveReadFields |
|
|
|
SaveGlobalState, //pfnSaveGlobalState |
|
RestoreGlobalState, //pfnRestoreGlobalState |
|
ResetGlobalState, //pfnResetGlobalState |
|
|
|
ClientConnect, //pfnClientConnect |
|
ClientDisconnect, //pfnClientDisconnect |
|
ClientKill, //pfnClientKill |
|
ClientPutInServer, //pfnClientPutInServer |
|
ClientCommand, //pfnClientCommand |
|
ClientUserInfoChanged, //pfnClientUserInfoChanged |
|
ServerActivate, //pfnServerActivate |
|
ServerDeactivate, //pfnServerDeactivate |
|
|
|
PlayerPreThink, //pfnPlayerPreThink |
|
PlayerPostThink, //pfnPlayerPostThink |
|
|
|
StartFrame, //pfnStartFrame |
|
ParmsNewLevel, //pfnParmsNewLevel |
|
ParmsChangeLevel, //pfnParmsChangeLevel |
|
|
|
GetGameDescription, //pfnGetGameDescription Returns string describing current .dll game. |
|
PlayerCustomization, //pfnPlayerCustomization Notifies .dll of new customization for player. |
|
|
|
SpectatorConnect, //pfnSpectatorConnect Called when spectator joins server |
|
SpectatorDisconnect, //pfnSpectatorDisconnect Called when spectator leaves the server |
|
SpectatorThink, //pfnSpectatorThink Called when spectator sends a command packet (usercmd_t) |
|
|
|
Sys_Error, //pfnSys_Error Called when engine has encountered an error |
|
|
|
PM_Move, //pfnPM_Move |
|
PM_Init, //pfnPM_Init Server version of player movement initialization |
|
PM_FindTextureType, //pfnPM_FindTextureType |
|
|
|
SetupVisibility, //pfnSetupVisibility Set up PVS and PAS for networking for this client |
|
UpdateClientData, //pfnUpdateClientData Set up data sent only to specific client |
|
AddToFullPack, //pfnAddToFullPack |
|
CreateBaseline, //pfnCreateBaseline Tweak entity baseline for network encoding, allows setup of player baselines, too. |
|
RegisterEncoders, //pfnRegisterEncoders Callbacks for network encoding |
|
GetWeaponData, //pfnGetWeaponData |
|
CmdStart, //pfnCmdStart |
|
CmdEnd, //pfnCmdEnd |
|
ConnectionlessPacket, //pfnConnectionlessPacket |
|
GetHullBounds, //pfnGetHullBounds |
|
CreateInstancedBaselines, //pfnCreateInstancedBaselines |
|
InconsistentFile, //pfnInconsistentFile |
|
AllowLagCompensation, //pfnAllowLagCompensation |
|
}; |
|
|
|
static void SetObjectCollisionBox( entvars_t *pev ); |
|
|
|
#if !_WIN32 |
|
extern "C" { |
|
#endif |
|
int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ) |
|
{ |
|
if( !pFunctionTable || interfaceVersion != INTERFACE_VERSION ) |
|
{ |
|
return FALSE; |
|
} |
|
|
|
memcpy( pFunctionTable, &gFunctionTable, sizeof(DLL_FUNCTIONS) ); |
|
return TRUE; |
|
} |
|
|
|
int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ) |
|
{ |
|
if( !pFunctionTable || *interfaceVersion != INTERFACE_VERSION ) |
|
{ |
|
// Tell engine what version we had, so it can figure out who is out of date. |
|
*interfaceVersion = INTERFACE_VERSION; |
|
return FALSE; |
|
} |
|
|
|
memcpy( pFunctionTable, &gFunctionTable, sizeof(DLL_FUNCTIONS) ); |
|
return TRUE; |
|
} |
|
|
|
#if !_WIN32 |
|
} |
|
#endif |
|
|
|
int DispatchSpawn( edict_t *pent ) |
|
{ |
|
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pent ); |
|
|
|
if( pEntity ) |
|
{ |
|
// Initialize these or entities who don't link to the world won't have anything in here |
|
pEntity->pev->absmin = pEntity->pev->origin - Vector( 1.0f, 1.0f, 1.0f ); |
|
pEntity->pev->absmax = pEntity->pev->origin + Vector( 1.0f, 1.0f, 1.0f ); |
|
|
|
pEntity->Spawn(); |
|
|
|
// Try to get the pointer again, in case the spawn function deleted the entity. |
|
// UNDONE: Spawn() should really return a code to ask that the entity be deleted, but |
|
// that would touch too much code for me to do that right now. |
|
pEntity = (CBaseEntity *)GET_PRIVATE( pent ); |
|
|
|
if( pEntity ) |
|
{ |
|
if( g_pGameRules && !g_pGameRules->IsAllowedToSpawn( pEntity ) ) |
|
return -1; // return that this entity should be deleted |
|
if( pEntity->pev->flags & FL_KILLME ) |
|
return -1; |
|
} |
|
|
|
// Handle global stuff here |
|
if( pEntity && pEntity->pev->globalname ) |
|
{ |
|
const globalentity_t *pGlobal = gGlobalState.EntityFromTable( pEntity->pev->globalname ); |
|
if( pGlobal ) |
|
{ |
|
// Already dead? delete |
|
if( pGlobal->state == GLOBAL_DEAD ) |
|
return -1; |
|
else if( !FStrEq( STRING( gpGlobals->mapname ), pGlobal->levelName ) ) |
|
pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive |
|
// In this level & not dead, continue on as normal |
|
} |
|
else |
|
{ |
|
// Spawned entities default to 'On' |
|
gGlobalState.EntityAdd( pEntity->pev->globalname, gpGlobals->mapname, GLOBAL_ON ); |
|
//ALERT( at_console, "Added global entity %s (%s)\n", STRING( pEntity->pev->classname ), STRING( pEntity->pev->globalname ) ); |
|
} |
|
} |
|
|
|
} |
|
|
|
return 0; |
|
} |
|
|
|
void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd ) |
|
{ |
|
if( !pkvd || !pentKeyvalue ) |
|
return; |
|
|
|
EntvarsKeyvalue( VARS( pentKeyvalue ), pkvd ); |
|
|
|
// If the key was an entity variable, or there's no class set yet, don't look for the object, it may |
|
// not exist yet. |
|
if ( pkvd->fHandled || pkvd->szClassName == NULL ) |
|
return; |
|
|
|
// Get the actualy entity object |
|
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pentKeyvalue ); |
|
|
|
if( !pEntity ) |
|
return; |
|
|
|
pEntity->KeyValue( pkvd ); |
|
} |
|
|
|
// HACKHACK -- this is a hack to keep the node graph entity from "touching" things (like triggers) |
|
// while it builds the graph |
|
BOOL gTouchDisabled = FALSE; |
|
|
|
void DispatchTouch( edict_t *pentTouched, edict_t *pentOther ) |
|
{ |
|
if( gTouchDisabled ) |
|
return; |
|
|
|
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pentTouched ); |
|
CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE( pentOther ); |
|
|
|
if( pEntity && pOther && ! ( ( pEntity->pev->flags | pOther->pev->flags ) & FL_KILLME ) ) |
|
pEntity->Touch( pOther ); |
|
} |
|
|
|
void DispatchUse( edict_t *pentUsed, edict_t *pentOther ) |
|
{ |
|
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pentUsed ); |
|
CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE( pentOther ); |
|
|
|
if( pEntity && !( pEntity->pev->flags & FL_KILLME ) ) |
|
pEntity->Use( pOther, pOther, USE_TOGGLE, 0 ); |
|
} |
|
|
|
void DispatchThink( edict_t *pent ) |
|
{ |
|
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pent ); |
|
if( pEntity ) |
|
{ |
|
if( FBitSet( pEntity->pev->flags, FL_DORMANT ) ) |
|
ALERT( at_error, "Dormant entity %s is thinking!!\n", STRING( pEntity->pev->classname ) ); |
|
|
|
pEntity->Think(); |
|
} |
|
} |
|
|
|
void DispatchBlocked( edict_t *pentBlocked, edict_t *pentOther ) |
|
{ |
|
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pentBlocked ); |
|
CBaseEntity *pOther = (CBaseEntity *)GET_PRIVATE( pentOther ); |
|
|
|
if( pEntity ) |
|
pEntity->Blocked( pOther ); |
|
} |
|
|
|
void DispatchSave( edict_t *pent, SAVERESTOREDATA *pSaveData ) |
|
{ |
|
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pent ); |
|
|
|
if( pEntity && pSaveData ) |
|
{ |
|
ENTITYTABLE *pTable = &pSaveData->pTable[pSaveData->currentIndex]; |
|
|
|
gpGlobals->time = pSaveData->time; |
|
|
|
if( pTable->pent != pent ) |
|
ALERT( at_error, "ENTITY TABLE OR INDEX IS WRONG!!!!\n" ); |
|
|
|
if( pEntity->ObjectCaps() & FCAP_DONT_SAVE ) |
|
return; |
|
|
|
// These don't use ltime & nextthink as times really, but we'll fudge around it. |
|
if( pEntity->pev->movetype == MOVETYPE_PUSH ) |
|
{ |
|
float delta = pEntity->pev->nextthink - pEntity->pev->ltime; |
|
pEntity->pev->ltime = gpGlobals->time; |
|
pEntity->pev->nextthink = pEntity->pev->ltime + delta; |
|
} |
|
|
|
pTable->location = pSaveData->size; // Remember entity position for file I/O |
|
pTable->classname = pEntity->pev->classname; // Remember entity class for respawn |
|
|
|
CSave saveHelper( pSaveData ); |
|
pEntity->Save( saveHelper ); |
|
|
|
pTable->size = pSaveData->size - pTable->location; // Size of entity block is data size written to block |
|
} |
|
} |
|
|
|
// Find the matching global entity. Spit out an error if the designer made entities of |
|
// different classes with the same global name |
|
CBaseEntity *FindGlobalEntity( string_t classname, string_t globalname ) |
|
{ |
|
edict_t *pent = FIND_ENTITY_BY_STRING( NULL, "globalname", STRING( globalname ) ); |
|
CBaseEntity *pReturn = CBaseEntity::Instance( pent ); |
|
if( pReturn ) |
|
{ |
|
if( !FClassnameIs( pReturn->pev, STRING( classname ) ) ) |
|
{ |
|
ALERT( at_console, "Global entity found %s, wrong class %s\n", STRING( globalname ), STRING( pReturn->pev->classname ) ); |
|
pReturn = NULL; |
|
} |
|
} |
|
|
|
return pReturn; |
|
} |
|
|
|
int DispatchRestore( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity ) |
|
{ |
|
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pent ); |
|
|
|
if( pEntity && pSaveData ) |
|
{ |
|
entvars_t tmpVars; |
|
Vector oldOffset; |
|
|
|
CRestore restoreHelper( pSaveData ); |
|
|
|
gpGlobals->time = pSaveData->time; |
|
|
|
if( globalEntity ) |
|
{ |
|
CRestore tmpRestore( pSaveData ); |
|
tmpRestore.PrecacheMode( 0 ); |
|
tmpRestore.ReadEntVars( "ENTVARS", &tmpVars ); |
|
|
|
// HACKHACK - reset the save pointers, we're going to restore for real this time |
|
pSaveData->size = pSaveData->pTable[pSaveData->currentIndex].location; |
|
pSaveData->pCurrentData = pSaveData->pBaseData + pSaveData->size; |
|
// ------------------- |
|
|
|
const globalentity_t *pGlobal = gGlobalState.EntityFromTable( tmpVars.globalname ); |
|
|
|
// Don't overlay any instance of the global that isn't the latest |
|
// pSaveData->szCurrentMapName is the level this entity is coming from |
|
// pGlobla->levelName is the last level the global entity was active in. |
|
// If they aren't the same, then this global update is out of date. |
|
if( !FStrEq( pSaveData->szCurrentMapName, pGlobal->levelName ) ) |
|
return 0; |
|
|
|
// Compute the new global offset |
|
oldOffset = pSaveData->vecLandmarkOffset; |
|
CBaseEntity *pNewEntity = FindGlobalEntity( tmpVars.classname, tmpVars.globalname ); |
|
if( pNewEntity ) |
|
{ |
|
//ALERT( at_console, "Overlay %s with %s\n", STRING( pNewEntity->pev->classname ), STRING( tmpVars.classname ) ); |
|
// Tell the restore code we're overlaying a global entity from another level |
|
restoreHelper.SetGlobalMode( 1 ); // Don't overwrite global fields |
|
pSaveData->vecLandmarkOffset = ( pSaveData->vecLandmarkOffset - pNewEntity->pev->mins ) + tmpVars.mins; |
|
pEntity = pNewEntity;// we're going to restore this data OVER the old entity |
|
pent = ENT( pEntity->pev ); |
|
// Update the global table to say that the global definition of this entity should come from this level |
|
gGlobalState.EntityUpdate( pEntity->pev->globalname, gpGlobals->mapname ); |
|
} |
|
else |
|
{ |
|
// This entity will be freed automatically by the engine. If we don't do a restore on a matching entity (below) |
|
// or call EntityUpdate() to move it to this level, we haven't changed global state at all. |
|
return 0; |
|
} |
|
} |
|
|
|
if( pEntity->ObjectCaps() & FCAP_MUST_SPAWN ) |
|
{ |
|
pEntity->Restore( restoreHelper ); |
|
pEntity->Spawn(); |
|
} |
|
else |
|
{ |
|
pEntity->Restore( restoreHelper ); |
|
pEntity->Precache(); |
|
} |
|
|
|
// Again, could be deleted, get the pointer again. |
|
pEntity = (CBaseEntity *)GET_PRIVATE( pent ); |
|
#if 0 |
|
if( pEntity && pEntity->pev->globalname && globalEntity ) |
|
{ |
|
ALERT( at_console, "Global %s is %s\n", STRING( pEntity->pev->globalname ), STRING( pEntity->pev->model ) ); |
|
} |
|
#endif |
|
// Is this an overriding global entity (coming over the transition), or one restoring in a level |
|
if( globalEntity ) |
|
{ |
|
//ALERT( at_console, "After: %f %f %f %s\n", pEntity->pev->origin.x, pEntity->pev->origin.y, pEntity->pev->origin.z, STRING( pEntity->pev->model ) ); |
|
pSaveData->vecLandmarkOffset = oldOffset; |
|
if( pEntity ) |
|
{ |
|
UTIL_SetOrigin( pEntity->pev, pEntity->pev->origin ); |
|
pEntity->OverrideReset(); |
|
} |
|
} |
|
else if( pEntity && pEntity->pev->globalname ) |
|
{ |
|
const globalentity_t *pGlobal = gGlobalState.EntityFromTable( pEntity->pev->globalname ); |
|
if( pGlobal ) |
|
{ |
|
// Already dead? delete |
|
if( pGlobal->state == GLOBAL_DEAD ) |
|
return -1; |
|
else if( !FStrEq( STRING( gpGlobals->mapname ), pGlobal->levelName ) ) |
|
{ |
|
pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive |
|
} |
|
// In this level & not dead, continue on as normal |
|
} |
|
else |
|
{ |
|
ALERT( at_error, "Global Entity %s (%s) not in table!!!\n", STRING( pEntity->pev->globalname ), STRING( pEntity->pev->classname ) ); |
|
// Spawned entities default to 'On' |
|
gGlobalState.EntityAdd( pEntity->pev->globalname, gpGlobals->mapname, GLOBAL_ON ); |
|
} |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
void DispatchObjectCollsionBox( edict_t *pent ) |
|
{ |
|
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE( pent ); |
|
if( pEntity ) |
|
{ |
|
pEntity->SetObjectCollisionBox(); |
|
} |
|
else |
|
SetObjectCollisionBox( &pent->v ); |
|
} |
|
|
|
void SaveWriteFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ) |
|
{ |
|
CSave saveHelper( pSaveData ); |
|
saveHelper.WriteFields( pname, pBaseData, pFields, fieldCount ); |
|
} |
|
|
|
void SaveReadFields( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ) |
|
{ |
|
CRestore restoreHelper( pSaveData ); |
|
restoreHelper.ReadFields( pname, pBaseData, pFields, fieldCount ); |
|
} |
|
|
|
edict_t *EHANDLE::Get( void ) |
|
{ |
|
if( m_pent ) |
|
{ |
|
if( m_pent->serialnumber == m_serialnumber ) |
|
return m_pent; |
|
else |
|
return NULL; |
|
} |
|
return NULL; |
|
} |
|
|
|
edict_t *EHANDLE::Set( edict_t *pent ) |
|
{ |
|
if( pent ) |
|
{ |
|
m_pent = pent; |
|
m_serialnumber = m_pent->serialnumber; |
|
} |
|
else |
|
{ |
|
m_pent = NULL; |
|
m_serialnumber = 0; |
|
} |
|
return pent; |
|
} |
|
|
|
EHANDLE::operator CBaseEntity *() |
|
{ |
|
return (CBaseEntity *)GET_PRIVATE( Get() ); |
|
} |
|
|
|
CBaseEntity *EHANDLE::operator = ( CBaseEntity *pEntity ) |
|
{ |
|
if( pEntity ) |
|
{ |
|
m_pent = ENT( pEntity->pev ); |
|
if( m_pent ) |
|
m_serialnumber = m_pent->serialnumber; |
|
} |
|
else |
|
{ |
|
m_pent = NULL; |
|
m_serialnumber = 0; |
|
} |
|
return pEntity; |
|
} |
|
|
|
EHANDLE::operator int () |
|
{ |
|
return Get() != NULL; |
|
} |
|
|
|
CBaseEntity * EHANDLE::operator -> () |
|
{ |
|
return (CBaseEntity *)GET_PRIVATE( Get() ); |
|
} |
|
|
|
// give health |
|
int CBaseEntity::TakeHealth( float flHealth, int bitsDamageType ) |
|
{ |
|
if( !pev->takedamage ) |
|
return 0; |
|
|
|
// heal |
|
if( pev->health >= pev->max_health ) |
|
return 0; |
|
|
|
pev->health += flHealth; |
|
|
|
if( pev->health > pev->max_health ) |
|
pev->health = pev->max_health; |
|
|
|
return 1; |
|
} |
|
|
|
// inflict damage on this entity. bitsDamageType indicates type of damage inflicted, ie: DMG_CRUSH |
|
|
|
int CBaseEntity::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) |
|
{ |
|
Vector vecTemp; |
|
|
|
if( !pev->takedamage ) |
|
return 0; |
|
|
|
// UNDONE: some entity types may be immune or resistant to some bitsDamageType |
|
|
|
// if Attacker == Inflictor, the attack was a melee or other instant-hit attack. |
|
// (that is, no actual entity projectile was involved in the attack so use the shooter's origin). |
|
if( pevAttacker == pevInflictor ) |
|
{ |
|
vecTemp = pevAttacker->origin - VecBModelOrigin( pev ); |
|
} |
|
else |
|
// an actual missile was involved. |
|
{ |
|
vecTemp = pevInflictor->origin - VecBModelOrigin( pev ); |
|
} |
|
|
|
// this global is still used for glass and other non-monster killables, along with decals. |
|
g_vecAttackDir = vecTemp.Normalize(); |
|
|
|
// save damage based on the target's armor level |
|
// figure momentum add (don't let hurt brushes or other triggers move player) |
|
if( ( !FNullEnt( pevInflictor ) ) && (pev->movetype == MOVETYPE_WALK || pev->movetype == MOVETYPE_STEP ) && ( pevAttacker->solid != SOLID_TRIGGER ) ) |
|
{ |
|
Vector vecDir = pev->origin - ( pevInflictor->absmin + pevInflictor->absmax ) * 0.5f; |
|
vecDir = vecDir.Normalize(); |
|
|
|
float flForce = flDamage * ( ( 32.0f * 32.0f * 72.0f ) / ( pev->size.x * pev->size.y * pev->size.z ) ) * 5.0f; |
|
|
|
if( flForce > 1000.0f ) |
|
flForce = 1000.0f; |
|
pev->velocity = pev->velocity + vecDir * flForce; |
|
} |
|
|
|
// do the damage |
|
pev->health -= flDamage; |
|
if( pev->health <= 0 ) |
|
{ |
|
Killed( pevAttacker, GIB_NORMAL ); |
|
return 0; |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
void CBaseEntity::Killed( entvars_t *pevAttacker, int iGib ) |
|
{ |
|
pev->takedamage = DAMAGE_NO; |
|
pev->deadflag = DEAD_DEAD; |
|
UTIL_Remove( this ); |
|
} |
|
|
|
CBaseEntity *CBaseEntity::GetNextTarget( void ) |
|
{ |
|
if( FStringNull( pev->target ) ) |
|
return NULL; |
|
edict_t *pTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING( pev->target ) ); |
|
if( FNullEnt( pTarget ) ) |
|
return NULL; |
|
|
|
return Instance( pTarget ); |
|
} |
|
|
|
// Global Savedata for Delay |
|
TYPEDESCRIPTION CBaseEntity::m_SaveData[] = |
|
{ |
|
DEFINE_FIELD( CBaseEntity, m_pGoalEnt, FIELD_CLASSPTR ), |
|
|
|
DEFINE_FIELD( CBaseEntity, m_pfnThink, FIELD_FUNCTION ), // UNDONE: Build table of these!!! |
|
DEFINE_FIELD( CBaseEntity, m_pfnTouch, FIELD_FUNCTION ), |
|
DEFINE_FIELD( CBaseEntity, m_pfnUse, FIELD_FUNCTION ), |
|
DEFINE_FIELD( CBaseEntity, m_pfnBlocked, FIELD_FUNCTION ), |
|
}; |
|
|
|
int CBaseEntity::Save( CSave &save ) |
|
{ |
|
if( save.WriteEntVars( "ENTVARS", pev ) ) |
|
return save.WriteFields( "BASE", this, m_SaveData, ARRAYSIZE( m_SaveData ) ); |
|
|
|
return 0; |
|
} |
|
|
|
int CBaseEntity::Restore( CRestore &restore ) |
|
{ |
|
int status; |
|
|
|
status = restore.ReadEntVars( "ENTVARS", pev ); |
|
if( status ) |
|
status = restore.ReadFields( "BASE", this, m_SaveData, ARRAYSIZE( m_SaveData ) ); |
|
|
|
if( pev->modelindex != 0 && !FStringNull( pev->model ) ) |
|
{ |
|
Vector mins, maxs; |
|
mins = pev->mins; // Set model is about to destroy these |
|
maxs = pev->maxs; |
|
|
|
PRECACHE_MODEL( STRING( pev->model ) ); |
|
SET_MODEL( ENT( pev ), STRING( pev->model ) ); |
|
UTIL_SetSize( pev, mins, maxs ); // Reset them |
|
} |
|
|
|
return status; |
|
} |
|
|
|
// Initialize absmin & absmax to the appropriate box |
|
void SetObjectCollisionBox( entvars_t *pev ) |
|
{ |
|
if( ( pev->solid == SOLID_BSP ) && |
|
( pev->angles.x || pev->angles.y || pev->angles.z ) ) |
|
{ |
|
// expand for rotation |
|
float max, v; |
|
int i; |
|
|
|
max = 0; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
v = fabs( ( (float *)pev->mins )[i] ); |
|
if( v > max ) |
|
max = v; |
|
v = fabs( ( (float *)pev->maxs )[i] ); |
|
if( v > max ) |
|
max = v; |
|
} |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
( (float *)pev->absmin )[i] = ( (float *)pev->origin )[i] - max; |
|
( (float *)pev->absmax )[i] = ( (float *)pev->origin )[i] + max; |
|
} |
|
} |
|
else |
|
{ |
|
pev->absmin = pev->origin + pev->mins; |
|
pev->absmax = pev->origin + pev->maxs; |
|
} |
|
|
|
pev->absmin.x -= 1; |
|
pev->absmin.y -= 1; |
|
pev->absmin.z -= 1; |
|
pev->absmax.x += 1; |
|
pev->absmax.y += 1; |
|
pev->absmax.z += 1; |
|
} |
|
|
|
void CBaseEntity::SetObjectCollisionBox( void ) |
|
{ |
|
::SetObjectCollisionBox( pev ); |
|
} |
|
|
|
int CBaseEntity::Intersects( CBaseEntity *pOther ) |
|
{ |
|
if( pOther->pev->absmin.x > pev->absmax.x || |
|
pOther->pev->absmin.y > pev->absmax.y || |
|
pOther->pev->absmin.z > pev->absmax.z || |
|
pOther->pev->absmax.x < pev->absmin.x || |
|
pOther->pev->absmax.y < pev->absmin.y || |
|
pOther->pev->absmax.z < pev->absmin.z ) |
|
return 0; |
|
return 1; |
|
} |
|
|
|
void CBaseEntity::MakeDormant( void ) |
|
{ |
|
SetBits( pev->flags, FL_DORMANT ); |
|
|
|
// Don't touch |
|
pev->solid = SOLID_NOT; |
|
// Don't move |
|
pev->movetype = MOVETYPE_NONE; |
|
// Don't draw |
|
SetBits( pev->effects, EF_NODRAW ); |
|
// Don't think |
|
pev->nextthink = 0.0f; |
|
// Relink |
|
UTIL_SetOrigin( pev, pev->origin ); |
|
} |
|
|
|
int CBaseEntity::IsDormant( void ) |
|
{ |
|
return FBitSet( pev->flags, FL_DORMANT ); |
|
} |
|
|
|
BOOL CBaseEntity::IsInWorld( void ) |
|
{ |
|
// position |
|
if( pev->origin.x >= 4096.0f ) |
|
return FALSE; |
|
if( pev->origin.y >= 4096.0f ) |
|
return FALSE; |
|
if( pev->origin.z >= 4096.0f ) |
|
return FALSE; |
|
if( pev->origin.x <= -4096.0f ) |
|
return FALSE; |
|
if( pev->origin.y <= -4096.0f ) |
|
return FALSE; |
|
if( pev->origin.z <= -4096.0f ) |
|
return FALSE; |
|
// speed |
|
if( pev->velocity.x >= 2000.0f ) |
|
return FALSE; |
|
if( pev->velocity.y >= 2000.0f ) |
|
return FALSE; |
|
if( pev->velocity.z >= 2000.0f ) |
|
return FALSE; |
|
if( pev->velocity.x <= -2000.0f ) |
|
return FALSE; |
|
if( pev->velocity.y <= -2000.0f ) |
|
return FALSE; |
|
if( pev->velocity.z <= -2000.0f ) |
|
return FALSE; |
|
|
|
return TRUE; |
|
} |
|
|
|
int CBaseEntity::ShouldToggle( USE_TYPE useType, BOOL currentState ) |
|
{ |
|
if( useType != USE_TOGGLE && useType != USE_SET ) |
|
{ |
|
if( ( currentState && useType == USE_ON ) || ( !currentState && useType == USE_OFF ) ) |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
int CBaseEntity::DamageDecal( int bitsDamageType ) |
|
{ |
|
if( pev->rendermode == kRenderTransAlpha ) |
|
return -1; |
|
|
|
if( pev->rendermode != kRenderNormal ) |
|
return DECAL_BPROOF1; |
|
|
|
return DECAL_GUNSHOT1 + RANDOM_LONG( 0, 4 ); |
|
} |
|
|
|
// NOTE: szName must be a pointer to constant memory, e.g. "monster_class" because the entity |
|
// will keep a pointer to it after this call. |
|
CBaseEntity *CBaseEntity::Create( const char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner ) |
|
{ |
|
edict_t *pent; |
|
CBaseEntity *pEntity; |
|
|
|
pent = CREATE_NAMED_ENTITY( MAKE_STRING( szName ) ); |
|
if( FNullEnt( pent ) ) |
|
{ |
|
ALERT ( at_console, "NULL Ent in Create!\n" ); |
|
return NULL; |
|
} |
|
pEntity = Instance( pent ); |
|
pEntity->pev->owner = pentOwner; |
|
pEntity->pev->origin = vecOrigin; |
|
pEntity->pev->angles = vecAngles; |
|
DispatchSpawn( pEntity->edict() ); |
|
return pEntity; |
|
}
|
|
|