Animal Companion (Showing New Selection GUI)

Hi All,

Can someone explain how the Animal Companion works with respect to Dinosaur Companion feat when a PC multi-classes.

Here is the situation … My wife is playing a druid and she levelled, but had to choose a fighter class because her alignment had changed. (5th Druid 1st Fighter) At the same time she chose Dinosaur Companion as the upgrade for her PC’s Animal Companion feat. However, when she uses the Animal Companion feat, she only gets the original boar creature.

Have things been “restricted” due to her having chosen to multi-class with a fighter?

Thanks in advance.
Lance.

hey Lance,

nah …

the summon animal companion spellscript ‘nw_s2_animalcom’ has to check the caster for the DinosaurCompanion feat. If found, an appropriate resref-string needs to be passed to

SummonAnimalCompanion(object oMaster=OBJECT_SELF, string sResRef="")

 
It can get complicated 'cause feats like TelthorCompanion and feats that might enhance the animal all need to be juggled/prioritized …

@kevL_s

Thanks for responding … So, should she have had the dinosaur companion appear rather than the boar? I mean she has the feat with the PC, but when she uses Animal Summoning, she only gets the boar and not the dinosaur.

Am I misunderstanding something here?

Thanks, Lance.

well … the stock script looks like this

//::///////////////////////////////////////////////
//:: Summon Animal Companion
//:: NW_S2_AnimalComp
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
    This spell summons a Druid's animal companion
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: Sept 27, 2001
//:://////////////////////////////////////////////

void main()
{
    //Yep thats it
    SummonAnimalCompanion();
}

i don’t see how that can summon a dinosaur … perhaps i’m missing something but I don’t use that script. The one im using looks like

//::///////////////////////////////////////////////
//:: Summon Animal Companion
//:: 'nw_s2_animalcom'
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
    This spellability summons an Animal Companion.
*/
// Created By: Preston Watamaniuk
// Created On: Sept 27, 2001
// kevL's
// 2012 jul 15 - note this is a modified Kaedrin's 1.41.4 script.
//             - added spellhook & tidy up.
// 2013 jan 06 - fixed apply DamageReduction w/ Exalted Companion.
//             - calls kL_SummonCMIAnimComp() to get around
//               issues w/out Kaedrin's PrC pack ...
// 2013 apr 20 - swapped in alphaConstants for numeric.
// 2016 sep 10 - refactor.
// 2017 apr 28 - huge refactor.


#include "x2_inc_spellhook"
#include "mrop_inc_const"
#include "mrop_inc_assoc"

//
void main()
{
    //SendMessageToPC(GetFirstPC(FALSE), "Run ( nw_s2_animalcom ) - " + GetName(OBJECT_SELF));

    // kL_RoM: addition of Spellhook
    if (GetLocalInt(GetModule(), MROP_VAR_ACTIVE) // the spellhook will fire in Learn mode if TRUE
        && !X2PreSpellCastCode())
    {
        return;
    }


    object oCaster = OBJECT_SELF;

    string sResref, sLabel;

    int iType = GetLocalInt(oCaster, VAR_ANICOTYPE); // console AniCo ->
    if (iType)
    {
        sResref = Get2DAString(HEN_COMPANION, BASERESREF_PREFIX, --iType); // decr to get the true ref
        sLabel  = GetLocalString(oCaster, VAR_ANICOLABEL);
    }

    kL_SummonAnimalCompanion(oCaster, sResref, sLabel);
}

And it’s support #include

// 'mrop_inc_assoc'
/*
    This is the include file for my content and will hopefully allow others to
    remap my content for their own use more easily.

    IMPORTANT: This file supercedes 'cmi_animcom'.
*/
// Created By: Kaedrin (Matt)
// modified by kevL: this is a subset of the kPrC pack's 'cmi_animcom'
// so that users wishing to recompile MRoP scripts don't need kPrC installed.
// Note that some versions of the kPrC don't include source anyway ....
//
// kevL's
// 2012 jun 17 - merged w/ Cyphre's Companions Pack.
// 2013 jan 06 - fixed problem with Animal Companions not leveling w/ PC
//               if Kaedrin's PrC pack is not also installed;
//               'cmi_player_levelup' deletes the string_var "cmi_animcomp"
//               on the PC object. This bypasses that ....
// 2013 jan 07 - renamed the file and functions to not conflict w/ Kaedrin's original.
// 2016 sep 09 - added constants and kL_ApplySilverFangEffect().
//             - refactor.
// 2016 sep 18 - changed name of file from 'kl_cmi_animcom' to 'mrop_cmi_animcom'.
// 2020 mar 14 - changed name of file from 'mrop_cmi_animcom' to 'mrop_inc_assoc'.


