// Initiate the message
MESSAGE_BEGIN( messageDestination, SVC_TEMPENTITY, origin );
// Send data relevant to the message
WRITE_BYTE( temporaryEntityId );
WRITE_XYZ( data );
// Mark the end of the message
MESSAGE_END();
For example, a smoke sprite effect:
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_SMOKE );
WRITE_COORD( pev->origin.x );
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
WRITE_SHORT( m_SmokeSprite ); // smoke sprite index
WRITE_BYTE( 50 ); // 5x scale
WRITE_BYTE( 10 ); // 10fps
MESSAGE_END();
Let's look at MESSAGE_BEGIN
a bit better:
inline void MESSAGE_BEGIN(int msg_dest, int msg_type, const float* pOrigin = NULL, edict_t* ed = NULL)
{
(*g_engfuncs.pfnMessageBegin)(msg_dest, msg_type, pOrigin, ed);
}
It has a few parameters, some of which are optional:msg_dest
- the destination of the message. The message may be sent to all clients, a few, or an individual, through reliable or unreliable channels. It can be one of the following:
MSG_BROADCAST
(0) - Send to all clients. (unreliable)MSG_ONE
(1) - Send to a selected client. Requires pOrigin
and ed
to be specified, ed
being the target client. (reliable)MSG_ALL
(2) - Send to all clients. (reliable)MSG_INIT
(3) - "Write to the init string". This is not used anywhere in the SDK.MSG_PVS
(4) - Send to clients that are within the PVS of pOrigin
. In other words, if a client can "see" or is in the same room as pOrigin
, they are bound to receive the message. (unreliable)MSG_PAS
(5) - Same as MSG_PVS
, except it uses the potentially audible set. Effectively the same as the PVS, just slightly "wider", more coarse in the area it encompasses. (unreliable)MSG_PVS_R
(6) - Same as MSG_PVS
but reliable.MSG_PAS_R
(7) - Same as MSG_PAS
but reliable.MSG_ONE_UNRELIABLE
(8) - Same as MSG_ONE
but unreliable.MSG_SPEC
(9) - "Send to all spectator proxies."msg_type
- Type of the message. There are generally a few built-in ones:
SVC_TEMPENTITY
(23) - Temporary entity message. To further identify which exact temporary entity is used, you must write a byte.SVC_INTERMISSION
(30) - Begins the intermission. In deathmatch, this puts up the scoreboard and relieves all players of their control, viewing the map through cameras.SVC_CDTRACK
(32) - Plays a music track.SVC_WEAPONANIM
(35) - Tells the client to play a given animation. This is only used when clientside weapon prediction is disabled.SVC_ROOMTYPE
(37) - Changes the DSP preset.SVC_DIRECTOR
(51) - Director message.pOrigin
- The location/position the message is emitted from, used by MSG_PVS
, MSG_PAS
and similar. It's unnecessary to set it if you're using MSG_ALL
and such.ed
- The edict of the target client. In other words, the client whom this message will be sent to.MSG_ALL
everywhere, you would effectively broadcast everything that ever happened, to every client in the map. This is far from optimal. There are generally a few guidelines you can follow here:
MSG_PAS
MSG_PVS
MSG_ONE
or MSG_ONE_UNRELIABLE
if it's not important at allMSG_BROADCAST
MSG_ALL
MESSAGE_BEGIN
should be something like this:
WRITE_BYTE( TE_SOMETHING );
This TE_SOMETHING
would be replaced by one of the appropriate temporary entity IDs, such as TE_SMOKE
. In const.h
, you can find all of them:
#define TE_BEAMPOINTS 0 // beam effect between two points
#define TE_BEAMENTPOINT 1 // beam effect between point and entity
#define TE_GUNSHOT 2 // particle effect plus ricochet sound
...
There are too many of them to list here. Also note that some of these temporary entities are not used at all in the SDK and/or broken. However, there is example code on how to use the ones that do work, as well as a video demonstrating how each looks.
Each temporary entity type has its own parameters, and they are documented in const.h
. Here is an example:
#define TE_SPRITE 17 // additive sprite, plays 1 cycle
// coord, coord, coord (position)
// short (sprite index)
// byte (scale in 0.1's)
// byte (brightness)
So in order to use TE_SPRITE
, you would need to:
env_sprite
/ CSprite
does this).MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
to initiate the message.WRITE_BYTE( TE_SPRITE );
to use TE_SPRITE
.WRITE_COORD( pev->origin.x );
followed by the same for Y and Z.WRITE_SHORT( m_iSprite );
to specify which sprite to use.WRITE_BYTE( 10 );
if you wish the scale to be 1, andWRITE_BYTE( 255 );
for maximum opacityMESSAGE_END();
.
ggrenade.cpp
has a nice example of a complete explosion message in CGrenade::Explode
:
int iContents = UTIL_PointContents( pev->origin );
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound
WRITE_COORD( pev->origin.x ); // Send to PAS because of the sound
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
if ( iContents != CONTENTS_WATER )
{
WRITE_SHORT( g_sModelIndexFireball );
}
else
{
WRITE_SHORT( g_sModelIndexWExplosion );
}
WRITE_BYTE( (pev->dmg - 50) * .60 ); // scale * 10
WRITE_BYTE( 15 ); // framerate
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();
Notice how, depending on whether the grenade is under water or not, a different sprite is used. Also note that this is only the visual aspect of the explosion. Damage to nearby entities is done separately.
rpg.cpp
you may find how to initiate trails, in CRpgRocket::IgniteThink
:
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
WRITE_BYTE( TE_BEAMFOLLOW );
WRITE_SHORT( entindex() ); // entity
WRITE_SHORT( m_iTrail ); // model
WRITE_BYTE( 40 ); // life
WRITE_BYTE( 5 ); // width
WRITE_BYTE( 224 ); // r, g, b
WRITE_BYTE( 224 ); // r, g, b
WRITE_BYTE( 255 ); // r, g, b
WRITE_BYTE( 255 ); // brightness
MESSAGE_END();
You may think this message is sent every once in a while, but actually it is only done when the rocket spawns. TE_BEAMFOLLOW
will simply make it so that trails are emitted until an entity stops moving.
world.cpp
, in CDecal::TriggerDecal
, you will find this:
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 ); // decal index
entityIndex = (short)ENTINDEX( trace.pHit );
WRITE_SHORT( entityIndex ); // entity index on which the decal will be placed
if ( 0 != entityIndex )
WRITE_SHORT( (int)VARS( trace.pHit )->modelindex ); // if not worldspawn, place it on this BSP model
MESSAGE_END();
It's important to note that pev->origin
here should be just a little bit above ground, maybe a couple units. The decal index is obtained like so:
pev->skin = DECAL_INDEX( pkvd->szValue );
The engine will look through decals.wad
and find the right decal index there.
You must log in to post a comment. You can login or register a new account.