Named Game of the Year by over 50 publications, Valve's début title blends action and adventure with award-winning technology to create a frighteningly realistic world where players must think to survive. Also includes an exciting multiplayer mode that allows you to play against friends and enemies around the world.

Post tutorial Report RSS Basing one monster class off another

This is a tutorial from valve-erc.com. The tutorial was written by Caleb 'Ghoul' Delnay. This was not made by me and I did not contribute to it. I am posting it here because valve-erc is only accessible through web.archive.org and is hard to find.

Posted by on - Basic Server Side Coding

Ok, so you want two monsters that are actually a lot alike, for example,
Human Grunts and Male Assassins (from Gearbox's Opposing Force) or a
scientist in a cleansuit (also from Opposing Force). Well, this is
actually really simple even if you have the most basic of C++ knowlege.

Now, programming time, let's make a cleansuit scientist. Open up your scientist.cpp file and find the Spawn function of CDeadScientist. After it we want to declare our new scientist, so:

//
// Clean Suit Scientist
//

class CCleanSuitScientist : public CScientist
{
public:
     void Spawn(void);
     void Precache(void);
     BOOL CanHeal(void);
};

This of course is our class and the functions it has. Notice that it is public CScientist. This gives our CCleanSuitScientist all the same funcions as a normal CScientist. However, we are overriding three of the CScientist functions with our own functions. We need a new Spawn
function because this is where the model for the monster is set, and
our cleansuit scientist has a cleansuit on, not a normal lab coat. A
new Precache
is needed because the cleansuits model file is precached here by the
engine, and we wouldn't want to precache the wrong model, now would we?
Finally, we have an overridden CanHeal
function because the normal Opposing Force model doesn't have a needle
to even heal you with, so having a cleansuit scientist heal you with
nothing wouldn't make sense, right? So, now we need the actual bodies
of these functions, which will go right below, but first don't forget
the link function which adds your monster to an entity name!

LINK_ENTITY_TO_CLASS( monster_cleansuit_scientist , CCleanSuitScientist);

That should do, now for the three functions.

BOOL CCleanSuitScientist::CanHeal(void)
{
     return FALSE;
}

Ok, this is our overridden CanHeal function. It always returns FALSE no matter what, so the scientist will never be able to heal you. Now for the Spawn function.

//=========================================================
// Spawn
//=========================================================
void CCleanSuitScientist::Spawn(void)
{
     Precache( );

     SET_MODEL(ENT(pev), "models/cleansuit_scientist.mdl");
     UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);

     pev->solid               = SOLID_SLIDEBOX;
     pev->movetype          = MOVETYPE_STEP;
     m_bloodColor          = BLOOD_COLOR_RED;
     pev->health               = gSkillData.scientistHealth;
     
     // position of the eyes relative to monster's origin.
     pev->view_ofs          = Vector ( 0, 0, 50 );

     // NOTE: we need a wide field of view so scientists will notice player and say hello
     m_flFieldOfView          = VIEW_FIELD_WIDE;
     m_MonsterState          = MONSTERSTATE_NONE;

//     m_flDistTooFar          = 256.0;

     m_afCapability          = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE;

     // White hands
     pev->skin = 0;

     if ( pev->body == -1 )
     { // -1 chooses a random head
          // pick a head, any head
          pev->body = RANDOM_LONG(0, NUM_SCIENTIST_HEADS-1);
     }

     // Luther is black, make his hands black
     if ( pev->body == HEAD_LUTHER )
          pev->skin = 1;
     
     MonsterInit();
     SetUse( FollowerUse );
}

Notice that it is almost exactly like the normal scientists Spawn function, except we are setting the model to have a different model. Next up is the Precache function!

//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CCleanSuitScientist::Precache(void)
{
     PRECACHE_MODEL("models/cleansuit_scientist.mdl");
     PRECACHE_SOUND("scientist/sci_pain1.wav");
     PRECACHE_SOUND("scientist/sci_pain2.wav");
     PRECACHE_SOUND("scientist/sci_pain3.wav");
     PRECACHE_SOUND("scientist/sci_pain4.wav");
     PRECACHE_SOUND("scientist/sci_pain5.wav");

     // every new scientist must call this, otherwise
     // when a level is loaded, nobody will talk (time is reset to 0)
     TalkInit();

     CTalkMonster::Precache();
}

Again this is almost exactly like the CScientist's Spawn function, but we are precaching our cleansuit model and not the normal scientist model.

Oddly enough, thats it! Now you should be able to add in your monster_cleansuit_scientist entry into the .fgd and place him in a map. All should work fine assuming you have the model in your models directory!

While this technique seems quite a waste compared too the other shorter
method of creating such slightly different monsters, it works. However,
I find this method perfect when making things very similar like
different zombie types, or perhaps new marine types with different
settings and the like. Good luck and good programming!

Post comment Comments
Ian50028
Ian50028

Great tutorial it's awesome thanks!.

Reply Good karma Bad karma+1 vote
Gunship_Mark_II
Gunship_Mark_II

Very good well explained tutorial, great job doing this, this will be very helpful to new modders.

Reply Good karma Bad karma+1 vote
23-down
23-down

Very nice tutorial. I wish Sohl would feature the remaining opposing force entities. The ropes , shock troopers, voltigores etc. That's basically the only thing preventing me from switching over there. Hard to write something if you got no clue how the code should look to get a result that comes close of the original Op4.

Reply Good karma Bad karma+1 vote
H.
H.

Old but gold. Nice tutorial.

Reply Good karma Bad karma+1 vote
Aynekko
Aynekko

I didn't expect for it to work that easily. I'm an absolute noob in programming but following this tutorial I managed to do it and understood everything. Thank you Caleb 'Ghoul' Delnay and OsirisGodoftheDead for sharing!

Reply Good karma Bad karma+1 vote
Wibble606
Wibble606

Absolutely invaluable piece of information.

Reply Good karma Bad karma+1 vote
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: