SDK2 Basics:

Creating a new Entity class, and adding it to a level

Intro

This is a follow-on to the previous SDK2 Basics tutorial, where we looked at the CGameText class. What we'll do now, is to make a copy of that class to a new entity CGameMenuText, and make a few simple modifications. We'll then create the .fgd definition that Worldcraft uses, and create an example level.

Creating the new class

Append the following code to the end of maprules.cpp (I've highlighted the bits of the class that  are different to the CGameText class). Basically, this is just a CGameText entity, where we've stripped out some of the possible keyvalues, and set default values in the Spawn() method instead:


// New classes / code
//================

// Declare my class as a subclass of CRulePointEntity - this will be at the same level as
// CGameText. Unfortunately, there's not much point in inheriting from CGameText directly, as
// the m_textparms member variable is private to that class.

class CGameMenuText : public CRulePointEntity // must be public - or else LINK_ENTITY_TO_CLASS dont work
{
public:
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void KeyValue( KeyValueData *pkvd );
void Spawn( void );

virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];

inline BOOL MessageToAll( void ) { return (pev->spawnflags & SF_ENVTEXT_ALLPLAYERS); }
inline void MessageSet( const char *pMessage ) { pev->message = ALLOC_STRING(pMessage); }
inline const char *MessageGet( void ) { return STRING(pev->message); }

private:
hudtextparms_t m_textParms;
};

// Link the game_menutext entity to this class, so that I can
// include the entity in maps
LINK_ENTITY_TO_CLASS( game_menutext, CGameMenuText );

// Save parms as a block. Will break save/restore if the structure changes, but this entity didn't ship with Half-Life, so
// it can't impact saved Half-Life games.
TYPEDESCRIPTION CGameMenuText::m_SaveData[] =
{
DEFINE_ARRAY( CGameMenuText, m_textParms, FIELD_CHARACTER, sizeof(hudtextparms_t) ),
};

IMPLEMENT_SAVERESTORE( CGameMenuText, CRulePointEntity );

// New default Spawn() method. This is called for all entities when the map is started
// and contains all the initialisation code for that entity.


void CGameMenuText::Spawn()
{
// Default a whole lotta stuff that you had to specify for the CGameText entity
m_textParms.channel = 1;
m_textParms.x = 0.1;
m_textParms.y = 0.3;
m_textParms.effect = 0;
m_textParms.fadeinTime = 0.5;
m_textParms.fadeoutTime = 0.5;
m_textParms.holdTime = 100.0;
m_textParms.fxTime = 0.2;

// Call parent's spawn method
CRulePointEntity::Spawn();
}

void CGameMenuText::KeyValue( KeyValueData *pkvd )
{
// Cut down version of CGameText::KeyValue.
// we set all the other keyvalues in the spawn() method above, so
// no need to check them on the entity
if (FStrEq(pkvd->szKeyName, "color"))
{
int color[4];
UTIL_StringToIntArray( color, 4, pkvd->szValue );
m_textParms.r1 = color[0];
m_textParms.g1 = color[1];
m_textParms.b1 = color[2];
m_textParms.a1 = color[3];
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "color2"))
{
int color[4];
UTIL_StringToIntArray( color, 4, pkvd->szValue );
m_textParms.r2 = color[0];
m_textParms.g2 = color[1];
m_textParms.b2 = color[2];
m_textParms.a2 = color[3];
pkvd->fHandled = TRUE;
}
else
CRulePointEntity::KeyValue( pkvd );
}

void CGameMenuText::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
// Copy of the Use method from CGameText.
if ( !CanFireForActivator( pActivator ) )
return;

// Add this alert - this is a handy debugging function that pprints out on the screen
// if you have the -console switch on. You should see it appearing in orange text
// at the top of the screen.

ALERT ( at_console, "Calling new GameMenuText entity\n" );

if ( MessageToAll() )
{
UTIL_HudMessageAll( m_textParms, MessageGet() );
}
else
{
if ( pActivator->IsNetClient() )
{
UTIL_HudMessage( pActivator, m_textParms, MessageGet() );
}
}
}


If you compare this to the original CGameText, you'll see that actually, very little has changed

Build the hl.dll, using the release configuration on Visual C++, and copy the hl.dll into valve\dlls (making a backup of the original Half-Life DLL!).

Getting the entity into Worldcraft

Well, the next step is to add the ability to use your new entity into Worldcraft. The entity list is defined in the file half-life.fgd (you might already have a few other ones for Counterstrike, TFC etc.). This is a text file, so all you need to do is make a backup, then add the following to the end of the file:


// New entity declarations

@PointClass base(Targetname, Target) size(-8 -8 -8, 8 8 8) = game_menutext : "HUD Text Menu"
[
    spawnflags(flags) =
    [
        1: "All Players" : 0
    ]

message(string) : "Message Text"
color(color255) : "Color1" : "100 100 100"
color2(color255) : "Color2" : "240 110 0"
master(string) : "Master"
]


This is basically just a cut-down version of the game_text declaration. See "FGD Guide" in the Worldcraft hlp file for more details.

Creating an example map

Start-up Worldcraft, and you should now be able to add a new entity "game_menutext" to your map!

Here's an example map: gmtxttst.rmf

There's a game_menutext entity with a message, that is triggered from the usual trigger_multiple entity. Run the map, and walk forward - you should see some text appearing, and the "Calling new gamemenutext entity" ALERT message appearing at the top of the screen

What's next ?

Coming up in the next tutorial - how to expand you new CGameMenuText class into a full menu system.

steve