Huntsman - Guide To Henchman: Advanced Topics

This is part of a series of tutorials on how to set up and modify the behaviour of the new henchmen brought in with the SoU and HoTU expansions. Other tutorials cover topics such as setting up a basic henchman, conversation interjections, one liners and party banter.


A demo module is available showing how the different types of conversation triggers are used, as well as most of the script changes outlined in this tutorial. You can download the module here.


Controlling the number of henchmen you can have.


If you want to have more than two henchmen per PC you will need to make an alteration to the 'HireHenchman()' function in 'x0_i0_henchman' as follows (approx line 481):


    int nCountHenchmen = X2_GetNumberOfHenchmen(oPC);
    int nNumberOfFollowers = X2_GetNumberOfHenchmen(oPC, TRUE);
    // * The true number of henchmen are the number of hired
    nCountHenchmen = nCountHenchmen ;

    int nMaxHenchmen = X2_NUMBER_HENCHMEN;

should be changed to:


    int nCountHenchmen = X2_GetNumberOfHenchmen(oPC);
    //int nNumberOfFollowers = X2_GetNumberOfHenchmen(oPC, TRUE);
    // * The true number of henchmen are the number of hired
    //nCountHenchmen = nCountHenchmen ;

    int nMaxHenchmen = GetMaxHenchmen();

Then save the altered file.


After that you will need to put the following line of script in the 'OnModuleLoad' script slot of the modules property sheet.


    SetMaxHenchmen(5); 

The number in brackets is where you change it to however many henchmen you want the player to be able to hire. In this case 5. If you just want the two henchmen then you do not need the other script changes, just the 'SetMaxHenchmen(2);' line in the 'OnModuleLoad' script.


Multiclass Henchmen.


These are setup in almost exactly the same way as you did in the first tutorial for the single class fighter except for a few script changes. Here is what you do.


1. Click on the word 'Tools' at the top of the toolset and then click on the option for 'Script Editor'.


2. With the script editor open click on the option at the top to 'Open an existing file' (hold the mouse over the top row of buttons to see what they are).


3. Paste in the script name 'x0_i0_henchman' to the box marked 'Resource name' and make sure to check the box marked 'All resources' at the left hand side. Then click the 'Open' button.


4. If you look at around line 1364 in function 'LevelUpXP1Henchman()' you will see an entry that looks like this (it might be a slightly different line number depending on if you have HotU or SoU installed.).


     else if (sTag == "x0_hen_dor")
     {
          LevelHenchmanUpTo(oAssociate, nLevel, CLASS_TYPE_CLERIC, 20);
     }

5. Now this may look a little complex, but it is not that bad really and you do not need to fully understand it to actually use it. All it does is tell the game what the second class of the henchman is going to be, if any, and what their maximum level in that class will be.


Look at the Tag of your henchman in your henchmans properties, and lets for this example say that it is 'Henchman_Bob'. You need to make your own entry to the file that is similar to the above, but using your henchmans tag, so yours would look like.


     else if (sTag == "henchman_bob")
     {
          LevelHenchmanUpTo(oAssociate, nLevel, CLASS_TYPE_CLERIC, 20);
     }

Just add your entry and save the script, but do not change the name of the script at all. (NOTE: you must use lower case for the tag in the script even if your tag has capital letters in it.)


6. The 'CLASS_TYPE_CLERIC' can be changed to whatever you want the second class of your henchman to be. You can see what choices there are by looking at the Constants list by clicking on the 'CONSTANTS' button to the right of the script editor window and typing 'class_type' into the filter box at the top. So you might want the second class to be a rogue, and so would use 'CLASS_TYPE_ROGUE' instead. The number 20 at the end of the line is the maximum level you want the henchman to level up to in this class.


7. Lastly to make sure the henchman levels up when you do during gameplay click on the word "Edit" at the top of your toolset, and choose the option "Module Properties", and click on the "Events" tab. In the script slot OnPlayerLevelUp make sure the script name "x1_playerlevelup" is added there.


Moving henchmen with you from one module to another.


To move a henchman with you from one module to another you need to set up a campaign database on both of the modules. Here is how you do that.


1) Place the below code in the OnModuleLoad script slot of BOTH of your modules. (Change "Henchmen" to something appropriate for your own campaign.)


void main()
{
    SetLocalString(GetModule(), "X0_CAMPAIGN_DB", "Henchmen");
}

2) Place the below code in the 'OnEnter' script of a generic trigger just before you jump the PC to the new module.


#include "x0_i0_henchman"
void main()
{
    object oPC = GetEnteringObject();
    StoreCampaignHenchman(oPC);
}

3) Place the below code in the 'OnEnter' script of the first area that the players enter.


#include "x0_i0_henchman"
void main()
{
    object oPC = GetEnteringObject();
    if(GetLocalInt(oPC, "LOAD_HENCHMAN") == 1)
        return;
    if(!GetIsPC(oPC))
        return;
    RetrieveCampaignHenchman(oPC);
    SetLocalInt(oPC, "LOAD_HENCHMAN", 1);
}

Thats all there is to it if you're only going to be carrying over a single henchman, but if you're intending the players to have more than one henchman you will need to do some additional work because Bioware only covered carrying one henchman forward with their function.


If you look at around line 1096 of the 'x0_i0_henchman' include file you will see a section of code that looks like this.


