[Français]

NEWS
INTRODUCTION
TERMINOLOGY
THE SOURCE CODE
THE BUGS
TUTORIALS
DOWNLOADS
THE AUTHOR

  THE MODS: ADDING A PREDATOR MOVEMENT TO CYCLE VISION BACK
Mod idea by Titan

One cool aspect of the Predator is that multi-vision system: one switch and humans can't hide! Another one and it's Aliens party ! Just in case you want to see your friends, you even got that switch too... One bad aspect of the Predator's that he spends his time switching his vision system to find the accurate one: one supplementary switch & you're ready to restart one cycle ! In summary: the Hunter becomes the Hunted !
So, the purpose of this tutorial is to add a new Predator key to switch back your vision modes. In it, we'll go deeper in changing the player key configuration system (as we already manipulated it in the "Adding a Marine movement to reload weapons" tutorial) and this will be an introduction to the Predator vision system. Like in the previous cited tutorial you'll need the Huffcomp tool from the avptools archive to manipulate textstrings, the AvP Source Code and more important: no knowledge of it !

1) Two main steps to make that mod

No surprise for this one: we already encountered a similar tutorial for the Marine dude. Quick summary for newcomers:
  • First, we'll deal with the Predator Key Configuration menu and the code to handle the player's requests on the key.
  • Second, we'll be ready to create the new cycle vision mode, i.e. the so-called "cycle vision back".
2) Updating the Predator Key Configuration menu

Here we go: every AvP fan knows that you can redefine your configuration keys for each specie and reset these to a default state. What is important to notice is that we've got 2 configuration types for each specie: the "primary" one and the "secondary" one. The goal of this system is to let you customize you keyboard/mouse with 2 configurations. What we'll do now is to add a key under the "Cycle Vision Modes" menu item.

a) handle of the keys

The default key we'll choose for that mod is 'P' for the primary config and nothing for the secondary one. No matter if it's the right choice, you can change it really easily: just read carefully what follows...

Keys in AvP are represented by constants - also called "enums" in C/C++ languages - and this is what we need to use in the code: after what we can see in the platform.h file, the KEY_ID enum is the right place to take what we want. In this case, we need the KEY_P enum for the 'P' key and the KEY_VOID one for a blank key.

Now, we'll put those enums somewhere in the code to let the game "understand" that we will increase the Predator Key Configurations: the trick is to add the 2 enums in the usr_io.c file: first, find the DefaultPredatorInputPrimaryConfig object definition in the "English" section. Thanks to the comments (they're very important dudes !), we can see that the KEY_SLASH, enum is the key that handle the common Cycle Vision Mode. So just after that, add the following: KEY_P, // CycleVisionModeBack;. The trick is the same with the DefaultPredatorInputSecondaryConfig object, but with the KEY_VOID enum.

b) display of the keys

The following deals with the text we'll add in the Predator Key Configuration menu to display our previous changes.

In AvP, the common displayed texts - i.e. "textstrings" - are located in an extern file: the "language.txt" one. What is important to understand is that this file is compressed and it respects a special structure: this one is dicted by the TEXTSTRING_ID enum in the langenum.h file, that is to say, each textstring is in the same place as each enum in that file. So in our case, we want the new key to appear in the menu just under the "key that handles the cycle vision" for the Predator, i.e. the TEXTSTRING_PREDATOR_KEY_CYCLE_VISION, enum - not that hard to understand huh ?! So as to reflect your changes, just put after that line: TEXTSTRING_PREDATOR_KEY_CYCLE_VISION_BACK, (our new textstring for the new command). Okay, now that it's done, the "language.txt" file must be adapted too. Here comes my little manipulation (here you'll need the huffcomp tool):

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 your textstrings file "language.txt" to the new "mylanguage.txt" file.

  • open the "mylanguage.txt" file, locate the "Cycle Vision Modes" textstring and just after, put your new one: "Cycle Vision Modes Back". 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

At this point, the game knows that the menu's got a new key and that it'll be associated with player's input. Now open the usr_io.h file and see the PLAYER_INPUT_CONFIGURATION structure.

This structure represents all player inputs, i.e. all species inputs. Therefore, some will be common (ex.: jumping, crouching, etc...) and others adapted for each player type (ex.: Predator's cloaking, Marine's infrared vision, etc...).

Looking back at our structure, we can understand that it's first part ("unsigned char" variables separated) is the common input part and the second one (with the "union" keywords) is the specific part.

So, in our mod, we want to change the Predator's part by adding a new input. In order to kept the stucture integrity, we need to insert our input just after the CycleVisionMode one and because each union must contain only 1 specie's input, then we must change the unions as follow:

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 PreviousWeapon; // Predator & Marine
unsigned char Taunt; // Alien
};

union
{
unsigned char FlashbackWeapon; // Predator & Marine
unsigned char Alien_MessageHistory; // Alien
};

union
{
unsigned char Cloak; // Predator
unsigned char ImageIntensifier; // Marine
unsigned char Alien_Say; // Alien
};

union
{
unsigned char CycleVisionMode; // Predator
unsigned char ThrowFlare; // Marine
unsigned char Alien_SpeciesSay; // Alien
};

union
{
unsigned char CycleVisionModeBack; // Predator // ==> Our new Predator input
unsigned char Jetpack; // Marine
unsigned char Alien_ShowScores; // Alien
};

union
{
unsigned char ZoomIn; // Predator
unsigned char MarineTaunt; // Marine
};

union
{
unsigned char ZoomOut; // Predator
unsigned char Marine_MessageHistory; // Marine
};

