|
|
|
Note: I will assume for this tutorial that you have at least a basic understanding of how to work with the Half-Life sdk code. If you are unsure about the basics, please consult the other excellent tutorials on this site. Throughout this tutorial, exisitng Half-Life code is highlighted in red, new code is highlighted in cyan, and code output is highlighted in yellow.
I decided to write this tutorial after spending several hours totally perplexed as to why the weapon I had just created (the bolter for the Half-Life Inferno Mod), was still being referred to as an mp5 within the game. This tutorial is meant to help anyone who runs into similar problems.
The Ugly Truth Of The Matter:
It turns out that the name of any particular weapon (which is used in phrases such as "Flower Child killed Bob with mp5" in the game) is not actually based on the code itself, but rather, is based on the weapon’s entity name within the map you are playing. Where we run into trouble is when we want to base our weapons on old entities (such as the mp5) so that our new weaponry will work with existing maps. It is kind of a trade off in that you can create entirely new weapon entities (such as "weapon_bolter") and have their names appear correctly, or you can maintain support for old maps, and end up with the old weapon names for your new weapons. This tutorial will attempt to explain a work-around for this problem that will allow you to get the best of both worlds.
The Solution:
It turns out that Valve must have run into similar problems during the development of Half-Life because there is actually an example of how to pull this off within the SDK code. If you open up mp5.cpp and check lines 57 and 58 you will find the following:
LINK_ENTITY_TO_CLASS( weapon_mp5, CMP5 );
LINK_ENTITY_TO_CLASS( weapon_9mmAR, CMP5 );
What this means is that the mp5 is actually linked to two different entities within the map files. However, if you notice, during play, the weapon is always referred to by a constant name, so apparently they must have done something to force the name of the weapon to remain the same no matter what entity it is based on. After digging around a bit, I also turned up the following within the Spawn method of the mp5 class (on line 70 of mp5.cpp)
pev->classname = MAKE_STRING("weapon_9mmAR"); // hack to allow for old names
Now, the way I understand it, whenever an entity is spawned within the world, it automatically has its pev->classname set to whatever the entity that generated it was named. If we dig a little further into the naming code, it turns out that the weapon name that is displayed on the player’s screen is actually derived from the above classname string , by stripping off the "weapon_" part of the string (I will get into a little more detail on this in "Advanced Topics" below, for now, just trust me on this point). So basically, what the above code is doing, is switching the classname to "weapon_9mmAR" regardless of whether the original entity was actually a "weapon_mp5" or "weapon_9mmAR".
For example, within the Inferno mod, I wanted to be able to switch any mp5’s on a map into bolters. After writing the code for the weapon, I linked it to three entities like this:
LINK_ENTITY_TO_CLASS( weapon_bolter, CBolter );
LINK_ENTITY_TO_CLASS( weapon_mp5, CBolter );
LINK_ENTITY_TO_CLASS( weapon_9mmAR, CBolter );
NOTE: Make sure to disable the corresponding statements within mp5.cpp or else you will end up with the same entities linked to two different classes, which would have unpredictable results.
Now, as the first line within my Spawn method for the bolter class, I added in the following line:
pev->classname = MAKE_STRING( "weapon_bolter" );
Which forces any particular instance of the weapon to be referred to as a bolter no matter which of the three entities it started as.
This is basically all you need to know to get your weapon names to display properly, but for those of you that want a little more information on how this works, I present you with:
Advanced Topics:
I mentioned above that the entity name actually gets stripped down from "weapon_bolter" (or whatever), to just "bolter" before being displayed to the screen. After digging around a bit, I found out where this is done. If you consult multiplayer_gamerules.cpp and look around line 540 you will find a method called DeathNotice(). This method is called every time a player is killed within a multiplayer game and determines what message should be displayed on every player’s screen to mark the event. The first part of the method basically determines what has killed the player, but if we assume that it was another player with a weapon, we will find at line 584 some code that reads:
if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 )
killer_weapon_name += 7;
Since killer_weapon_name is a pointer that has already been set to the classname of the entity that killed the player, what is happening here is that the code is effectively stripping out the "weapon_" part of the name before displaying it, by advancing the killer_weapon_name to a point beyond "weapon_" (exactly 7 bytes). In the case of my "weapon_bolter", after this code is executed, killer_weapon_name effectively points to a string that reads "bolter", which is exactly what we would like.
If we continue to follow the logic of the code, we will wind up at line 609 which reads:
UTIL_LogPrintf( "\"%s<%i>\" killed \"%s<%i>\" with %s\n", STRING( pKiller->netname ),
GETPLAYERUSERID( ENT(pKiller) ),
STRING( pVictim->pev->netname ),
GETPLAYERUSERID( pVictim->edict() ),
killer_weapon_name );
This is the portion of the code that actually displays our message to the screen. It has the same parameters as a standard printf(), so I don’t think I need to get into too much detail other than to point out the last %s in the sentence is filled in with killer_weapon_name. Which from our example above is bolter. So basically what we end up with is a message on everyone’s screen that reads:
’Killer’ killed ‘Victim’ with bolter
Sweet! :)
I will mention one last thing on this topic. What if we want the name displayed to be totally different from the entity name? For example, instead of displaying "bolter", which is basically the entity name, what if I wanted to say "marine bolter" instead. Well, it turns out that Valve wanted to do exactly the same thing with the gluon gun and the tau cannon, so we already have an example to work from. If you consult line 553 of multiplayer_gamerules.c (this is in the same method we’ve been working with), you will find the following:
char *tau = "tau_cannon";
char *gluon = "gluon gun";
In our case, we will want to add a line right after this that reads:
char *bolter = "marine bolter";
Down on line 598 we then find the following:
if ( !strcmp( killer_weapon_name, "egon" ) )
killer_weapon_name = gluon;
else if ( !strcmp( killer_weapon_name, "gauss" ) )
killer_weapon_name = tau;
This is basically overriding the default class names with the ones specified above for display purposes, so right below we can add the following:
else if ( !strcmp( killer_weapon_name, "bolter" ) )
killer_weapon_name = bolter;
and Presto! The weapon will now be referred to as a "marine bolter" whenever someone kills someone with it.
Well, that’s about it. I hope that the above was clear enough, and that it saves someone the hours of digging that I had to go through to figure this out.
Any questions or suggestions should be sent to me:
FlowerChildM7@yahoo.com