/*******************************************************************************
 * INCLUDES
 */

#include "cmi_includes"


/*******************************************************************************
 * CONSTANTS
 */

/*
const int CLASS_LION_TALISID         = 123; // these constants are needed for the Release version.

const int FEAT_EPIC_ANIMAL_COMPANION = 1959;
const int FEAT_EPIC_DRAGON_COMPANION = 2002;
const int FEAT_NATURAL_BOND          = 2106;
const int FEAT_DEVOTED_TRACKER       = 3697;
const int FEAT_TELTHOR_COMPANION     = 3704;
const int FEAT_IMPROVED_NATURAL_BOND = 3705;
const int FEAT_SILVER_FANG           = 3527;
const int FEAT_EXALTED_COMPANION     = 3133;
*/

const string VAR_ANICOTYPE  = "AniCoType";
const string VAR_ANICOLABEL = "AniCoLabel";


/*******************************************************************************
 * PROTOTYPES
 */

void   kL_SummonAnimalCompanion(object oCaster, string sResref, string sLabel);
string kL_GetStandardResref(object oCaster);
int    kL_GetTier(object oCaster, int bEleType, int bTotalLevel);
int    kL_GetCasterTier(object oCaster, int bTotalLevel = FALSE);
void   kL_ApplyFeatEffects(object oAniCo, object oCaster);


/*******************************************************************************
 * FUNCTIONS
 */

// public.
void kL_SummonAnimalCompanion(object oCaster, string sResref, string sLabel)
{
    int bTotalLevel = (sResref != ""); // is console Anico.
    if (!bTotalLevel)
    {
        if (GetTag(oCaster) == "co_umoja") // Umoja from SoZ always gets his Dino.
        {
            SendMessageToPC(oCaster, "<c=seagreen>Umoja calls to nature for his trusted companion.</c>");
            sResref = "c_ancom_dino";
        }
        else if (GetHasFeat(FEAT_TELTHOR_COMPANION, oCaster, TRUE))     // Telthor Companion takes precedence over the rest.
        {
            sResref = "cmi_ancom_telthor";
            sLabel  = "Spirit Telthor"; // <- TODO: that in blueprints
        }
        else if (GetHasFeat(FEAT_EPIC_DRAGON_COMPANION, oCaster, TRUE)) // Epic Dragon Companion next
        {
            switch (GetAlignmentGoodEvil(oCaster))
            {
                case ALIGNMENT_GOOD:
                case ALIGNMENT_NEUTRAL:
                    sResref = "c_ancom_bronze";
                    break;

                case ALIGNMENT_EVIL:
                    sResref = "c_ancom_blue";
            }
        }
        else if (GetHasFeat(FEAT_DINOSAUR_COMPANION, oCaster, TRUE))    // Deinonychus ...
        {
            sResref = "c_ancom_dino";
        }
        else if ((sResref = GetLocalString(oCaster, "sAnico")) == "")   // else standard type ->
        {
            sResref = kL_GetStandardResref(oCaster);
            SetLocalString(oCaster, "sAnico", sResref);
        }
    }

    if (sResref != "")
    {
        int bEleType = (FindSubString(sResref, "ele") != -1);
        sResref += IntToString(kL_GetTier(oCaster, bEleType, bTotalLevel));

        SummonAnimalCompanion(oCaster, sResref); // replace current associate

        object oAniCo = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oCaster);
        if (GetIsObjectValid(oAniCo))
        {
            kL_ApplyFeatEffects(oAniCo, oCaster);

            if (sLabel != "")
                SetFirstName(oAniCo, sLabel);
        }
        else
            SendMessageToPC(oCaster, "<c=red>ERROR ( mrop_inc_assoc )"
                    + " cannot find Animal Companion resref : "
                    + sResref + "</c>");
    }
    else
        SendMessageToPC(oCaster, "<c=red>ERROR ( mrop_inc_assoc )"
                + " Animal Companion resref is blank !</c>");
}

