| |
|
|
THE MODS: ADDING A MARINE MOVEMENT TO RELOAD WEAPONS
In every new 3D FPS games, the player's got the choice to reload his weapons when he wants. In AvP, you can't actually do that: instead, the character reloads automaticaly if there's no ammo.
In that way, we'll see in this tutorial how to code a new movement, which will bring you basis to study a little part of weapons, the player key configuration system and to get a tip on the use of the text strings on a video game. To manipulate textstrings in the mod, you'll need the Huffcomp tool from the avptools archive.
1) The different steps to make such a mod
In order to create this movement, you'll need to achieve 2 main steps.
- First, you've got to update the Marine Key Configuration menu and the code to handle the player's requests on the key.
- Second, you can create the movement effect, i.e. the reload of the current weapon.
2) Updating the Marine Key Configuration menu
So let's start with the menu: as everyone knows, you can redefine your configuration keys for each specie and reset these to a default state. Plus, you've got one primary and one secondary configurations per specie that can be used simultaneously.
a) handle of the keys
Here, we'll define the default keys for the movement of the Marine: 'R' (for the primary config) and nothing (for the secondary config).
AvP's got constants for each key, depending on your language: these are defined in the file platform.h, under the KEY_ID enum. So the ones that we'll take is the KEY_R and the KEY_VOID enums.
Now, open the file usr_io.c and find the DefaultMarineInputPrimaryConfig object definition for English. Then, just after KEY_TAB,, add the following: KEY_R, // ReloadWeapon;. Then, do the same thing with the DefaultMarineInputSecondaryConfig object, but with the KEY_VOID enum.
b) display of the keys
Now that we've added the keys, we need to display them in the Marine Key Configuration menu.
All is a matter of text strings: just open the langenum.h file and locate the ADDITIONAL_TEXTSTRING_MARINE_KEY_SHOWSCORES, line (it refers to the previous KEY_TAB key), then add the line TEXTSTRING_MARINE_KEY_RELOAD_WEAPON, (this will be your textstring for the displayed movement). Finally, you must declare the new textstring in the ressource file that is "language.txt": AvP stores every text strings in this file (which is useful for translated products...). For reasons that'll be explained in the third chapter of this tutorial, we'll make a copy of this file and apply the changes to this one. Here's the manipulation:
Important note: make a copy of your existing "language.txt" file before making those manipulations, or you'll probably lose it...
-
go to a console mode under Windows and type: huffcomp language.txt mylanguage.txt
This will uncompress the first file to the second one.
-
open the "mylanguage.txt" file, locate the second "Show Scores" line (the first one of the file is for the Predator) and just after, put your textstring: "Reload". Save and close the file.
-
now, compress your new ressource file with: huffcomp mylanguage.txt language.txt.
-
finally, you can delete the "mylanguage.txt" file.
c) handle of player's requests
Now that the keys are defined, we must associate these with a player input: open the usr_io.h file and see the PLAYER_INPUT_CONFIGURATION structure. Thanks to the comments, we can see that the first part of the structure defines common specie's inputs like Forward, Walk, etc... and that the second part is reserved to each species. It means that if we put an input to the first part, the 3 species will have it: in this tutorial, only the Marine can reload his weapons, so we just need to put the input in the second part as this:
typedef struct
{
unsigned char Forward;
unsigned char Backward;
unsigned char Left;
unsigned char Right;
unsigned char Strafe;
unsigned char StrafeLeft;
unsigned char StrafeRight;
unsigned char LookUp;
unsigned char LookDown;
unsigned char CentreView;
unsigned char Walk;
unsigned char Crouch;
unsigned char Jump;
unsigned char Operate;
unsigned char FirePrimaryWeapon;
unsigned char FireSecondaryWeapon;
union
{
unsigned char NextWeapon; // Predator & Marine
unsigned char AlternateVision; // Alien
};
(...)
union
{
unsigned char Predator_MessageHistory; // Predator
unsigned char Marine_ShowScores; // Marine
};
// unsigned char Predator_Say; // we share this Predator input with our new Marine input
union
{
unsigned char Predator_Say; // Predator
unsigned char ReloadWeapon; // Marine // ==> OUR NEW INPUT
};
unsigned char Predator_SpeciesSay;
unsigned char Predator_ShowScores;
unsigned char ExpansionSpace7;
unsigned char ExpansionSpace8;
} PLAYER_INPUT_CONFIGURATION;
With it, the structure will keep it's original size and the appropriate input will be used whether we play as the Marine or the Predator. And what for the Alien ? How can the engine understand that the 2 variables don't correspond to the Alien ? In fact, there are defined constants above the structure that indicates how many inputs we've got for each specie. In this example, you need to change the lines
#if MARINE_DEMO||DEATHMATCH_DEMO
#define NUMBER_OF_MARINE_INPUTS 27
#else
#define NUMBER_OF_MARINE_INPUTS 27
#endif
to:
#if MARINE_DEMO||DEATHMATCH_DEMO
#define NUMBER_OF_MARINE_INPUTS 27
#else
#define NUMBER_OF_MARINE_INPUTS 28
#endif
Okay, so we've got our new ReloadWeapon input: in AvP, this variable will be accessed from the KeyboardInput array (KeyboardInput[primaryInput->ReloadWeapon] for the primary config and KeyboardInput[secondaryInput->ReloadWeapon] for the secondary one).
The player input should now correspond to a player's request: in the bh_types.h file, locate the PLAYER_INPUT_REQUESTS structure and in the end of the Marine section, put the line unsigned int Rqst_Reload :1;. Now open the usr_io.c file and in the void InitPlayerGameInput(STRATEGYBLOCK* sbPtr) procedure, under the playerStatusPtr->Mvt_InputRequests.Mask2 = 0; line, add this one: playerStatusPtr->Mvt_InputRequests.Flags.Rqst_Reload = 0;.
See the special note at the end of this tutorial if you want more details on this line...
The last thing to do is to associate the player's input with the player's request in the void ReadPlayerGameInput(STRATEGYBLOCK* sbPtr) procedure: in the "character specific abilities", the Marine part, add the following just before the break; statement:
if(KeyboardInput[primaryInput->ReloadWeapon]
||KeyboardInput[secondaryInput->ReloadWeapon])
playerStatusPtr->Mvt_InputRequests.Flags.Rqst_Reload = 1;
At this point, the game can be compiled, but BE CAREFUL NOT TO EXECUTE IT BEFORE READING THE THIRD CHAPTER, or you'll crash your configuration files...
3) Creating the movement effect
Now, we'll code the effect of pressing the default 'R' key: first, we'll create a new player move and we'll adapt the weapons state management system.
a) the new player move
Okay, so all we need to do now is to code the new player move: this is handled in the file pmove.c. Get the void ExecuteFreeMovement(STRATEGYBLOCK* sbPtr) procedure, then just after the switch statement and the
if (playerStatusPtr->Mvt_InputRequests.Flags.Rqst_Operate)
OperateObjectInLineOfSight();
lines, add the following code:
// If player is the Marine and requests a reload
if ((AvP.PlayerType == I_Marine)&&(playerStatusPtr->Mvt_InputRequests.Flags.Rqst_Reload))
{
PLAYER_WEAPON_DATA *weaponPtr;
TEMPLATE_WEAPON_DATA *twPtr;
// Get the player's current weapon
weaponPtr = &(playerStatusPtr->WeaponSlot[playerStatusPtr->SelectedWeaponSlot]);
// If we've got enough ammo and don't reload yet
if((weaponPtr->PrimaryMagazinesRemaining > 0) && (weaponPtr->CurrentState != WEAPONSTATE_RELOAD_PRIMARY))
{
// We change the weapon to a reloading state
weaponPtr->CurrentState = WEAPONSTATE_RELOAD_PRIMARY;
weaponPtr->StateTimeOutCounter = WEAPONSTATE_INITIALTIMEOUTCOUNT;
// We get the template corresponding to the weapon...
twPtr = &TemplateWeapon[weaponPtr->WeaponIDNumber];
// ...and execute the weapon's state handling function
(*twPtr->WeaponStateFunction[weaponPtr->CurrentState])((void *)playerStatusPtr,weaponPtr);
}
}
b) adaptation of the weapons state management system
By default, the weapons reload directly when there's ONLY ammo counters to 0. In our mod, we need to reload EVEN if there're still ammos: that's why we must change the weapons state management system to allow such a thing.
Just open the weapons.c file, locate the void UpdateWeaponStateMachine(void) procedure and change the following section (just add what's in blue):
case WEAPONSTATE_RELOAD_PRIMARY:
{
/* load a new magazine */
TEMPLATE_AMMO_DATA *templateAmmoPtr = &TemplateAmmo[twPtr->PrimaryAmmoID];
// HERE ! if (weaponPtr->PrimaryRoundsRemaining==0) {
if (weaponPtr->WeaponIDNumber==WEAPON_TWO_PISTOLS) {
/* Two pistols reloads BOTH primary and secondary. */
if (weaponPtr->PrimaryMagazinesRemaining) {
weaponPtr->PrimaryRoundsRemaining = templateAmmoPtr->AmmoPerMagazine;
weaponPtr->PrimaryMagazinesRemaining--;
}
if (weaponPtr->SecondaryMagazinesRemaining) {
weaponPtr->SecondaryRoundsRemaining = templateAmmoPtr->AmmoPerMagazine;
weaponPtr->SecondaryMagazinesRemaining--;
}
} else {
/* Grenade launcher has already reloaded at this point. */
weaponPtr->PrimaryRoundsRemaining = templateAmmoPtr->AmmoPerMagazine;
weaponPtr->PrimaryMagazinesRemaining--;
}
// HERE TOO ! }
weaponPtr->CurrentState = WEAPONSTATE_IDLE;
weaponPtr->StateTimeOutCounter=0;
playerStatusPtr->Encumberance=twPtr->Encum_Idle;
break;
}
4) Using the mod
This kind of mod can't be used directly in your AvP directory for one simple reason: we changed the configuration system, so this won't be compatible with the original AvP. That's why the simplest way of using it is to copy your AvP folder to another one, put your executable in it and delete your profile(s).
After that, you can launch the game, selecting any playing mode, test your new move, change your configuration...
5) Conclusion
We've seen that to add such a player move, we had to change the Marine Key Menu, by adding a new item in the movements list that we've called "Reload" and updating the player's inputs and requests system. After that, we added the movement, internally linked to this system by the engine, to finally modify the weapon state handling system to suits our needs.
Note that the present mod allows you to reload when you want, even if your weapon ammo magazine is full, plus only the primary ammo was taken in consideration.
I hope that this more evolved tutorial will bring you ideas and/or teached you something.
Special note:
Before adding the "unsigned int Rqst_Reload :1;" line, you can remark that the sum of the other variables bit lengh is equal to 32 bits, which is the exact size of the "unsigned int" type. This means that by adding our line, we increase the size of the "PLAYER_INPUT_REQUESTS" structure in the game, i.e. the size of the "Mvt_InputRequests" union in the "PLAYER_STATUS" structure.
When the player inputs is reset (i.e. the "void InitPlayerGameInput(STRATEGYBLOCK* sbPtr)" procedure), the lines "playerStatusPtr->Mvt_InputRequests.Mask = 0;" and "playerStatusPtr->Mvt_InputRequests.Mask2 = 0;" reset the request flags to 0 to a limit of 32 bits (size of the type of the 2 masks), so if we don't manually reset our new flag request, the game will endlessly try to reload the current weapon !
So in short: if you want to create new moves with new requests, don't forget to reset each new request flag manually.
|