Huntsman - Guide To Henchman: Advanced Topics

From NWN Lexicon
Jump to: navigation, search

This is part of a series of tutorials on how to set up and modify the behavior 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.

Changing the maximum number of henchmen

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 nMaxHenchmen = GetMaxHenchmen();

Then save the altered file.

After that you will need to add the following line to the script in the OnModuleLoad slot of the Module Properties sheet.

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, you do not need the other script changes, just the SetMaxHenchmen(2); line in the OnModuleLoad script.

Multiclass henchmen

These are set up in almost exactly the same way as you did in the first tutorial for the single class fighter except for a few script changes.

  1. Open the Script Editor: in the menu at the top of the toolset, click "Tools", then select "Script Editor" from the drop-down menu.
  2. In the Script Editor window, click the Open an existing file' toolbar button (hold the mouse over the top row of buttons to see what they are).
  3. In the box marked "Resource Name", type "x0_i0_henchman". Make sure to check the box marked "All resources" on 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 henchman's properties (for this example, we'll 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 henchman's 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 your choices by looking at the Constants list. Click on the "CONSTANTS" button to the right side of the Script Editor window and type "CLASS_TYPE_" into the filter box at the top. (Alternatively, visit the CLASS_TYPE_* Constant Group page in the Lexicon). So if you want the second class to be a rogue, 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 select "Edit" from the top menu in the toolset and choose the option "Module Properties". Click on the "Events" tab. Set the script in the OnPlayerLevelUp script slot to "x1_playerlevelup".

Taking henchmen 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.

  1. Add the following code in the OnModuleLoad script slot of BOTH of your modules:
        SetLocalString(GetModule(), "X0_CAMPAIGN_DB", "Henchmen");
  2. Place the following 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 following code in the OnEnter script of the first area that the players enter in the new module.
    #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);
    }

That's all there is to it if you're only going to be carrying over a single henchman. If you're intending for the player to have more than one henchman, you will need to do some additional work as 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;
    string sHench;
    int ret, iSlot, 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)
{
    string sHench;
    object oHench, oDupe;
    location lLoc = GetLocation(oPC);
    int iSlot, iMax = GetMaxHenchmen();
 
    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");
    }
}

Special 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 doesn't 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 doesn't 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. Provided these files are found and recompiled after changing x0_i0_henchman, the tutorial above should work as expected. These six scripts require no changes; they just need to be recompiled.


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