union
{
unsigned char GrapplingHook; // Predator
unsigned char Marine_Say; // Marine
};

union
{
unsigned char RecallDisc; // Predator
unsigned char Marine_SpeciesSay; // Marine
};

union
{
unsigned char PredatorTaunt; // Predator
unsigned char Marine_ShowScores; // Marine
};

unsigned char Predator_MessageHistory; // Predator
unsigned char Predator_Say;
unsigned char Predator_SpeciesSay;
unsigned char Predator_ShowScores;
unsigned char ExpansionSpace7;
unsigned char ExpansionSpace8;
} PLAYER_INPUT_CONFIGURATION;
As you can see, each Predator input were displaced in the bottom, so we increased the size of the structure. So we will now adjust the game so as to take in account the new input.

In this example, you need to change the lines
#if PREDATOR_DEMO||DEATHMATCH_DEMO
#define NUMBER_OF_PREDATOR_INPUTS 30
#else
#define NUMBER_OF_PREDATOR_INPUTS 30
#endif
to:
#if PREDATOR_DEMO||DEATHMATCH_DEMO
#define NUMBER_OF_PREDATOR_INPUTS 30
#else
#define NUMBER_OF_PREDATOR_INPUTS 31
#endif
(located just above the structure).

Okay, so we've got our new CycleVisionModeBack input: in AvP, this variable will be accessed from the KeyboardInput array (KeyboardInput[primaryInput->CycleVisionModeBack] for the primary config and KeyboardInput[secondaryInput->CycleVisionModeBack] 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 Predator section, just after the Rqst_CycleVisionMode request, put the line unsigned int Rqst_CycleVisionModeBack :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_Jetpack = 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 "ckeck for character specific abilities" section, the Predator part, add the following lines:
( . . . )
if(DebouncedKeyboardInput[primaryInput->CycleVisionMode]
||DebouncedKeyboardInput[secondaryInput->CycleVisionMode])
playerStatusPtr->Mvt_InputRequests.Flags.Rqst_CycleVisionMode = 1;
// Here we test our input state to activate our new request
if(DebouncedKeyboardInput[primaryInput->CycleVisionModeBack]
||DebouncedKeyboardInput[secondaryInput->CycleVisionModeBack])
playerStatusPtr->Mvt_InputRequests.Flags.Rqst_CycleVisionModeBack = 1;
#if !(PREDATOR_DEMO||DEATHMATCH_DEMO)
if(DebouncedKeyboardInput[primaryInput->GrapplingHook]
||DebouncedKeyboardInput[secondaryInput->GrapplingHook])
playerStatusPtr->Mvt_InputRequests.Flags.Rqst_GrapplingHook = 1;
#endif

( . . . )
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 that our key is bound to an input and that one to a request, we need to create the desired movement effect (i.e. the vision cycle in the reverse way) and adapt the movement manager.
a) the new movement effect

Okay, so all we need to do now is to create another cycle for the Predator vision : this is handled in the file Vision.c. Just after the extern void ChangePredatorVisionMode(void) procedure, add the following code:
extern void ChangePredatorVisionModeBack(void) // Our new Predator's vision cycle
{
switch (CurrentVisionMode)
{
case VISION_MODE_NORMAL:
{
CurrentVisionMode=VISION_MODE_PRED_SEEPREDTECH;
break;
}
case VISION_MODE_PRED_THERMAL:
{
CurrentVisionMode=VISION_MODE_NORMAL;
break;
}
case VISION_MODE_PRED_SEEALIENS:
{
CurrentVisionMode=VISION_MODE_PRED_THERMAL;
break;
}
case VISION_MODE_PRED_SEEPREDTECH:
{
CurrentVisionMode=VISION_MODE_PRED_SEEALIENS;
break;
}
}
Sound_Play(SID_VISION_ON,"h");
PredatorVisionChangeCounter=ONE_FIXED;
}
b) adaptation of the movement manager

Let's finish the source code mods with the movement manager update : just open the Pmove.c file, then locate the void ExecuteFreeMovement(STRATEGYBLOCK* sbPtr) procedure and right after

if (playerStatusPtr->Mvt_InputRequests.Flags.Rqst_CycleVisionMode)
{
ChangePredatorVisionMode();
}
add the following code :
if (playerStatusPtr->Mvt_InputRequests.Flags.Rqst_CycleVisionModeBack) {
ChangePredatorVisionModeBack();
}
4) Using the mod

As you may have seen in the "Adding a Marine movement to reload weapons" tutorial, 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, select any playing mode, test your new move, change your configuration...

5) Conclusion

We've seen that to add such a player movement, we had to change the Predator Key Menu, by adding a new item in the movements list that we've called "Cycle Vision Modes Back" and updating deeper the player's inputs and requests system. Finally, we added the new movement, which consisted in making a new procedure for the Predator's Vision System, the easiest way to achieve our goal according to me.

I hope that in contrast with the pre-cited tutorial, you understood the constraints we can have in making such mods.

Special note:
The reason for adding such a line is the same as exposed in the "Adding a Marine movement to reload weapons" tutorial, but this time, our new request wasn't the last one in the PLAYER_INPUT_REQUESTS structure, so the result was that the Rqst_Jetpack request became the 33th bit of the structure, i.e. one bit over the size of the PLAYER_STATUS.Mvt_InputRequests.Mask variable and then, when this variable is reset to 0 (in the void InitPlayerGameInput(STRATEGYBLOCK* sbPtr) procedure of the usr_io.c file), the rest of the structure remains unchanged: the effect produced woul be an infinited loop for the activation of the Marine Jetpack !!!