[FIXED] Storing/Retrieving Henchmen Using the Functions in X0_I0_HENCHMAN

EDIT - FIX IS DOWN FURTHER

I’ve isolated a few glitches with the persistence of henchmen and I think I have a possible solution, but its a little beyond my scripting capabilities…

Here’s the issue (the functions referred to are in x0_i0_henchman):

  1. When henchmen are exported to the database using StoreCampaignHenchman because they are stored as a local object, any variables set on them are deleted/removed.
  2. When you retrieve the henchmen using RetrieveCampaignHenchmen, the henchman is immediately rehired. This is a workaround Bioware did because if you don’t immediately rehire them, they turn hostile.
  3. Horses exported and retrieved from the database in a dismounted state are processed as henchmen. This causes the horse to return in a state where, when mounted, you end up with two portraits of the horse in the party window. One portrait is interactive (you can right-click it and get the radial options), the other portrait is a “ghost”. Its there, but you can’t interact with it. Lastly, you cannot remove the horse as a henchman using the radial “remove from party” option.
  4. Custom mounts exported and retrieved from the database in a dismounted state have all the issues noted above AND are non-rideable. This occurs because the variables that define it as a mount have been deleted/removed.

I’m thinking the following can work, but I’m not sure how to go about doing it:

  1. When the PC leaves the Server (logs off) a “doll” is created in the PC’s inventory, one for for each henchman and dismounted horse the PC is “master” of. The doll stores a particular henchman/horse’s ResRef, and Name as a local variable on the doll item.
  2. Any variables stored on the henchman/horse are copied to the doll item.
  3. When the PC returns to the server, the dolls, having been stored in the PC’s bic, allow the PC to summon back all his henchmen/horses, copy the variables back to them, and then rehire them (for henchmen) or set the PC as owner (for horses).

Anyone care to help me figure out just how to do this?

I don’t have exact scripts for that, but a general example, which you might tweak:

After initializing the database eg. in OnModLoad I use this code to store and get appearance values:

// PC appearance store (tool)
// Save the appearance of a PC to a database
// Called in the conversation of the tool

void main ()
{
object oPC = GetPCSpeaker();

//Get current values of the PC
SendMessageToPC(oPC, "Storing your appearance!");

int iAppear = GetAppearanceType(oPC);
string sAppear = IntToString (iAppear);
SendMessageToPC(oPC, "Appearance value is: " + sAppear);

int iHead = GetCreatureBodyPart(CREATURE_PART_HEAD, oPC);
string sHead = IntToString (iHead);
SendMessageToPC(oPC, "Head value is: " + sHead);

int iPheno = GetPhenoType(oPC);
string sPheno = IntToString (iPheno);
SendMessageToPC(oPC, "Phenotype value is: " + sPheno);

string sPortraitResRef = GetStringLeft(GetPortraitResRef(oPC),11);
SendMessageToPC (oPC, "Portrait-ResRef is: " + sPortraitResRef);

// Write value into database
SetCampaignInt("DLCR_RACEAPPEARANCE", "DLCR_MDL_APPEAR", iAppear, oPC);
SetCampaignInt("DLCR_RACEAPPEARANCE", "DLCR_MDL_HEAD", iHead, oPC);
SetCampaignInt("DLCR_RACEAPPEARANCE", "DLCR_MDL_PHENO", iPheno, oPC);
SetCampaignString("DLCR_RACEAPPEARANCE", "DLCR_MDL_PORTRAIT", sPortraitResRef, oPC);
}
// PC appearance restore (tool)
// Load the appearance of a PC from a database
// Called in the conversation of the tool

void main ()
{
object oPC = GetPCSpeaker();

//Get values from the database to restore the appearance
SendMessageToPC(oPC, "Restoring your appearance! - Importing data!");

int iAppear = GetCampaignInt("DLCR_RACEAPPEARANCE", "DLCR_MDL_APPEAR",oPC);
string sAppear = IntToString (iAppear);
SendMessageToPC(oPC, "Appearance value from DB is: " + sAppear);

int iHead = GetCampaignInt("DLCR_RACEAPPEARANCE", "DLCR_MDL_HEAD",oPC);
string sHead = IntToString (iHead);
SendMessageToPC(oPC, "Head value from DB is: " + sHead);

int iPheno = GetCampaignInt("DLCR_RACEAPPEARANCE", "DLCR_MDL_PHENO",oPC);
string sPheno = IntToString (iPheno);
SendMessageToPC(oPC, "Phenotype value from DB is: " + sPheno);

string sPortraitResRef = GetCampaignString("DLCR_RACEAPPEARANCE", "DLCR_MDL_PORTRAIT", oPC);
SendMessageToPC(oPC, "Portrait-ResRef from DB is: " + sPortraitResRef);

// Just for fun. An effect…
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_FNF_STRIKE_HOLY), oPC, 1.5);

