Think()
callback for it with a sufficient delay set for nextthink
. Now I'm wondering if this is the elegant way of delaying actions in a plugin or is there a more straightforward way.// clip brushes don't stay in the drawing hull
if (contents == CONTENTS_CLIP)
{
b->hulls[0].faces = NULL;
b->contents = CONTENTS_SOLID;
}
This makes clip brushes invisible but also removes them from the point hull, used for things like hitscan ray traces which is why you can shoot through them.CGameRules
and the code around it.InstallGameRules
(assuming they'd be implemented as gamerules) instead of mangling with different DLLs. The latter could be pretty messy.ClientPutInServer
which runs when the player enters the game, but unfortunately it is too early to send messages from here.
void ClientPutInServer( edict_t *pEntity ) {
const char* playername = STRING(pEntity->v.netname);
char* modelname = g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pEntity ), "model" );
char modelfile[256];
snprintf(modelfile, sizeof(modelfile), "models/player/%s/%s.mdl", modelname, modelname);
LOG_MESSAGE(PLID, "Player %s is using model %s", playername, modelname);
if (fileExists( modelfile )) {
LOG_MESSAGE(PLID, "Model %s is present on server", modelname);
SAY(pEntity, "Your model is present on the server, it will be precached for the next round!");
if (addPrecacheEntry( playername, modelname ))
LOG_MESSAGE(PLID, "Added model %s to precache list", modelname);
else
LOG_MESSAGE(PLID, "Model %s will not be precached - reduntant or precache list is full", modelname);
}
else {
LOG_MESSAGE(PLID, "Unable to precache due to FILE MISSING: %s!", modelfile);
SAY(pEntity, "Your model is not present on the server, can't distribute for other players!");
}
RETURN_META(MRES_IGNORED);
}
If I send a message to other players who are already in game, or all players, they receive the message, so it's not that SAY()
doesn't work. I think somehow I should add some timing, so the message would be sent several seconds later when they are already playing. (Just an irrelevant sidenote: the log message "Model %s will not be precached - reduntant or precache list is full
" is confusing, because if the model is already added to the list, it WILL be precached for the next round, but won't if the list is full.)SAY()
is a macro I defined to send raw messages – I remember there are other methods I tried, but all of them effectively do this at their core.
#define SAY(pEntity, text) {\
MESSAGE_BEGIN( MSG_ONE, GET_USER_MSG_ID(PLID, "SayText", NULL), NULL, pEntity );\
WRITE_BYTE( ENTINDEX(pEntity) );\
WRITE_STRING( text );\
MESSAGE_END();\
}
And fileExists()
here is just a wrapper for calling access()
(this is the other thing I have problem with):
bool fileExists(const char* path) {
char fullpath[256];
snprintf(fullpath, sizeof(fullpath), "%s/%s", &gGamedir[0], path);
return access(fullpath, R_OK) == 0;
}
I highly doubt that LoadFileForMe()
would mmap()
the file – it would be somewhat better because it's faster and the kernel could at least unload it if it's not used, but it would still be better to not load files in the middle of a game when they are not needed in memory.IFileSystem
in public/FileSystem.h
is interesting, I think it would do what I need but I have no idea what it links to.+door1
and train -> newpos
instead of having to manually create a trigger_relay or trigger_changetarget. Advanced users can even make their own template entities and behaviors, and share them with others via .zip files.{_tb_group}
to entity names and targetnames inside linked groups to produce unique names per group.
Download links:
Unknown command from unsafe location. Ignoring.
CBarney
class and overriding the methods those are different for your CCustom
class.barney.cpp
from the source code, then renamed everything to the name of my custom monster. I took care to make sure that everything was using the right case (i.e. CBarney
vs. BARNEY_AE_DRAW
vs. monster_barney
), that the file paths were correct, and that the code still made sense. Even though it compiles fine and I can't see what I did wrong, I get the error message Can't init monster_custom
in the console. This is the code I used (you can see at this point I've just replaced "barney" with the name of the monster): models/custom.mdl
and sound/custom/...
are real files and directories and have the expected contents.LoadFileForMe
, but I don't want to load the model files at that point, just check for their existence. I also don't know the lifetime of the buffer returned by this function, is the engine smart enough to not load this file again when it is precached the next round, or would I waste memory, how to ask the engine to free it if I need to.ALLOC_STRING()
would redundantly allocate the same strings for the same player models over time and I don't know when these strings get freed. So I felt like this is the only way to control the lifetimes of these strings, but I'm curious for better ideas.game_text
entity to allow for custom font sizes. I implemented the custom parameter fontSize
, but the next step is getting the HUD message system to display my text. I was able to find this cryptic-looking code which writes a message to be displayed on the HUD, and implemented my custom parameter:
void UTIL_HudMessage( CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage )
{
if ( !pEntity || !pEntity->IsNetClient() )
return;
MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pEntity->edict() );
WRITE_BYTE( TE_TEXTMESSAGE );
WRITE_BYTE( textparms.channel & 0xFF );
WRITE_SHORT( FixedSigned16( textparms.x, 1<<13 ) );
WRITE_SHORT( FixedSigned16( textparms.y, 1<<13 ) );
WRITE_BYTE( textparms.effect );
WRITE_BYTE( textparms.r1 );
WRITE_BYTE( textparms.g1 );
WRITE_BYTE( textparms.b1 );
WRITE_BYTE( textparms.a1 );
WRITE_BYTE( textparms.r2 );
WRITE_BYTE( textparms.g2 );
WRITE_BYTE( textparms.b2 );
WRITE_BYTE( textparms.a2 );
WRITE_SHORT( FixedUnsigned16( textparms.fadeinTime, 1<<8 ) );
WRITE_SHORT( FixedUnsigned16( textparms.fadeoutTime, 1<<8 ) );
WRITE_SHORT( FixedUnsigned16( textparms.holdTime, 1<<8 ) );
WRITE_SHORT( textparms.fontSize ); // my code
if ( textparms.effect == 2 )
WRITE_SHORT( FixedUnsigned16( textparms.fxTime, 1<<8 ) );
if ( strlen( pMessage ) < 512 )
{
WRITE_STRING( pMessage );
}
else
{
char tmp[512];
strncpy( tmp, pMessage, 511 );
tmp[511] = 0;
WRITE_STRING( tmp );
}
MESSAGE_END();
}
I was able to trace the MESSAGE_BEGIN()
macro to "enginecallback.h", where the macro is defined to call a function in "eiface.h", which is defined as this:void (*pfnMessageBegin) (int msg_dest, int msg_type, const float *pOrigin, edict_t *ed);
void pfnMessageBegin(int, int, float, edict_t)
that the pfnMessageBegin
pointer in "eiface.h" was referencing. How do I change this code to use my custom data?