// private.
string kL_GetStandardResref(object oCaster)
{
    SummonAnimalCompanion(oCaster); // need to summon the associate just to find out what it is.
    // because ->
//  iType = GetAnimalCompanionCreatureType(...); <- doesn't fing work!!
    // hence we do this the hard way ->

    // Screw that. Store the baseresref-prefix on the character.
    // Even better store its RowID from Hen_Companion.2da

    string sTag = GetTag(GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oCaster));

    if (FindSubString(sTag, "badger") != -1)        // #0 badger - Hen_Companion.2da
        return "c_ancom_badger";

    if (FindSubString(sTag, "wolf") != -1)          // #1 wolf
        return "c_ancom_wolf";

    if (FindSubString(sTag, "bear") != -1)          // #2 bear
        return "c_ancom_bear";

    if (FindSubString(sTag, "boar") != -1)          // #3 boar
        return "c_ancom_boar";

    if (FindSubString(sTag, "spider") != -1)        // #4 giant spider
        return "c_ancom_spider";

    if (FindSubString(sTag, "blue") != -1)          // #5 blue dragon
        return "c_ancom_blue";

    if (FindSubString(sTag, "panther") != -1)       // #6 panther
        return "c_ancom_panther";

    if (FindSubString(sTag, "bronze") != -1)        // #4 bronze dragon
        return "c_ancom_bronze";

    if (FindSubString(sTag, "dino") != -1)          // #8 deinonychus
        return "c_ancom_dino";

                                                    // #9, #10 : not used.

    if (FindSubString(sTag, "elew") != -1)          // #11 water elemental
        return "cmi_ancom_elew";

    if (FindSubString(sTag, "elef") != -1)          // #12 fire elemental
        return "cmi_ancom_elef";

    if (FindSubString(sTag, "telthor") != -1)       // #13 telthor wolf
        return "cmi_ancom_telthor";

    if (FindSubString(sTag, "elea") != -1)          // #14 air elemental
        return "cmi_ancom_elea";

    if (FindSubString(sTag, "elee") != -1)          // #15 earth elemental
        return "cmi_ancom_elee";

    if (FindSubString(sTag, "abo") != -1)           // #16 arctic boar
        return "c_ancom_abo";

    if (FindSubString(sTag, "awo") != -1)           // #17 arctic wolf
        return "c_ancom_awo";

    if (FindSubString(sTag, "hshark") != -1)        // #18 hammerhead shark
        return "c_ancom_hshark";

    if (FindSubString(sTag, "snake") != -1)         // #19 highland snake
        return "c_ancom_snake";

    if (FindSubString(sTag, "mshark") != -1)        // #20 mako shark
        return "c_ancom_mshark";

    if (FindSubString(sTag, "mlion") != -1)         // #21 mountain lion
        return "c_ancom_mlion";

    if (FindSubString(sTag, "bpolar") != -1)        // #22 polar bear
        return "c_ancom_bpolar";

    if (FindSubString(sTag, "sleo") != -1)          // #23 snow leopard
        return "c_ancom_sleo";

    if (FindSubString(sTag, "wolverine") != -1)     // #24 wolverine
        return "c_ancom_wolverine";

    if (FindSubString(sTag, "tiger") != -1)         // #25 tiger
        return "c_ancom_tiger";

    if (FindSubString(sTag, "stiger") != -1)        // #26 snow tiger
        return "c_ancom_stiger";

    if (FindSubString(sTag, "white_tiger") != -1)   // UNVERIFIED
        return "c_ancom_white_tiger";

    if (FindSubString(sTag, "grender") != -1)       // #27 gray render
        return "c_ancom_grender";

    if (FindSubString(sTag, "smilo") != -1)         // #28 smilodon
        return "c_ancom_smilo";

    if (FindSubString(sTag, "deino") != -1)         // #29 deinonychus
        return "c_ancom_deino";

    if (FindSubString(sTag, "megara") != -1)        // #30 megaraptor
        return "c_ancom_megara";

    if (FindSubString(sTag, "jsnake") != -1)        // #31 jungle snake
        return "c_ancom_jsnake";

    if (FindSubString(sTag, "myconid") != -1)       // #32 myconid
        return "c_ancom_myconid";

    if (FindSubString(sTag, "psimyco") != -1)       // #33 psionic myconid
        return "c_ancom_psimyco";

    if (FindSubString(sTag, "wmyco") != -1)         // #34 wood myconid
        return "c_ancom_wmyco";

    if (FindSubString(sTag, "felion") != -1)        // #35 female lion
        return "c_ancom_felion";

    if (FindSubString(sTag, "malion") != -1)        // #36 male lion
        return "c_ancom_malion";

    if (FindSubString(sTag, "dmt") != -1)           // #37 dinosaur
        return "c_ancom_dmt";

                                                    // #38 : dire badger
                                                    // #39 : dire bear
                                                    // #40 : dire boar
                                                    // #41 : dire wolf
                                                    // #42 : dire wolverine
                                                    // #43 : dire rat
                                                    // #44 : dire spider
                                                    // #45 : celestial dire bear!
                                                    // #46 : giant scorpion
                                                    // #47 : giant ant

    if (FindSubString(sTag, "shambling") != -1)     // #48 shambling mound
        return "c_ancom_shamblingm";

    if (FindSubString(sTag, "winter") != -1)        // #49 winter wolf, UNVERIFIED.
        return "c_ancom_winter_wolf";

    if (FindSubString(sTag, "worg") != -1)          // #50 worg
        return "c_ancom_worg";

    if (FindSubString(sTag, "basilisk") != -1)      // #51 basilisk
        return "c_ancom_basilisk";

                                                    // #52 : umberhulk
                                                    // #53 : glow spider
                                                    // #54 : phase spider
                                                    // #55 : snow spider
                                                    // #56 : manticore

    if (FindSubString(sTag, "gcube") != -1)         // #57 gelatinous cube
        return "c_ancom_gcube";

                                                    // #58 - 68 : not used.

    if (FindSubString(sTag, "toki") != -1)          // #69 Toki of Caerbannog
        return "c_acp_toki";

    return ""; // not found
}

// private.
int kL_GetTier(object oCaster, int bEleType, int bTotalLevel)
{
    if (bEleType)
        return (kL_GetCasterTier(oCaster, bTotalLevel) + 2) / 6 + 1;

    return kL_GetCasterTier(oCaster, bTotalLevel) / 3 + 1;
}

// private.
int kL_GetCasterTier(object oCaster, int bTotalLevel = FALSE)
{
    if (bTotalLevel)
        return GetTotalLevels(oCaster, FALSE);

    int iTier = 0;

    if (   GetHasFeat(1835, oCaster) // Animal Domain Cleric (yep, all 3 of them.)
        || GetHasFeat( 312, oCaster)
        || GetHasFeat(1520, oCaster))
    {
        iTier += GetLevelByClass(CLASS_TYPE_CLERIC, oCaster);
    }

    int iClass = GetLevelByClass(CLASS_TYPE_RANGER, oCaster);
    if (iClass > 3) iTier += (iClass - 3);

    iTier += GetLevelByClass(CLASS_TYPE_DRUID, oCaster);
    iTier += GetLevelByClass(CLASS_LION_TALISID, oCaster);

    if (GetHasFeat(FEAT_DEVOTED_TRACKER, oCaster))
    {
        iClass = GetLevelByClass(CLASS_TYPE_PALADIN, oCaster);
        if (iClass > 4) iTier += (iClass - 4);
    }

    if (GetHasFeat(FEAT_TELTHOR_COMPANION, oCaster))
    {
        iClass = GetLevelByClass(CLASS_TYPE_SPIRIT_SHAMAN, oCaster);
        if (iClass > 3) iTier += (iClass - 3);
    }

    if (GetHasFeat(FEAT_IMPROVED_NATURAL_BOND, oCaster))
    {
        if ((iClass = GetHitDice(oCaster)) > iTier
            && (iTier += 6) > iClass)
        {
            iTier = iClass;
        }
    }
    else if (GetHasFeat(FEAT_NATURAL_BOND, oCaster))
    {
        if ((iClass = GetHitDice(oCaster)) > iTier
            && (iTier += 3) > iClass)
        {
            iTier = iClass;
        }
    }

    if (GetHasFeat(FEAT_EPIC_ANIMAL_COMPANION, oCaster))
        iTier += 3;

    return iTier;
}

