Creating a henchmen with Animal Companion

Instead of (this line is the function’s declaration in NWScript):

void AddHenchman(oMaster, oHenchman=OBJECT_SELF);

Use (this is function’s call in your script):

AddHenchman(oMaster, oHenchman);

Or in your case, simply pass master and henchman objects:

AddHenchman(Isiovien, oChicken);

But Isiovien is undefined. Try this before adding the hench:

object Isiovien = GetObjectByTag("TAG_OF_YOUR_NPC_HERE");

Can you fix DecrementRemainingFeatUses in your script?

Hello

I manage to compile the script. Here it is:

void main()
{
if(!GetHasFeat(FEAT_ANIMAL_COMPANION))
       {
        SendMessageToPC(GetFirstPC(), "Cannot use feat");
        return;
       }

    // Object type to create
    int nObjectType = OBJECT_TYPE_CREATURE;

    // What to create - a "standard" chicken
    string sChicken = "nw_chicken";

    // Where to make the chicken appear
    location lTarget = GetLocation(OBJECT_SELF);

    // Actually create the object
    object oChicken = CreateObject(nObjectType, sChicken, lTarget);


    object Isiovien = GetObjectByTag("ISIOVIEN");

    AddHenchman(Isiovien, oChicken);

    DecrementRemainingFeatUses(Isiovien, FEAT_ANIMAL_COMPANION);
}

I didn’t understand why the GetObjectByTag. Another thing is that I managed to change the chicken to Pebbles the black bear. But this workaround behaves diferently than the SummonAnimalCompanion. The bear is following me, when I tell the druid henchman to stand ground, the bear keeps following me. It doesn’t happen this way with the nameless badger, it obeys the druid. Is it supposed to be like this?

Thank you.

Creature follows object who is its master (set via AddHenchman). If bear is following you then you made it follow you (or follow OBJECT_SELF and it happened that OBJECT_SELF was the PC).

We made some progress, so here’s the above script re-written to be used as a summon AC function. Having a function is not needed but allows you to specify which AC to summon for which creature. It makes code more robust, reusable.

Read the code and the comments; understand them; there is nothing new here. Notice however how oMaster is referenced everywhere inside the function so that the functionality is wrapped around it.

// creates creature with ResRef sResRef and turns it
// into a henchman of oMaster. oMaster must have
// FEAT_ANIMAL_COMPANION available and usable
void SummonFakeAnimalCompanion(string sResRef, object oMaster=OBJECT_SELF)
{
    // if oMaster cannot use AC summon feat, it means it
    // doesn't have the feat or used it today (needs resting)
    if(!GetHasFeat(FEAT_ANIMAL_COMPANION, oMaster))
    {
        SendMessageToPC(GetFirstPC(), GetName(oMaster) + " cannot summon AC");
        // return - end of function
        return;
    }

    SendMessageToPC(GetFirstPC(), "Summoning AC with ResRef " + sResRef +
        " for " + GetName(oMaster));

    // create new creature with ResRef sResRef at the
    // location of oMaster (will appear very close to it)
    object oAnimal = CreateObject(OBJECT_TYPE_CREATURE,
        sResRef, GetLocation(oMaster));

    SendMessageToPC(GetFirstPC(), "Summoned " + GetName(oAnimal));

    // add oAnimal as henchman of oMaster
    AddHenchman(oMaster, oAnimal);

    // reduce number of AC summon feat uses of oMaster
    DecrementRemainingFeatUses(oMaster, FEAT_ANIMAL_COMPANION);
}

void main()
{
    // assume that "Isiovien" is the druid - AC's master
    // (choose one method, comment the other with //)

    // METHOD 1:
    // if Isiovien is the caller of this script, that is,
    // Isovien is OBJECT_SELF here, do this:
    SummonFakeAnimalCompanion("nw_chicken", OBJECT_SELF);

    // METHOD 2:
    // if this script is called by someone else (i.e. PC),
    // find Isiovien by object tag and then pass it to the function
    object oIsiovien = GetObjectByTag("ISIOVIEN");
    SummonFakeAnimalCompanion("nw_chicken", oIsiovien);
}