void StoreCampaignHenchman(object oPC)
{
    object oHench = GetHenchman(oPC, 1);
    if (!GetIsObjectValid(oHench)) 
    {
        DBG_msg("No valid henchman to store");
        return;
    }
    DBG_msg("Storing henchman: " + GetTag(oHench));
    int ret = StoreCampaignDBObject(oPC, sStoredHenchmanVarname, oHench);
    if (!ret) 
    {
        DBG_msg("Error attempting to store henchman");
    } 
    else 
    {
        DBG_msg("Henchman stored successfully");
    }
}
// Call this function when a PC enters a sequel module to restore
// the henchman (complete with inventory). The function
// StoreCampaignHenchman must have been called first, and both
// modules must use the same campaign db. (See notes in x0_i0_campaign.)
//
// The restored henchman will automatically be re-hired and will be
// created next to the PC.
//
// Any object in the module with the same tag as the henchman will be
// destroyed (to remove duplicates).
void RetrieveCampaignHenchman(object oPC)
{
    location lLoc = GetLocation(oPC);
    object oHench = RetrieveCampaignDBObject(oPC, sStoredHenchmanVarname, lLoc);
    // Delete the henchman object from the db
    DelayCommand(0.5, DeleteCampaignDBVariable(oPC, sStoredHenchmanVarname));
    if (GetIsObjectValid(oHench)) 
    {
        DelayCommand(0.5, HireHenchman(oPC, oHench));
        object oHenchDupe = GetNearestObjectByTag(GetTag(oHench),oHench);
        if (GetIsObjectValid(oHenchDupe) && oHenchDupe != oHench) 
        {
            DestroyObject(oHenchDupe);
        }
    } 
    else
    { 
        DBG_msg("No valid henchman retrieved");
    }
}

You need to replace that section of code with the code below. This will then make it work for however many henchmen you have.


void StoreCampaignHenchman(object oPC)
{
    object oHench;
    int ret;
    int iSlot;
    string sHench;
    int iMax = GetMaxHenchmen();
    for (iSlot = 1;iSlot <= iMax;iSlot++)
    {
        oHench = GetHenchman(oPC, iSlot);
        if (!GetIsObjectValid(oHench))
        {
            DBG_msg("No valid henchman to store");
        }
        else
        {
            DBG_msg("Storing henchman: " + GetTag(oHench));
            sHench = "Henchman" + IntToString(iSlot);
            ret = StoreCampaignDBObject(oPC, sHench, oHench);
            if (!ret)
            {
                DBG_msg("Error attempting to store henchman " + GetName(oHench));
            }
            else
            {
                DBG_msg("Henchman " + GetName(oHench) + " stored successfully");
            }
        }
    }
}
// Call this function when a PC enters a sequel module to restore
// the henchman (complete with inventory). The function
// StoreCampaignHenchman must have been called first, and both
// modules must use the same campaign db. (See notes in x0_i0_campaign.)
//
// The restored henchman will automatically be re-hired and will be
// created next to the PC.
//
// Any object in the module with the same tag as the henchman will be
// destroyed (to remove duplicates).
void RetrieveCampaignHenchman(object oPC)
{
    location lLoc = GetLocation(oPC);
    object oHench;
    object oDupe;
    int iSlot;
    int iMax = GetMaxHenchmen();
    string sHench;
    for (iSlot = 1; iSlot <= iMax; iSlot++)
    {
        sHench = "Henchman" + IntToString(iSlot);
        oHench = RetrieveCampaignDBObject(oPC, sHench, lLoc);
        DelayCommand(0.5, DeleteCampaignDBVariable(oPC, sHench));
        if (GetIsObjectValid(oHench))
        {
            DelayCommand(0.5, HireHenchman(oPC, oHench));
            oDupe = GetNearestObjectByTag(GetTag(oHench), oHench);
            if ((oDupe != OBJECT_INVALID) && (oDupe != oHench))
            {
                AssignCommand(oDupe,SetIsDestroyable(TRUE));
                SetPlotFlag(oDupe,FALSE);
                SetImmortal(oDupe,FALSE);
                DestroyObject(oDupe);
            }
        }
        else
        {
            DBG_msg("No valid henchman retrieved");
        }
    }
}

Note


The topics in these tutorials mean changing the 'x0_i0_henchman' file. Generally, changing official files is something to be avoided, especially for include files, as it can lead to confusion over which version is being used and possibly other problems too if Bioware ever updates these files in future expansions or patches.


Unfortunately sometimes there seems no way round it, either because the Bioware function doesnt work properly or in the case of an include file it is so widely used that you have to alter the original. The problem with include files is that just because you have changed the 'x0_i0_henchman' file it doesnt mean that every script that uses it will automatically be recompiled, even by a full build.


So all those official precompiled scripts that use 'x0_i0_henchman' will still be using the copy they were compiled with... the old copy.


In case you were thinking of finding each script file and modifying them slightly so they get recompiled, 'x0_i0_henchman' is used in nearly 400 official scripts out of over 3000 as of HoTU patch 1.61. It is possible but it will take a lot of time to change all of them.


Fortunately for us we are only modifying four functions, and those functions are only used by six script files, 'x0_d1_hen_hired','x0_d1_hen_rejoin', 'x0_o0_modleave', 'x0_o0_modenter', 'x1_playerlevelup' and 'x2_hen_spell.nss'. Provided these files are found and recompiled after changing 'x0_i0_henchman' the tutorial above should work as expected.


It is worth noting that these six scripts require no changes, they just need to be recompiled.





 author: Huntsman, editors: Grimlar, Mistress, contributor: Ken Cotterill