// Applying the loaded data
SetCreatureAppearanceType(oPC, iAppear);
SetCreatureBodyPart(CREATURE_PART_HEAD, iHead , oPC);
SetPhenoType(iPheno, oPC);
SetPortraitResRef(oPC, sPortraitResRef);
}

Not sure, if that is, what you are asking for…

@BlackRider Could you please edit that post so that the script is in a code block. The reason is that if you look closely at your screen you’ll see that your quotation marks have been changed from

""

to

“”

by the Discourse software that runs these forums. All you should need to do is to put [code] immediately before the first line of code and [/code] after. Aside from restoring your quotation marks, it should also restore your layout and will give you a copy button in the top right corner of the code block when it is reposted.

TR

Thanks to Txpple on the Vault Discord - this issue is fixed.

The Fix is to the RetrieveCampaignHenchman function in x0_i0_henchman. I’ve included both revised henchman functions (since both need an upgrade to work with multiple henchmen).

// Call this function when the PC is about to leave a module
// to enable restoration of the henchman on re-entry into the
// sequel module. Both modules must use the same campaign db
// for this to work.
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 oHenchDB;
    object oHenchFixed;
    object oDupe;
    int iSlot;
    int iMax = GetMaxHenchmen();
    string sHench;
    for (iSlot = 1; iSlot <= iMax; iSlot++)
    {
        sHench = "Henchman" + IntToString(iSlot);
        oHenchDB = RetrieveCampaignDBObject(oPC, sHench, lLoc);
        DelayCommand(0.5, DeleteCampaignDBVariable(oPC, sHench));

        //Pull henchman object in db storage, but it must be fixed.  If you AddHenchman from a DB
        //retrieved object, it will often not RemoveHenchman and stay bugged in the game.
        //We have to CopyObject the real DB retrieved object because it is buggy.  CopyObject somehow
        //"cleans" out the object and the Henchman wlil remove properly every time.  So everytime you summon your
        //henchman, you are actually copying him, killing him off, and using his fixed duplicate.
        oHenchFixed = CopyObject(oHenchDB, GetLocation(oPC));
        AddHenchman(oPC, oHenchFixed);
        AssignCommand(oHenchDB, SetIsDestroyable(TRUE, FALSE, FALSE));
        AssignCommand(oHenchDB, DestroyObject(oHenchDB));

        if (GetIsObjectValid(oHenchFixed))
        {
            DelayCommand(0.5, HireHenchman(oPC, oHenchFixed));
            oDupe = GetNearestObjectByTag(GetTag(oHenchFixed), oHenchFixed);
            if ((oDupe != OBJECT_INVALID) && (oDupe != oHenchFixed))
            {
                AssignCommand(oDupe,SetIsDestroyable(TRUE));
                SetPlotFlag(oDupe,FALSE);
                SetImmortal(oDupe,FALSE);
                DestroyObject(oDupe);
            }
        }
        else
        {
            DBG_msg("No valid henchman retrieved");
        }
    }
}
3 Likes

Update - I must say that the Bioware Horse System works VERY well in initial testing for my PW project. The only issue I’ve found is that if you used placed horses that PCs can take ownership of, when the PC next logs in, the original horse the PC took will not be recognized as a duplicate by RetrieveCampaignHenchman. I’m sure its just a timing issue, but fixing it is low priority as it is much better, imo, to make horses only available through a horse merchant.

Other than that I am EXTREMELY HAPPY with being able to use these as Bioware intended without the need for sloppy “widget” workarounds.

Thanks again to Txpple.

2 Likes

@Tarot_Redhand
Thanks for the notice. I was a bit in a hurry. Edited and I hope i got oll the quotes…