MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_EXPLOSION );
WRITE_COORD( pev->origin.x );
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
...
MESSAGE_END();
WRITE_COORD
has a hardcoded maximum of ±8192 units, not affected by the values in delta.lst
at all. In order to work around this, we can write a custom user message and intercept all instances of SVC_TEMPENTITY
to use that custom message.WRITE_COORDS
actually points to. For the purposes of this tutorial, we'll go with the former.MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin );
WRITE_BYTE( TE_EXPLOSION );
WRITE_LONG( pev->origin.x );
WRITE_LONG( pev->origin.y );
WRITE_LONG( pev->origin.z );
...
MESSAGE_END();
hud.h
you may add this:
// Custom temporary entities because some builtin ones
// simply do not support large (+8192) map sizes
class CHudTemporaryEntities final : public CHudBase
{
public:
int Init() override;
int MsgFunc_TempEnt(const char* pszName, int iSize, void* pbuf);
private:
void CreateExplosion(); // each TE will have its own method
};
You may add CHudTemporaryEntities m_TempEnts;
to CHud
, and actually register this in the HUD system.#include "hud.h"
#include "cl_util.h"
#include "parsemsg.h"
#include "CustomTemporaryEntities.h"
DECLARE_MESSAGE( m_TempEnts, TempEnt );
int CHudTemporaryEntities::Init()
{
HOOK_MESSAGE( CustomTE );
m_iFlags = HUD_ACTIVE;
gHUD.AddHudElem( this );
return 1;
}
int CHudTemporaryEntities::MsgFunc_TempEnt( const char* pszName, int iSize, void* pBuf )
{
BEGIN_READ( pBuf, iSize );
uint8_t type = READ_BYTE();
switch ( type )
{
case TemporaryEntities::Explosion: CreateExplosion(); break;
default:
gEngfuncs.Con_DPrintf( "ERROR: TempEnt: tempent type is unknown! (%u)\n", uint32_t(type) );
break;
}
return 1;
}
void CHudTemporaryEntities::CreateExplosion()
{
// Read 3 longs because the server will send 3 longs, duh
Vector position = { READ_LONG(), READ_LONG(), READ_LONG() };
int spriteIndex = READ_SHORT();
float scale = READ_BYTE() / 10.0f;
float framerate = READ_BYTE();
int flags = READ_BYTE();
// It just so happens that the TE explosion flags map directly to the engine API explosion flags, so we can pass them as-is
gEngfuncs.pEfxAPI->R_Explosion( position, spriteIndex, scale, framerate, flags );
}
You may have noticed the CustomTemporaryEntities.h
header. Here are its contents:
#pragma once
struct TemporaryEntities
{
enum Type : uint8_t
{
// long long long (position)
// short (sprite index)
// byte (scale in tens of units)
// byte (framerate)
// byte (flags, refer to TE_EXPLFLAG_* constants)
Explosion = TE_EXPLOSION,
};
};
You may have noticed by now, that this allows us to have custom temporary effects, just like in Source.LinkUserMessages
:
gmsgTempEntities = REG_USER_MSG( "TempEnt", -1 );
SVC_TEMPENTITY
referencesTE_EXPLOSION
:
apache.cpp
- CApache::DyingThink
crossbow.cpp
- CCrossbowBolt::ExplodeThink
explode.cpp
- CEnvExplosion::Use
ggrenade.cpp
- CGrenade::Explode
osprey.cpp
- COsprey::DyingThink
CApache::DyingThink
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_EXPLOSION );
WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -150, 150 ) );
WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -150, 150 ) );
WRITE_COORD( pev->origin.z + RANDOM_FLOAT( -150, -50 ) );
WRITE_SHORT( g_sModelIndexFireball );
WRITE_BYTE( RANDOM_LONG( 0, 29 ) + 30 );
WRITE_BYTE( 12 );
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();
MESSAGE_BEGIN( MSG_PVS, gmsgTempEntity, pev->origin );
WRITE_BYTE( TE_EXPLOSION );
WRITE_LONG( pev->origin.x + RANDOM_FLOAT( -150, 150 ) );
WRITE_LONG( pev->origin.y + RANDOM_FLOAT( -150, 150 ) );
WRITE_LONG( pev->origin.z + RANDOM_FLOAT( -150, -50 ) );
WRITE_SHORT( g_sModelIndexFireball );
WRITE_BYTE( RANDOM_LONG( 0, 29 ) + 30 );
WRITE_BYTE( 12 );
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();
CEnvExplosion::Use
// draw fireball
if ( !(pev->spawnflags & SF_ENVEXPLOSION_NOFIREBALL) )
{
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_EXPLOSION );
WRITE_COORD( pev->origin.x );
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
WRITE_SHORT( g_sModelIndexFireball );
WRITE_BYTE( (BYTE)m_spriteScale ); // scale * 10
WRITE_BYTE( 15 ); // framerate
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();
}
else
{
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_EXPLOSION );
WRITE_COORD( pev->origin.x );
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
WRITE_SHORT( g_sModelIndexFireball );
WRITE_BYTE( 0 ); // no sprite
WRITE_BYTE( 15 ); // framerate
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();
}
// draw fireball
if ( !(pev->spawnflags & SF_ENVEXPLOSION_NOFIREBALL) )
{
MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin );
WRITE_BYTE( TE_EXPLOSION );
WRITE_LONG( pev->origin.x );
WRITE_LONG( pev->origin.y );
WRITE_LONG( pev->origin.z );
WRITE_SHORT( g_sModelIndexFireball );
WRITE_BYTE( (BYTE)m_spriteScale ); // scale * 10
WRITE_BYTE( 15 ); // framerate
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();
}
else
{
MESSAGE_BEGIN( MSG_PAS, gmsgTempEntity, pev->origin );
WRITE_BYTE( TE_EXPLOSION );
WRITE_LONG( pev->origin.x );
WRITE_LONG( pev->origin.y );
WRITE_LONG( pev->origin.z );
WRITE_SHORT( g_sModelIndexFireball );
WRITE_BYTE( 0 ); // no sprite
WRITE_BYTE( 15 ); // framerate
WRITE_BYTE( TE_EXPLFLAG_NONE );
MESSAGE_END();
}
You must log in to post a comment. You can login or register a new account.