There are two summon methods in main, which differ in how oMaster is selected. Use first if you run this script during a conversation with a NPC, so that oMaster = this NPC. Use second if you run this script somewhere else, so that oMaster becomes Isiovien (accessed via tag).

Hello

Thank you for changing the script, but the bear keeps following the PC. Could it be that I did something wrong when I created the bear? I used the first method because it’s called from conversation and changed “nw_chicken” with the tag of the bear I created. The chicken follows no one.

Thank you for your help and patience.

Chicken follows no one because it is a NPC with a non-henchman script set. This is correct as it exists here only to test CreateObject. Summoned creatures (and “real” ACs) use henchman AI, which makes them follow their master, respond to orders, etc.

What’s your bear’s ResRef? Does PC talk to itself? Try this so we can see who’s going to be the master:

SendMessageToPC(GetFirstPC(), "Master: " + GetName(OBJECT_SELF));
SummonFakeAnimalCompanion("nw_chicken", OBJECT_SELF);

Also try the second approach. It ensures a specific creature (who does not need to be talked to right now) will become the master.

Hello
The bear’s name is Pebbles, the tag is PEBBLES and the blueprint ResRef is pebbles. I don’t understand what you mean by PC talking to himself. You mean the yellow text? if so, yes and with this new code you provided it proves that the henchman is the master. If I expel her from the party I expel the bear too.

The method two changed nothing. I’ve been thinking, when I created the bear, in the tab “scripts”, I clicked “load scriptset” like I do with the henchmen I create and chose the file “Set_Xp1_Henchmen.ini”. Could it be the cause that it is following the PC instead of the Master?

Thank you.

The yellow text is just the debug from SendMessageToPC. It’s for your information.

Using XP1 henchmen script set shouldn’t cause trouble as they are mostly built on top of nw_ch_* with some extra code which you probably don’t need.

Instead of your bear, try using ResRef nw_ac_bear01 and tell me how this bear behaves.

Hello

I did as you said and it summoned a Brown Bear Companion, but he does follows me around even when I tell the druid henchman to stand ground.

Thank you.