// private.
void kL_ApplyFeatEffects(object oAniCo, object oCaster)
{
    if (GetHasFeat(FEAT_SILVER_FANG, oCaster))
    {
        object oWeapon;

        int iSlot = INVENTORY_SLOT_CWEAPON_L;
        for (; iSlot != INVENTORY_SLOT_CARMOUR; ++iSlot)
        {
            if (GetIsObjectValid(oWeapon = GetItemInSlot(iSlot, oAniCo)))
                SetItemBaseMaterialType(oWeapon, GMATERIAL_METAL_ALCHEMICAL_SILVER);
        }
    }

    string sTag = GetTag(oAniCo);
    if (FindSubString(sTag, "blue") != -1 || FindSubString(sTag, "bronze") != -1) // is Dragon
    {
        effect eEffect = EffectImmunity(IMMUNITY_TYPE_SNEAK_ATTACK);
               eEffect = SupernaturalEffect(eEffect);

        DelayCommand(0.f, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oAniCo));
    }
    else if (FindSubString(sTag, "ele") != -1) // is Elemental
    {
        effect eEffect = EffectSetScale(0.3f);
               eEffect = SupernaturalEffect(eEffect);

        DelayCommand(0.f, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oAniCo));
    }


    int iHd = GetHitDice(oAniCo);
    effect eSr = EffectSpellResistanceIncrease(iHd + 5);

    int bExalted = FALSE;
    if (GetHasFeat(FEAT_EXALTED_COMPANION, oCaster))
    {
        bExalted = TRUE;

        effect eLink;

        int iPower;
        if (iHd > 11) iPower = 10; // Reduct power ->
        else          iPower =  5;

        effect eReduct = EffectDamageReduction(iPower, DAMAGE_POWER_PLUS_ONE);

        if (iHd > 7) iPower = 10; // Resist power ->
        else         iPower =  5;

        switch (GetAlignmentGoodEvil(oCaster))
        {
            default:
            case ALIGNMENT_GOOD: // Celestial ->
            {
                AdjustAlignment(oAniCo, ALIGNMENT_GOOD, 100);

                effect eCold = EffectDamageResistance(DAMAGE_TYPE_COLD,       iPower);
                effect eFire = EffectDamageResistance(DAMAGE_TYPE_FIRE,       iPower);
                effect eElec = EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, iPower);

                eLink = EffectLinkEffects(eCold, eFire);
                eLink = EffectLinkEffects(eLink, eElec);

                effect eSef = EffectNWN2SpecialEffectFile("fx_exalted_celestial", oAniCo);
                eLink = EffectLinkEffects(eLink, eSef);
                break;
            }

            case ALIGNMENT_NEUTRAL: // Abnormal ->
            {
                AdjustAlignment(oAniCo, ALIGNMENT_NEUTRAL, 50);

                effect eCold = EffectDamageResistance(DAMAGE_TYPE_COLD,  iPower);
                effect eFire = EffectDamageResistance(DAMAGE_TYPE_FIRE,  iPower);
                effect eSonc = EffectDamageResistance(DAMAGE_TYPE_SONIC, iPower);

                eLink = EffectLinkEffects(eCold, eFire);
                eLink = EffectLinkEffects(eLink, eSonc);

                effect eSef = EffectNWN2SpecialEffectFile("fx_exalted_abnormal", oAniCo);
                eLink = EffectLinkEffects(eLink, eSef);
                break;
            }

            case ALIGNMENT_EVIL: // Fiendish ->
            {
                AdjustAlignment(oAniCo, ALIGNMENT_EVIL, 100);

                effect eCold = EffectDamageResistance(DAMAGE_TYPE_COLD, iPower);
                effect eFire = EffectDamageResistance(DAMAGE_TYPE_FIRE, iPower);
                effect eAcid = EffectDamageResistance(DAMAGE_TYPE_ACID, iPower);

                eLink = EffectLinkEffects(eCold, eFire);
                eLink = EffectLinkEffects(eLink, eAcid);

                effect eSef = EffectNWN2SpecialEffectFile("fx_exalted_fiendish", oAniCo);
                eLink = EffectLinkEffects(eLink, eSef);
            }
        }

        effect eDarkVision = EffectDarkVision();

        eLink = EffectLinkEffects(eLink, eDarkVision);
        eLink = EffectLinkEffects(eLink, eSr);
        eLink = EffectLinkEffects(eLink, eReduct);

        eLink = SupernaturalEffect(eLink);
        DelayCommand(0.f, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oAniCo));
    }

    if (GetHasFeat(FEAT_DEVOTED_TRACKER, oCaster)) // Paladin L5
    {
        FeatAdd(oAniCo, FEAT_DASH,             FALSE);
        FeatAdd(oAniCo, FEAT_EVASION,          FALSE);
        FeatAdd(oAniCo, FEAT_IMPROVED_EVASION, FALSE);

        effect eLink = EffectACIncrease(2);

        if (!bExalted && GetLevelByClass(CLASS_TYPE_PALADIN, oCaster) > 14)
        {
            eLink = EffectLinkEffects(eLink, eSr);
        }

        eLink = SupernaturalEffect(eLink);
        DelayCommand(0.f, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oAniCo));
    }
}

 
maybe SoZ uses its own script for the DinosaurCompanion idk … am just saying if you want to look into it, that’s the sequence ( 'cause apparently what you got now aint workin quite right… )

1 Like

Hi KevL,

Ah … That’s what I discovered when I tried searching for what was going on … I never had access to the other script you have posted. So thank you for that!

She has played a druid before and successfully upgraded to a dinosaur, so I never worried about how it worked before. However, after she experienced not having the dinosaur appear, that had me checking. We both thought it may have been caused due to her no longer being a full druid, but I could not find anything in the rules to support that. (So I guess that function is “hard-coded” for various checks.)

Looks like I may have to implement your scripts then … just when I thought I could continue with module two … sigh

Is that your own script or from an earlier NWN2? EDIT: OK, I just read the script comments … :wink:

Thanks in advance.
Lance.

EDIT: Am I missing some other includes, or is it a matter of its all here, but I need to refigure it?

k  :)

