This is part 2 in a series dedicated to creating your first plugin!
NPC Taxi
The idea of our mod is to let us hop onto random NPC's horses and wagons and travel a bit differently. Whenever you target an NPC, either by holding down rightclick or by aiming at them, and then press G you can ride with them. In this part we are looking at how to actually detect a keypress and then figure out whether we are targetting a valid NPC.
Check for keypresses
Now that we have created the first version of our plugin, let's take a look at adding some actual functionality. All code changes will be made in NPCTaxi.cs as that is the core of our project. First, we need to figure out whether the player wants to hop onto someone's horse or wagon. For this, we can use a key check to determine whether a certain key is down. The Game class in Rage Plugin Hook provides many useful methods for that. Let's look at all that's needed:
// If the G key was pressed.
if (Game.WasKeyJustPressed(Keys.G))
{
// Do something!
}
Pretty easy! The Keys enumeration contains a definition for all of your keyboard keys so feel free to change the key. Whenever the player now presses the key, the if-check is passed and we will execute the code between {}. It is important to note that this keycheck only evaluates to true once while the key is being pressed, i.e. if the key is being held down for a few seconds, the code is still only being executed once. If you need to check for a key being held down every tick, you can use Game.IsKeyDown.
Getting the player's target
So we now know when the player would like to ride with a NPC, but we don't know who that NPC is or whether there even is a valid one. For that we need to get the target the player is currently either focusing (by holding down right mouse) or aiming at. Let's create a small helper function for that to cover both cases:
private static Entity GetPlayerTarget()
{
if (Game.LocalPlayer.IsFreeAimingAtAnyEntity)
{
return Game.LocalPlayer.FreeAimingTarget;
}
return Game.LocalPlayer.Target;
}
We first check whether the player is aiming at any entity. An entity is the base type of peds, wagons, horses, objects and other things you can see in the game. They all share the properties of an entity, such as having a visual representation in the game, a position and much more. Since we do not know what the player is aiming at exactly (could be a wagon, a ped etc.) it is presented to us as the most basic type (the lowest common denominator if you will). This gives us access to all functionality of an entity, e.g. changing its position, but not to ped or vehicle specific stuff as we do not yet know what type it really is. If we are aiming at any entity, we return the current free aiming target. If not, we return the player's current target, which is the current interaction target. We do not worry about whether any of these targets are actually valid and exist in the game in this function yet. We will do this from where we call the function instead. Since we return an entity, which is a base type, we need to verify that it is a ped as we want the rider/driver. (If you want to expand the mod, you could for instance also support targeting/aiming at horses and wagons directly instead of relying on the driver and create a driver if there is none.) We can do this using the following line, also checking whether it actually exists in the game world first:
if (targetEntity.Exists() && targetEntity.IsPed)
Now follows some voodoo as we want to make sure that Visual Studio now treats our Entity as a Ped since we just confirmed it is one. Every Ped is an Entity, but not every Entity is a Ped. To do that, we can use the "as" keyword in C# which essentially tells Visual Studio that it should try to make the entity a ped if it is one. Please note that there is nothing actually happening in the game during this conversion, it really is just telling Visual Studio that we would like to treat this type as another type (because we know it is something else!). If it's successful, we have a new variable with access to all ped functions. This looks like this:
var target = targetEntity as Ped;
This might be a bit scary, but it is quite an important concept to understand. If we had not confirmed that our entity was a ped and it turned out that it was a vehicle, then target would have the value null since the conversion was invalid. These tricks are necessary when dealing with base types such as Entity that could in reality be for instance a Ped or a Vehicle. Or full code looks like this now:
// If the G key is down.
if (Game.WasKeyJustPressed(Keys.G))
{
// Get the current player target.
var targetEntity = GetPlayerTarget();
if (targetEntity.Exists() && targetEntity.IsPed)
{
// We confirmed that it is a ped, so turn it into one.
var target = targetEntity as Ped;
}
}
If you want to test the code, you can add something like target.Kill(); after the conversion. This will kill any ped you target or aim at when pressing G. Press Shift+F6 (Build NPCTaxi) again to build your project and copy the files in the same folder as before. Note: Your game can still be running while doing that, RPH is smart enough to realize that you have updated the files with new code and will automatically reload your plugin with your with your changes. Magic! Now walk around and bring death upon everyone for at least 10 minutes.
Getting the mount/vehicle and native invocation
Since we are unfortunately working on a taxi mod and not a destruction mod, we remove the kill code again and focus on getting the rider's mod or driver's wagon respectively. Let's first consider the mount logic. This is an interesting case as we will be using a game function that is not yet supported by RPH (i.e. nicely wrapped for us), but instead we will have to call the game function directly. We will be using IS_PED_ON_MOUNT and I recommend to check out that link to see for yourself how the documentation for game functions work. These functions are usually referred to as native functions and are called by using their hash or their name (hash will always work, name only if RPH knows the name). This might sound a bit confusing, so let's look at the code instead to clear things up a bit:
// Check if they are on a mount first. This is not yet implemented in RPH so we call the native via its name.
var isOnMount = Game.CallNative<bool>("IS_PED_ON_MOUNT", target);
if (isOnMount)
{
}
There is a lot going on here, but it is important to understand since many functions will not yet be available through RPH. So we can see that we are assigning a variable called isOnMount with the result from CallNative. The return type is a bool, i.e. true or false. The name of the native we are calling is IS_PED_ON_MOUNT as in the link above and we pass it one parameter, our target ped. If the ped is on a horse, this function will hence return true; otherwise it will return false.
Stay tuned for part 3 to actually interact with the mount!