entvars_t
vs. edict_t
vs. entity classesCBaseEntity
and a CFuncWall
. Instead, the engine only knows one form of entity. And that is edict_t
.
struct edict_s
{
qboolean free;
int serialnumber;
link_t area; // linked to a division node or leaf
int headnode; // -1 to use normal leaf check
int num_leafs; // How many leaves the entity occupies, shouldn't be more than MAX_ENT_LEAFS
short leafnums[MAX_ENT_LEAFS];
float freetime; // sv.time when the object was freed
void* pvPrivateData; // Alloced and freed by engine, used by DLLs; pointer to a HL SDK entity
entvars_t v; // Common entity variables
};
It's basically an entity dictionary, containing data such as whether the entity's memory is free, its "serial number", its link to a BSP leaf, a pointer to the game entity (an instance of an HL SDK entity class), and an entvars_t variable to hold common entity variables.edict_t
often. CBaseEntity
, there's a reference to this entvars_t
.
entvars_t *pev;
"pev" stands for "Pointer to Entity Variables", and it is essentially a group of variables that is common to all entities. entvars_t
is defined in progdefs.h
, and constants that can be used with variables from entvars_t are in const.h
.entvars_t
are:
classname
- entity's classnametargetname
- entity's nametarget
- target entity's nameorigin
- entity's current positionvelocity
- entity's current velocitymovedir
- entity's current movement directionangles
- entity's current anglesavelocity
- angular velocitymodel
- path to the modelnextthink
- next time the entity will call its think callbackmovetype
- movement type of the entity, which can be:MOVETYPE_NONE
- entity won't moveMOVETYPE_WALK
- entity will use walk physics (players only)MOVETYPE_STEP
- entity will use walk physics for monstersMOVETYPE_FLY
- entity not be affected by gravityMOVETYPE_TOSS
- affected by gravityMOVETYPE_PUSH
- entity can push other entitiesMOVETYPE_NOCLIP
- not affected by gravity, passes through everythingMOVETYPE_FLYMISSILE
- same as MOVETYPE_FLY
but with extra size so it can hit monstersMOVETYPE_BOUNCE
- affected by gravity, bounces from the surface depending on pev->friction
MOVETYPE_BOUNCEMISSILE
- combination of FLYMISSILE and BOUNCEMOVETYPE_FOLLOW
- follows pev->aimentMOVETYPE_PUSHSTEP
- a combination of PUSH and STEP for brush entities, only works with SOLID_BSP
solid
- solidity mode of the entity, which can be:SOLID_NOT
- non-solid, everything passes through itSOLID_TRIGGER
- calls Touch, otherwise non-solidSOLID_BBOX
- uses its axis-aligned bounding box for collisions (this collision box cannot rotate, keep in mind), calls Touch when an entity touches one of its edges, can call BlockedSOLID_SLIDEBOX
- can only call Touch when on the ground, calls it when an entity touches one of its edgesSOLID_BSP
- uses BSP cliphulls of the brush model for collisions, Touch on edge and can call Blockedrendermode
- render mode of this entity, can be:kRenderNormal
- NormalkRenderTransColor
- ColorkRenderTransTexture
- TexturekRenderGlow
- GlowkRenderTransAlpha
- SolidkRenderTransAdd
- Additiverenderamt
- render amount of this entity (typically wrongly named in FGDs as "FX Amount")rendercolor
- render colourrenderfx
- render effect (look at const.h
, it's got many of them)spawnflags
- flags set in the map editor (in the Flags tab)flags
- temporary entity flags. The constants are defined in const.h, and here are some of the more important ones:FL_SKIPLOCALHOST
- Tells the engine not to transmit the entity to the local hostFL_ONGROUND
- Indicates whether the entity is on the ground or not; can be overriddenFL_FAKECLIENT
- Indicates that this is a bot; set this while writing your own botsFL_FLOAT
- Tells the engine to apply buoyancy to the entity when in waterFL_ALWAYSTHINK
- Calls Think every frameFL_MONSTERCLIP
- Only collide with and block entities that have this flag setFL_ONTRAIN
- Ignore the player's movement commands; useful to set this when controlling a vehicleFL_WORLDBRUSH
- Tells the engine that this is an entirely static brush entity, almost as if it were worldspawnFL_KILLME
- Marks the entity to be deleted from memory; this is set by UTIL_Remove
FL_DORMANT
- Doesn't send updates to the client, thus saving on bandwidth; the entity cannot call Think if this is setedict_t
as a parameter, so you'll need to convert your CBaseEntity
or CFuncWall
or any entity class you're using into edict_t
. While there's no direct conversion, you can use the ENT
utility function to 'convert' an entvars_t
into an edict_t
. For example:
SET_MODEL( ENT( pev ), STRING( pev->model ) );
We'll discuss utility functions later on.
string_t
string_t
type. This is not a C-string.string_t
, since it's essentially an unsigned int
, so I'll briefly explain it here.H e l l o w o r l d \0 f u n c _ w a l l \0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
If we wanted to allocate a new string there via ALLOC_STRING
, like so:string_t something = ALLOC_STRING( "something" );
string_t
variable with a value of 11, STRING
would return "func_wall" in the example above. In reality, you'd get garbage at low string_t values, and usable strings usually show up around 250 million.
CBaseEntity
. We won't cover all of its methods and members, but we will go through the most important ones.
Spawn
Precache
PRECACHE_MODEL
- precaches studio modelsPRECACHE_SOUND
- precaches audio filesPRECACHE_GENERIC
- precaches any file, you can even precache a whole .bsp if you wanted to
KeyValue
*pkvd*
- the data of one keyvalueif (FStrEq(pkvd->szKeyName, "myKeyvalue"))
{
m_myVariable = atoi(pkvd->szValue);
pkvd->fHandled = TRUE; // This line is NOT needed if using Solokiller's Half-Life: Updated SDK!
}
else
CBaseEntity::KeyValue(pkvd);
We compare szKeyName
to all possible keyvalues this entity may have, and then convert the szValue
string into a value of the type we need.Save
& Restore
Save
and Restore
are the key methods for transferring custom data fields across save files. If an entity has its own members, but doesn't save/restore, then it is most likely to break while loading a save file, or across level transitions.Save
and Restore
methods as well as declaring the save-restore table (TYPEDESCRIPTION
) as m_SaveData
like this:
virtual bool Save( CSave &save );
virtual bool Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
Outside of the entity class, you define the save-restore table like this:
TYPEDESCRIPTION CPendulum::m_SaveData[] =
{
DEFINE_FIELD( CPendulum, m_accel, FIELD_FLOAT ),
DEFINE_FIELD( CPendulum, m_distance, FIELD_FLOAT ),
DEFINE_FIELD( CPendulum, m_time, FIELD_TIME ),
DEFINE_FIELD( CPendulum, m_damp, FIELD_FLOAT ),
DEFINE_FIELD( CPendulum, m_maxSpeed, FIELD_FLOAT ),
DEFINE_FIELD( CPendulum, m_dampSpeed, FIELD_FLOAT ),
DEFINE_FIELD( CPendulum, m_center, FIELD_VECTOR ),
DEFINE_FIELD( CPendulum, m_start, FIELD_VECTOR ),
};
IMPLEMENT_SAVERESTORE( CPendulum, CBaseEntity );
Note: Valve generally placed this near either the class declaration or the /LINK_ENTITY_TO_CLASS/
line, but it can be placed anywhere.DEFINE_FIELD
requires 3 parameters: the class being saved/restored, the attribute being saved/restored and its type. The following table contains all available types of variables you can save and restore. The types with an asterisk (*
) are the ones you will see and likely use very often in the HL SDK :
Type | Description |
---|---|
FIELD_FLOAT* | A floating point value (0.2 , 75.5 ) that is not related to time (use FIELD_TIME for that purpose) |
FIELD_STRING* | A string ID (string_t - often the result of ALLOC_STRING ) |
FIELD_ENTITY | An entity offset (EOFFSET ) |
FIELD_CLASSPTR* | An entity class pointer (like CBaseEntity * ) |
FIELD_EHANDLE* | An entity handle (EHANDLE ) |
FIELD_EVARS | An entity variables pointer (EVARS * ) |
FIELD_EDICT | An entity dictionary pointer (edict_t * ) |
FIELD_VECTOR* | A vector (Vector , vec3_t , array of 3 float ) |
FIELD_POSITION_VECTOR | A world coordinate (fixed up across level transitions "automagically") |
FIELD_POINTER | Arbitrary data pointer (scheduled to be removed by Valve but seems that isn't the case) |
FIELD_INTEGER* | An integer (5 , 3 ) or enum value. |
FIELD_FUNCTION | A class function pointer (like Think , Touch , Use ...) |
FIELD_BOOLEAN* | A boolean value. |
FIELD_SHORT | A 2 byte integer value. |
FIELD_CHARACTER | A single byte value. |
FIELD_TIME* | Same as FIELD_FLOAT but for variables related to time (like weapons reload time), usually a floating point value that has a relation to the game time (gpGlobals->time ) |
FIELD_MODELNAME | An engine string that is a model's name (requires precaching). |
FIELD_SOUNDNAME | Same as previous one but for sounds. |
IMPLEMENT_SAVERESTORE
requires 2 parameters: the class being saved and its parent. It tells the save/restore code to take care of your entity variables as well as the parent's ones. This is handy because it avoids having to redefine every variable from the parent(s).
ObjectCaps
FCAP_CUSTOMSAVE
- unknown and unusedFCAP_ACROSS_TRANSITION
- the entity will transfer across level transitionsFCAP_MUST_SPAWN
- calls Spawn right after Restore, i.e. after loading a savefileFCAP_DONT_SAVE
- don't write into the savefileFCAP_IMPULSE_USE
- can be used by the playerFCAP_CONTINUOUS_USE
- can be held by the player (such as levers and valves)FCAP_ONOFF_USE
- can be toggled by the playerFCAP_DIRECTIONAL_USE
- receives +/- from the player, only used by func_tracktrainFCAP_MASTER
- entity can be used as a master (multisource has this cap)FCAP_FORCE_TRANSITION
- entity always goes across transitionsActivate
CBaseEntity
has a lot of overrideable methods, but some of these are modular - they can be changed dynamically at runtime.virtual void Think( void )
{
if ( m_pfnThink )
(this->*m_pfnThink)();
}
These methods actually call callbacks.
void (CBaseEntity ::*m_pfnThink)(void);
void (CBaseEntity ::*m_pfnTouch)( CBaseEntity *pOther );
void (CBaseEntity ::*m_pfnUse)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void (CBaseEntity ::*m_pfnBlocked)( CBaseEntity *pOther );
Keep in mind, however, that you don't really have to use these, and for simple entities, you can just override these methods without consequences.
Think
SetThink
macro.pev->nextthink
variable.pev->nextthink = gpGlobals->time + 0.5; // think every 0.5 seconds
Touch
SetTouch
macro.pOther
- entity that touched this entityUse
SetUse
macro.pActivator
- entity that started the trigger sequencepCaller
- entity that actually fired this entityuseType
- type of usage: USE_TOGGLE, USE_ON, USE_OFF and USE_SETvalue
- the value sent from pCaller, typically used only in func_tracktrain*Blocked*
pOther
- entity that blocked this entityCreate
szName
- the classname itself, e.g. env_sprite, monster_zombie etc.vecOrigin
- the position at which it'll spawnvecAngles
- the angles it'll have after spawningpentOwner
- owner of the newly created entity (NULL by default)*Instance*
edict_t*
, entvars_t*
or entity indices into CBaseEntity*
.pent
- entity dictionary/edict to convert from (edict_t*
overload)pev
- entity variables to convert from (entvars_t*
overload)eoffset
- entity index to retrieve from (int
overload)DispatchSpawn
DispatchKeyValue
DispatchTouch
DispatchUse
DispatchThink
DispatchBlocked
DispatchSave
DispatchRestore
DispatchObjectCollsionBox
The game exports these to the engine when hl.dll
is loaded, after which the engine calls them for each edict_t
in its internal array of entities.
CWorld
- worldspawn entityCItem
- base item classCBaseDelay
- generic delay entity, can be spawned when an entity tries to trigger another entity with a delay
CBaseAnimating
- entity that supports studio model animations, provides utilities for submodels, setting controllers etc.CBasePlayerItem
- generic player itemCBasePlayerWeapon
- generic player weaponCBaseToggle
- generic toggleable entity, implements linear movement for trains, opening and closing mechanism for doors etc.CBaseMonster
- base monster classCBasePlayer
- player classYou must log in to post a comment. You can login or register a new account.