it’s a mishmash, mostly a top to bottom rework of Kaedrin’s PrC script(s)

So… you’ll have to pick yer way through it and take only what you need/want

the dino’s resref must be stored on the character-object somehow … (but that’s a guess)

1 Like

probably. But again it’s a matter of what you need

1 Like

OK … That’s what I thought you might say … :slight_smile:

1 Like

I may try experimenting with that approach too … maybe … but I think I may prefer the just do it for myself with your scripts too .:wink:

Thanks once again for your help! Appreciated!

1 Like

okie doke

Have a good one, Lance

1 Like

ps. Anything dealing with MRoP and both VAR_ANICOTYPE and VAR_ANICOLABEL can be ignored.

MRoP is for the MetaRod of Preparation, and the other vars are for consoling in an AnimalCompanion for Paladins  :)

1 Like

OK. Thanks for the heads up there! :slight_smile:

1 Like

I think I know what’s going on in the original scenario, based on a quick test to confirm an assumption and my prior experience modding the game’s Animal Companions. Sorry for it being long, but the TL;DR is Don’t Take Dinosaur (or Dragon) Companion on a non-Druid or Ranger level because the game is dumb.

All characters have an Animal Companion and familiar set on them as two variables. You can see and change them in the toolset, with them being dropdowns. The variable for animal companions points to a line in hen_companion.2da for it to find and summon the blueprint with the defined string plus the stage of the animal companion’s progression, of which there are 12, as its resref. For example, as a level 5 druid the boar your wife would summon would be the creature in the toolset titled “Boar{3-5}” with the resref “c_ancom_boar2”, as her class level is within the 3-5 level range.

When you take a level of Druid (or hit level 4 in ranger), after the feat selection step the game is programmed to open up an “Animal Companion Selection” step. Picking an animal companion through this step sets the associated variable on your character. When you take the Dragon Companion or Dinosaur Companion feats, it is programmed to open this menu as well, nullifying all options except for those that need the feat you just took. (ie, a Blue or Bronze Dragon for Dragon Companion, and a Deinonychus for Dinosaur Companion) Selecting the new animal companion from the list and finishing character creation then tells the game to change the animal companion variable to the new one.

However, I’m assuming there’s a bit of messy code in there, likely an If being put in the wrong spot. If I may guess a bit, the game checks if you’re leveling up as a druid or a ranger before it actually opens the animal companion UI, as the window is useless to other classes. After that, it then checks the level of your character so that it only opens it at 1st and 4th level respectively, or if you’ve taken a feat that allows you to pick new animal companions, but only after you’ve passed the “Druid or Ranger” check. Thus, when she took the feat, she failed the “Druid or Ranger” check because she’s leveling up as a fighter, and thus didn’t check if she got the feat, and thus the level up didn’t know it had to change her animal companion variable to the Deinonychus.

If any of that was confusing, let me know.

On a similar note, IIRC the companion Umoja in Storm of Zehir doesn’t actually have Dinosaur Companion as a feat, but has the Deinonychus set as his animal companion. At least, that’s how he was last I checked.