Are you positive that the bear is a henchman of the henchman (i.e. you can’t give the bear “attack nearest” etc orders via radial menu?

When I test that code and summon it for some NPC, it follows that NPC.

Can you test this on a freshly-made module? Can you empty your override folder? (Make a backup of it). Perhaps something interferes with the system.

Hello

Sorry for the delay. The animal follower is not a henchman. The radial menu has things like examine, attack, talk, special abilities. It’s just like the nameless badger.

I exported the druid and tested in other modules and in a freshly created area. The animal still follows me. It sometimes follows the PC closer than the master!

I cut the Override and pasted on the desktop but to no avail.

Could it be the conversation file? I copied it from other module, I think it’s from 69MEH69. I was trying to make a dialogue from scratch but is hard for someone who never done before.

Is your Neverwinter nights Enhanced Edition? If not maybe it changed?

Thank you very much.

Try this module: ac_test.zip (8.9 KB)

It was made with Diamond, where it works as expected.

Hello

I tried your module, I see it comes with a druid. Guess what, her bear still follows me even when I tell her to stand ground. I even thought another thing: I had files in the Development folder, but I removed them and still the same thing. I don’t know what else to do.

So I guess Beamdog broke something. Well, it’s better to have a disobedient Animal Companion than no Animal Companion at all.

Thank you very much for your help and patience.

Alright, one last test: ac_test2.7z (7.3 KB)

Make the druid summon all three companions and tell me what they do.

  • bear is a vanilla animal companion
  • ox is a custom creature with standard hench script set (same as bear)
  • cow is a custom creature with custom script set

They should all follow her.

Also make sure you don’t have any possibly interfering haks enabled as patches via nwnpatch.ini.

Hello

I removed the userpatch.ini thing but it changed nothing, but I have good news: While the bear and the ox didn’t work, they still follow the PC, the cow works! It follows the druid and when I tell her to stand ground the cow does too.

Thank you.

//::///////////////////////////////////////////////
//:: Associate: Heartbeat
//:: NW_CH_AC1.nss
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
    Move towards master or wait for him
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: Nov 21, 2001
//:: Updated On: Jul 25, 2003 - Georg Zoeller
//:://////////////////////////////////////////////
#include "X0_INC_HENAI"
#include "X2_INC_SUMMSCALE"

#include "X2_INC_SPELLHOOK"
void main()
{   //SpawnScriptDebugger();

    // GZ: Fallback for timing issue sometimes preventing epic summoned creatures from leveling up to their master's level.
    // There is a timing issue with the GetMaster() function not returning the fof a creature
    // immediately after spawn. Some code which might appear to make no sense has been added
    // to the nw_ch_ac1 and x2_inc_summon files to work around this
    // This code is only run at the first hearbeat
    int nLevel =SSMGetSummonFailedLevelUp(OBJECT_SELF);
    if (nLevel != 0)
    {
        int nRet;
        if (nLevel == -1) // special shadowlord treatment
        {
          SSMScaleEpicShadowLord(OBJECT_SELF);
        }
        else if  (nLevel == -2)
        {
          SSMScaleEpicFiendishServant(OBJECT_SELF);
        }
        else
        {
            nRet = SSMLevelUpCreature(OBJECT_SELF, nLevel, CLASS_TYPE_INVALID);
            if (nRet == FALSE)
            {
                WriteTimestampedLogEntry("WARNING - nw_ch_ac1:: could not level up " + GetTag(OBJECT_SELF) + "!");
            }
        }

        // regardless if the actual levelup worked, we give up here, because we do not
        // want to run through this script more than once.
        SSMSetSummonLevelUpOK(OBJECT_SELF);
    }

    // Check if concentration is required to maintain this creature
    X2DoBreakConcentrationCheck();

    object oMaster = GetMaster();
    if(!GetAssociateState(NW_ASC_IS_BUSY))
    {

        //Seek out and disable undisabled traps
        object oTrap = GetNearestTrapToObject();
        if (bkAttemptToDisarmTrap(oTrap) == TRUE) return ; // succesful trap found and disarmed

        if(GetIsObjectValid(oMaster) &&
            GetCurrentAction(OBJECT_SELF) != ACTION_FOLLOW &&
            GetCurrentAction(OBJECT_SELF) != ACTION_DISABLETRAP &&
            GetCurrentAction(OBJECT_SELF) != ACTION_OPENLOCK &&
            GetCurrentAction(OBJECT_SELF) != ACTION_REST &&
            GetCurrentAction(OBJECT_SELF) != ACTION_ATTACKOBJECT)
        {
            if(
               !GetIsObjectValid(GetAttackTarget()) &&
               !GetIsObjectValid(GetAttemptedSpellTarget()) &&
               !GetIsObjectValid(GetAttemptedAttackTarget()) &&
               !GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN))
              )
            {
                if (GetIsObjectValid(oMaster) == TRUE)
                {
                    if(GetDistanceToObject(oMaster) > 6.0)
                    {
                        if(GetAssociateState(NW_ASC_HAVE_MASTER))
                        {
                            if(!GetIsFighting(OBJECT_SELF))
                            {
                                if(!GetAssociateState(NW_ASC_MODE_STAND_GROUND))
                                {
                                    if(GetDistanceToObject(GetMaster()) > GetFollowDistance())
                                    {
                                        ClearActions(CLEAR_NW_CH_AC1_49);
                                        if(GetAssociateState(NW_ASC_AGGRESSIVE_STEALTH) || GetAssociateState(NW_ASC_AGGRESSIVE_SEARCH))
                                        {
                                             if(GetAssociateState(NW_ASC_AGGRESSIVE_STEALTH))
                                             {
                                                //ActionUseSkill(SKILL_HIDE, OBJECT_SELF);
                                                //ActionUseSkill(SKILL_MOVE_SILENTLY,OBJECT_SELF);
                                             }
                                             if(GetAssociateState(NW_ASC_AGGRESSIVE_SEARCH))
                                             {
                                                ActionUseSkill(SKILL_SEARCH, OBJECT_SELF);
                                             }
                                             //MyPrintString("GENERIC SCRIPT DEBUG STRING ********** " + "Assigning Force Follow Command with Search and/or Stealth");
                                             ActionForceFollowObject(oMaster, GetFollowDistance());
                                        }
                                        else
                                        {
                                             //MyPrintString("GENERIC SCRIPT DEBUG STRING ********** " + "Assigning Force Follow Normal");
                                             ActionForceFollowObject(oMaster, GetFollowDistance());
                                             //ActionForceMoveToObject(GetMaster(), TRUE, GetFollowDistance(), 5.0);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                else if(!GetAssociateState(NW_ASC_MODE_STAND_GROUND))
                {
                    if(GetIsObjectValid(oMaster))
                    {
                        if(GetCurrentAction(oMaster) != ACTION_REST)
                        {
                            ClearActions(CLEAR_NW_CH_AC1_81);
                            if(GetAssociateState(NW_ASC_AGGRESSIVE_STEALTH) || GetAssociateState(NW_ASC_AGGRESSIVE_SEARCH))
                            {
                                 if(GetAssociateState(NW_ASC_AGGRESSIVE_STEALTH))
                                 {
                                    //ActionUseSkill(SKILL_HIDE, OBJECT_SELF);
                                    //ActionUseSkill(SKILL_MOVE_SILENTLY,OBJECT_SELF);
                                 }
                                 if(GetAssociateState(NW_ASC_AGGRESSIVE_SEARCH))
                                 {
                                    ActionUseSkill(SKILL_SEARCH, OBJECT_SELF);
                                 }
                                 //MyPrintString("GENERIC SCRIPT DEBUG STRING ********** " + "Assigning Force Follow Command with Search and/or Stealth");
                                 ActionForceFollowObject(oMaster, GetFollowDistance());
                            }
                            else
                            {
                                 //MyPrintString("GENERIC SCRIPT DEBUG STRING ********** " + "Assigning Force Follow Normal");
                                 ActionForceFollowObject(oMaster, GetFollowDistance());
                            }
                        }
                    }
                }
            }
            else if(!GetIsObjectValid(GetAttackTarget()) &&
               !GetIsObjectValid(GetAttemptedSpellTarget()) &&
               !GetIsObjectValid(GetAttemptedAttackTarget()) &&
               !GetAssociateState(NW_ASC_MODE_STAND_GROUND))
            {
                //DetermineCombatRound();
            }

        }
        // * if I am dominated, ask for some help
        if (GetHasEffect(EFFECT_TYPE_DOMINATED, OBJECT_SELF) == TRUE && GetIsEncounterCreature(OBJECT_SELF) == FALSE)
        {
            SendForHelp();
        }

        if(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT))
        {
            SignalEvent(OBJECT_SELF, EventUserDefined(1001));
        }
    }
}



Ok, so the script is the same. Here’s the final test: ac_test3.7z (10.8 KB)

1 Like

Hello

In this new test the bear keeps pursuing me, but the ox and the cow follows the druid and when I tell her to stand ground both obey her.

Thank you.

I saved the vanilla nw_ch_ac_1 in the module (for the bear) and used a renamed copy for the ox.

The only conclusion is that you (still) have something enabled (an AI system?) somewhere which trumps the script from the module.

Resource lookup order has changed in EE. See it here and try to find the issue. Look for any suspicious hak files.

Hello

I will think through on what could be, but at first I don’t know what could be. In the override folder I only have Tony K inventory and familiar ai, CCOH, new rain texture, a few .uti of items I created and the packages for the henchmen, but I removed the override folder and the problem continues. In the development folder I have CCOH only. userpatch.ini I have just a few uninportant things, the HAK folder is full of files, I never empty the hak folder.

HAKs interfere even if they aren’t used in the module? I will try to remove the whole Neverwinter Nights my documents folder and see what happens.

Thank you very much.

Yeah, try that. You can simply rename it for test. Haks from hak folder won’t cause any trouble. But definitely clear override and development folders.

Hello

I did that… and it worked. The bear from the test module you sent actually follows the druid now. I remember that I was playing a module that interfered with the Tony K AC ai, so I couldn’t tell the AC to be brave and it always fled from battle. So I copied Tony K ai to the development folder, and this is what was causing the trouble with the script.

Sorry for all the trouble I have caused and thank you for helping me.