Skip to content

Henchman Won't Transition

ZephiriusZephirius Member Posts: 419
edited January 2023 in Builders - Scripting
I've got a hell hound henchman that doesn't follow me through open air transitions. Anyone know a bit of code that would remedy this?
Just checked with standard door transitions - mixed results with the hell hound "spawning" about 10 seconds after area transition click. Not good! :(
Post edited by Zephirius on

Comments

  • ProlericProleric Member Posts: 1,316
    This is typically an issue if the transition is to another place in the same area.

    For an instant fix, you can use the version of nw_g0_transition from my Travel Builder package on the Vault. You just need that one script.

    This uses an iterative approach for each associate type. It works fine. A more elegant solution is to use GetFirst / NextPartyMember, which I leave as an excercise for the reader.
  • ZephiriusZephirius Member Posts: 419
    edited January 2023
    Thanks Proleric. Gonna download now.
  • MelkiorMelkior Member Posts: 216
    Yeah, intra-area jumps don't by default send your associates too.
    I made a script called "PetsJumpToo" which should do what you want.
    // Jumps all of the master's "pets" (summons, familiars, etc.) to the
    // same location as their master.  Intended only for intra-area use, as
    // inter-area transitions automatically jump pets as well.
    void PetsJumpToo(object oMaster=OBJECT_SELF,object oDest=OBJECT_INVALID)
    {
    // Exit immediately if the "master" isn't a player.
      if(!GetIsPC(oMaster)) return;
    // If there's no destination, the location of the player is the place
    // to send the pets to.
      if(!GetIsObjectValid(oDest)) oDest=oMaster;
    // Get the location from the object.
      location lDest=GetLocation(oDest);
    // If the location's area is invalid, do nothing because the player
    // must be in an area transition.
      if(GetAreaFromLocation(lDest)==OBJECT_INVALID) return;
    // Loop through henchmen.
      int iNum=1;
      object oSummon=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oMaster,iNum++);
      while(GetIsObjectValid(oSummon))
      {
        AssignCommand(oSummon,JumpToLocation(lDest));
        oSummon=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oMaster,iNum++);
      }
    // Animal companions.
      iNum=1;
      oSummon=GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION,oMaster,iNum++);
      while(GetIsObjectValid(oSummon))
      {
        AssignCommand(oSummon,JumpToLocation(lDest));
        oSummon=GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION,oMaster,iNum++);
      }
    // Dominated creatures.
      iNum=1;
      oSummon=GetAssociate(ASSOCIATE_TYPE_DOMINATED,oMaster,iNum++);
      while(GetIsObjectValid(oSummon))
      {
        AssignCommand(oSummon,JumpToLocation(lDest));
        oSummon=GetAssociate(ASSOCIATE_TYPE_DOMINATED,oMaster,iNum++);
      }
    // Familiars.
      iNum=1;
      oSummon=GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oMaster,iNum++);
      while(GetIsObjectValid(oSummon))
      {
        AssignCommand(oSummon,JumpToLocation(lDest));
        oSummon=GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oMaster,iNum++);
      }
    // Summons.
      iNum=1;
      oSummon=GetAssociate(ASSOCIATE_TYPE_SUMMONED,oMaster,iNum++);
      while(GetIsObjectValid(oSummon))
      {
        AssignCommand(oSummon,JumpToLocation(lDest));
        oSummon=GetAssociate(ASSOCIATE_TYPE_SUMMONED,oMaster,iNum++);
      }
    }
    
  • ProlericProleric Member Posts: 1,316
    That's similar to my script, which fixes all transitions instantly by customising the default nw_g0_transition.

    Mine is iterative, so it handles associates of associates to any level of nesting.

    Like I said, you can also do all associate types and follower nesting in one go, using GetFirst / NextPartyMember, which I would do if writing it today.
  • MelkiorMelkior Member Posts: 216
    Proleric wrote: »
    That's similar to my script, which fixes all transitions instantly by customising the default nw_g0_transition.

    Mine is iterative, so it handles associates of associates to any level of nesting.

    Like I said, you can also do all associate types and follower nesting in one go, using GetFirst / NextPartyMember, which I would do if writing it today.

    That's a good idea although for multi-player, you'd probably want to not automatically jump all party members but only the party member who entered the transition plus their summons, which is what my script was designed for.
  • ProlericProleric Member Posts: 1,316
    Note the clarification to GetFirstPartyMember() in the Lexicon, which gives the parameter setting to do exactly that.
  • MelkiorMelkior Member Posts: 216
    You mean GetFirstFactionMember()?
    I'm pretty sure that the description in the Lexicon is incorrect, in that FALSE returns both PCs and NPCs whereas TRUE returns only the PCs in the party.

    Regardless, using FALSE would return ALL NPCs in the faction, and not only the NPCs who are "pets" of the PC making the jump, which means that in a multi-player module you'd be attempting to transition all summons from all players in the party to the destination, even if they happened to be in a different area or different room in the same area at the time. That's why my function only searches for pets belonging to the PC which the function has been given.

    An alternative would be to use my GetTrueMaster() function to find the "owner" of the pet and only jump it to the new location if the master is the PC who jumped.
    // Ensures that the true ultimate master of a summon is found in a multi-summon module.
    object GetTrueMaster(object oSummon=OBJECT_SELF)
    {
      if(GetObjectType(oSummon)!=OBJECT_TYPE_CREATURE) return OBJECT_INVALID;
      object oMaster=GetMaster(oSummon);
      while(oMaster!=GetMaster(oMaster))
        oMaster=GetMaster(oMaster);
      return oMaster;
    }
    
    (If you attempt to get the "master" of a creature which has no master, GetMaster() will return the same creature)

    The reason for this function existing is that in two cases, the GetMaster() function will not return the PC. The first case is where a summon in turn calls a second summon. In that case, the second summon's master is the first summon. The second case is where the module permits multiple summons of the same type. The second summon automatically becomes a summon of your first summon, and a third summon automatically becomes a summon of your second summon. This function will automatically go up through the chain of "masters" until it finds the ultimate master, regardless of whether that's a PC or a NPC.
  • NeverwinterWightsNeverwinterWights Member Posts: 339
    edited January 2023
    I'm a bit rusty and this is untested, but shouldn't something like this work, as per @Proleric suggestion?:
    void TeleportMyFollowers(object oPC)
    {
        object oFollower = GetFirstFactionMember(oPC, FALSE);
        while (GetIsObjectValid(oFollower))
        {
            if (GetMaster(oFollower) == oPC && !GetIsPC(oFollower))
            {
    	    AssignCommand(oFollower, ClearAllActions(TRUE));
                AssignCommand(oFollower, JumpToObject(oPC));
                //or JumpToLocation()
            }
            oFollower = GetNextFactionMember(oPC, FALSE);
        }
    }
    

    Also as per the remarks on the GetMaster() funtion on the Lexicon: "This can be used in a loop to find the PC who ultimately controls the associate (for instance, if there are multiple henchmen, or if the henchman has summoned a creature)"
    Post edited by NeverwinterWights on
  • ProlericProleric Member Posts: 1,316
    That looks right. Best to ClearAllActions(TRUE) before jumping.

    Doing it that way takes care of the nested associate-of-associate cases without any special code. For example, henchman horses, familiars, etc
  • MelkiorMelkior Member Posts: 216
    I'm a bit rusty and this is untested, but shouldn't something like this work, as per @Proleric suggestion?:
    void TeleportMyFollowers(object oPC)
    {
        object oFollower = GetFirstFactionMember(oPC, FALSE);
        while (GetIsObjectValid(oFollower))
        {
            if (GetMaster(oFollower) == oPC && !GetIsPC(oFollower))
            {
                JumpToObject(oPC);
                //or JumpToLocation()
            }
            oFollower = GetNextFactionMember(oPC, FALSE);
        }
    }
    

    Also as per the remarks on the GetMaster() funtion on the Lexicon: "This can be used in a loop to find the PC who ultimately controls the associate (for instance, if there are multiple henchmen, or if the henchman has summoned a creature)"

    While that looks right at first glance, at second glance it appears to me that it will skip all the summons of summons if this is an intra-area jump rather than an inter-area jump. This is because all of the summons of summons will not have a PC as their master and therefore they will be skipped. But I haven't tested it to be sure. Perhaps the auto-catchup code used for summons will kick in and send the summons of summons to the destination?
  • ProlericProleric Member Posts: 1,316
    Good catch - the GetMaster() check should be omitted.
  • NeverwinterWightsNeverwinterWights Member Posts: 339
    Melkior wrote: »
    I'm a bit rusty and this is untested, but shouldn't something like this work, as per @Proleric suggestion?:
    void TeleportMyFollowers(object oPC)
    {
        object oFollower = GetFirstFactionMember(oPC, FALSE);
        while (GetIsObjectValid(oFollower))
        {
            if (GetMaster(oFollower) == oPC && !GetIsPC(oFollower))
            {
                JumpToObject(oPC);
                //or JumpToLocation()
            }
            oFollower = GetNextFactionMember(oPC, FALSE);
        }
    }
    

    Also as per the remarks on the GetMaster() funtion on the Lexicon: "This can be used in a loop to find the PC who ultimately controls the associate (for instance, if there are multiple henchmen, or if the henchman has summoned a creature)"

    While that looks right at first glance, at second glance it appears to me that it will skip all the summons of summons if this is an intra-area jump rather than an inter-area jump. This is because all of the summons of summons will not have a PC as their master and therefore they will be skipped. But I haven't tested it to be sure. Perhaps the auto-catchup code used for summons will kick in and send the summons of summons to the destination?

    So the lexicon remarks about GetMaster() are not correct? It says that it should get summons and summons' summons.
  • ProlericProleric Member Posts: 1,316
    ...only in an iterative loop.

    If a PC has a henchman who has a summons, the summons is a member of the PC faction, but their master is the henchman, not the PC.
  • MelkiorMelkior Member Posts: 216
    ... so in summary, if the module is single-player only then the best way of doing what you want is with GetFirstFactionMember() / GetNextFactionMember() for the PC's faction and jumping them all, but;

    if you want this to be for a multiplayer module, the best way to do it is probably to either use GetFirst/NextFactionMember along with GetTrueMaster() to ensure that you only jump the pets belonging to the PC who made the transition, or use PetsJumpToo() so that all of the pets of the PC who made the transition get jumped.

    Anything else in a multiplayer module is going to cause problems, I believe.
  • ZephiriusZephirius Member Posts: 419
    edited January 2023
    Sincere thanks to all who contributed to this thread. I'll post back with the results.
    Z
  • ZephiriusZephirius Member Posts: 419
    edited January 2023
    void main()
    {
        int nHench;
        object oHench;
        object oTarget;
    
        // Get the creature who triggered this event.
        object oPC = GetEnteringObject();
    
        // Find the location to which to teleport.
        oTarget = GetWaypointByTag("WP_JUMP_02");
    
        // Teleport the PC.
        AssignCommand(oPC, ClearAllActions());
        AssignCommand(oPC, JumpToObject(oTarget));
    
        // Also teleport associates.
        oHench = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC);
        AssignCommand(oHench, ClearAllActions());
        AssignCommand(oHench, JumpToObject(oTarget));
        oHench = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC);
        AssignCommand(oHench, ClearAllActions());
        AssignCommand(oHench, JumpToObject(oTarget));
        oHench = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC);
        AssignCommand(oHench, ClearAllActions());
        AssignCommand(oHench, JumpToObject(oTarget));
        oHench = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC);
        AssignCommand(oHench, ClearAllActions());
        AssignCommand(oHench, JumpToObject(oTarget));
    
        // Support for multiple henchmen (includes horses).
        nHench = 1;
        oHench = GetHenchman(oPC, 1);
        while ( oHench != OBJECT_INVALID )
        {
            AssignCommand(oHench, ClearAllActions());
            AssignCommand(oHench, JumpToObject(oTarget));
            // Next henchman.
            oHench = GetHenchman(oPC, ++nHench);
        }
    }
    

    This worked well for open-air transitions. The hell hound is now following me promptly through to the other side. I didn't realize there was an option in the script generator to send your associates when you jump.
  • ProlericProleric Member Posts: 1,316
    As previously mentioned, that won't work for associates-of-associates. Some may follow eventually, when their AI / engone tells them to, but IIRC there are cases where even that doesn't work, e.g. henchman horses.

    Hence the iterative method in my nw_g0_transition, or the GetFirst/NextFactionMember method (for single-player, at least).
Sign In or Register to comment.