4 Likes

@DerpCity,

Thank you for your detailed explanation. I think your assessment is correct. Now I understand that it was not a simple ruling, I am looking at how to fix the issue now.

In my own test, I noticed how the Animal Companion GUI opens if you take the feat as a druid during character creation levelling, but skips it when taking the fighter class.

Ideally, I want to be able to address it at this stage, as it would allow the personalisation GUI to fire again. However, I am not sure if I will be able to intercept it here due to the broken “dumb” code. Therefore, I may consider doing a check after the levelling that could open the same GUI or a cut-down home-brew one that first fixes the animal provided and allows naming it.

I’ll look at it today and post my results. It is not something I wish to spend too much time on, but would like it to work as intended.

Thanks, Lance.

1 Like

I’m speaking purely as a DM in pen in paper. Since she had an alignment shift from Neutral she should not be able to get the new animal companion. You may want to check the alignment properties. I hope this helps. In the pnp game she shouldn’t get an animal companion until her alignment is Neutral again. Then again NWN 2 has taken liberties with the rules before because Kelgar should not have been able to be a Monk either since he started as NG.

1 Like

Definitely my approach too! :smiley:

Change in alignment was an angle I first thought may be the case too. However, I tried levelling a “neutral” druid from 1-5 and then adding a fighter level (without an alignment change) and it still failed to offer the GUI. So, unfortunately, I think it still needs to be looked at.

At least the game (in my wife’s game) had prevented the druid from continuing as a druid due to the alignment change. :slight_smile: That part was correct.

Thanks, Lance.

2 Likes

Well at least you were able to rule that out.

1 Like

@Lance_Botelle I just did another test, I think I have an idea that’ll work using the ingame console. Aside from “DebugMode 1” to get access to the console commands, there are two commands that are needed: “GiveXP” and “rs ga_alignment (0,0)”.

  1. With GiveXP, you can actually take XP from a player character. Take enough away to reduce her character’s level, ie “GiveXP -5000” (though in my experience the command seems to take away more or less XP than you specify sometimes…).

  2. Then, you need to shift her alignment back towards neutrality using rs_ga_alignment (0,0). The first 0 represents the Good/Evil axis, and the second 0 represents the Law/Chaos axis: putting a positive number pushes you towards the first of each axis, and a negative pushes you to the other. Just a note, but 1/-1 shifts you 1 point, 2/-2 seems to shift you 3 points, 3/-3 seems to shift you 10 points, and 4/-4 seems to shift you 70. Any other number doesn’t seem to work.

  3. Then you want to level your wife’s character again, but this time level her up as a Druid. Take the Dinosaur Companion feat. This will prompt the UI to show up and let you pick the Deinonychus.

  4. Delevel your wife’s character. If you summon your animal companion at this point, you should notice you have a Deinonychus without having the Dinosaur Companion feat.

  5. Fix your wife’s alignment and level her up as a fighter again, taking the Dinosaur Companion feat for consistency’s sake.

  6. Turn off DebugMode 0 so you don’t see the ugly yellow trigger boxes on the ground.

After putting in more effort than is probably worth, she should now have a Deinonychus and have her fighter level. Admittedly, I tested this in singleplayer and I haven’t played NWN2 multiplayer very much, so apologies if it doesn’t work.

It makes perfect sense to me. As you know, alignments are not static: The literal entire point of Khelgar’s questline is that he’s gradually shifting towards Lawful as he learns the importance of duty, honor, and justice. The only thing they didn’t do was actually shift Khelgar’s alignment until he becomes a Monk, which I assume was due to it being mechanically unnecessary or some sort of engine limitation. Its the same as a player’s Paladin going through a fall from grace and becoming a Blackguard, represented mechanically by shifting their alignment towards evil (and taking 5 points in hide for some reason only the gods know).

2 Likes

( ‘ginc_alignment’ maps the values to categorical alignment-shifts )

1 Like

@DerpCity

Thanks for considering this option for her … Unfortunately, that proverbial ship has sailed as she has continued as it stood for now. (She has played the module nine times already , so it’s not such a thing for her, especially as she is trying to find bugs anyway.)

However, having now sorted another issue I had been working on today, I can finally take a closer look and try to find a way (if possible) of fixing the issue if such should be encountered again.

As I say, I will update if and when I achieve anything … hopefully, I won’t be distracted with another issue. :slight_smile:

Thanks, Lance.

1 Like