Compatible Companion Mod Creation/The Fade

From Dragon Age Toolset Wiki
Jump to: navigation, search

This step fully describes how to give your custom companion their own nightmare and how to control their behaviour in the fade.

This tutorial and the gift handling are the hardest. There is so much stuff to do for this, I hope I don't forget anything. If someone tries it sent me message, tell me it worked. Otherwise I forgot something to tell you here probably. So let's take it slow.

What we need

When the PC first enters the fade, there are several important flags and module variables set automatically by the game. The plot resource of interest is CIR300PT_FADE and the flag ENTER_FADE, signals the PC is in Weisshaupt (where he/she meets the fake Duncan). This flag needs to be captured, so build the flag VALERIA_ENTER_FADE, into the NPC plot VALERIA_NPC_HIRE, in order to do this only once. There are three possible nightmares, each for each companion the PC had. We'll make it so our companion goes into the first available one.

We also need to capture the following flags from CIR300PT_FADE: SLOTH_DEMON_ATTACKS (that's the demon that put you to sleep, you fight him at the end of the fade), and SLOTH_DEMON_DEFEATED (when the demon is killed Valeria has to come back to the party, if she didn't help fight the demon).

So create in the plot VALERIA_NPC_HIRE the flags VALERIA_SLOTH_DEMON_ATTACKS, VALERIA_SLOTH_DEMON_DEFEATED (these are so we do it once), as well as, the following important flags for Valeria: VALERIA_HAS_NIGHTMARE_A, VALERIA_HAS_NIGHTMARE_B, VALERIA_HAS_NIGHTMARE_C (these three are to see to which area from the 3 possible Valeria is in)

First add this in includes:

...
#include "plt_valeria_fade_nightmare"
#include "plt_cir000pt_main"
#include "plt_cir300pt_fade"
...

Changes in the function check_plot_changed

Add the following lines in check_plot_changed, to capture important events in the PC's travels in the fade:

//enter the fade, remove valeria from party
    if (WR_GetPlotFlag(PLT_CIR300PT_FADE,ENTER_FADE) && !WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_ENTER_FADE))
    {
        WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_ENTER_FADE,TRUE);
//if she was in the party when the demon put you to sleep, she needs 
//to have a nightmare area for her
        if(WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY))
        {
            SetFollowerState(oVal, FOLLOWER_STATE_AVAILABLE);  //Adds follower to the active party
//this flag is important, it tells us valeria has indeed a nightmare
//and will be used in her dialogs(see below)
            WR_SetPlotFlag(PLT_VALERIA_FADE_NIGHTMARE,VALERIA_SLEEP,TRUE);
//which area to put valeria in. these module variables are set when 
//first entering the fade and are positive if a companion nightmare 
//area will have an actual companion in it (imagine going there with 
//only 3 in the party, one area should be inactive, the value -1
//so circle through all 3 and see which one is the first available
//the core game scripts would put the other companions already in
//one of these, so we just find any available.
//so if say Rory(Ser Gilmore) and Valeria are in party, these scripts
//are disjoint and they would both behave appropriately
//notice how we fill in the first available slot with the companion
//I use 50, you should use any other number above 50 to make sure
//the vanilla scripts don't think a vanilla companion is occupying 
//this nightmare area
            int fol1=GetLocalInt(GetModule(),CIR_FADE_FOLLOWER_1);
            int fol2=GetLocalInt(GetModule(),CIR_FADE_FOLLOWER_2);
            int fol3=GetLocalInt(GetModule(),CIR_FADE_FOLLOWER_3);
            if(fol1<1)
            {
                WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_HAS_NIGHTMARE_A,TRUE);
                SetLocalInt(GetModule(),CIR_FADE_FOLLOWER_1,50);  // REPLACE THE NUMBER "50" FOR YOUR OWN NUMBER
            }
            else if(fol2<1)
            {
                WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_HAS_NIGHTMARE_B,TRUE);
                SetLocalInt(GetModule(),CIR_FADE_FOLLOWER_2,50); // REPLACE THE NUMBER "50" FOR YOUR OWN NUMBER
            }
            else if(fol3<1)
            {
                WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_HAS_NIGHTMARE_C,TRUE);
                SetLocalInt(GetModule(),CIR_FADE_FOLLOWER_3,50); // REPLACE THE NUMBER "50" FOR YOUR OWN NUMBER
            }
        }
        WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY, FALSE);//not in party anymore anyways
    }
 
/* 
***************
NUMBERS IN USE:
***************
 
50 - IDOMENEAS' QUEST & LEGENDS
1140150041 - IMMORTALITY'S SER GILMORE NPC
 
*/
 
 
//fighting the demon in the fade
//after the PC makes it to the inner sanctum, they get to talk to the 
//demon and the battle begins. This is captured here. Notice that if
//we have talked to Valeria in the fade, she should be brought back 
//to help in battle.
    if  (WR_GetPlotFlag(PLT_CIR300PT_FADE,SLOTH_DEMON_ATTACKS) &&  !WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_SLOTH_DEMON_ATTACKS)  && WR_GetPlotFlag(PLT_VALERIA_FADE_NIGHTMARE,  VALERIA_TALKED_TO_IN_FADE))
    {
       WR_SetFollowerState(oVal, FOLLOWER_STATE_ACTIVE);
       SetImmortal(oVal,0);
       WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_SLOTH_DEMON_ATTACKS,TRUE);
       WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY,TRUE);
       SetLocalInt(oVal, CREATURE_REWARD_FLAGS, 0);  //Allows the follower to gain XP
       SetLocalInt(oVal, AMBIENT_SYSTEM_STATE, 0);
       SetObjectActive(oVal,1);
       AddCommand(oVal, CommandJumpToLocation(GetLocation(GetHero())));   //Ensures follower appears at PC's location.
    }
//demon in the fade defeated, this is captured here
//if valeria was with the PC when he first entered the fade
//she should come back to him regardless of him talking to her in the fade
    if  (WR_GetPlotFlag(PLT_CIR300PT_FADE,SLOTH_DEMON_DEFEATED) &&  !WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_SLOTH_DEMON_DEFEATED)  && WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_SLEEP))
    {
       WR_SetFollowerState(oVal, FOLLOWER_STATE_ACTIVE);
       WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY,TRUE);
       WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_SLOTH_DEMON_DEFEATED,TRUE);
       SetLocalInt(oVal, CREATURE_REWARD_FLAGS, 0);  //Allows the follower to gain XP
       SetLocalInt(oVal, AMBIENT_SYSTEM_STATE, 0);
       SetObjectActive(oVal,1);
       AddCommand(oVal, CommandJumpToLocation(GetLocation(GetHero())));   //Ensures follower appears at PC's location.
    }

The VALERIA_FADE_NIGHTMARE plot, it's journal and script

Another important point, is that the game uses another plot for each companion. So we need to create the plot VALERIA_FADE_NIGHTMARE that will contain similar variables to the corresponding game plot for a companion. Name them: VALERIA_DEMON_HOSTILE, VALERIA_DEMON_KILLED, VALERIA_REALISES_DEMON, VALERIA_RETURNS_TO_PARTY, VALERIA_SLEEP (these have to do with her behavior when we meet her in her nightmare), VALERIA_TALKED_TO_IN_FADE (if we went to Valeria's nightmare and talked her out of it) .

Here is now the script for the plot VALERIA_FADE_NIGHTMARE (in the object inspector for the plot just change the standard plot_core script to this one). I call it: valeria_fade_subplot_script.nss

#include "utility_h"
#include "wrappers_h"
#include "plot_h"
 
#include "cir_constants_h"
#include "cir_functions_h"
#include "plt_valeria_fade_nightmare"
//this will look weird to you, but it shouldn't really(we're in the
//script not the definitions of the flags). when you see below you'll 
//notice that certain flags from this plot are not set here, they are 
//handled by global_plot_operations 
//(just setting the flags to TRUE or FALSE nothing more)
 
#include "plt_valeria_npc_hire"
#include "plt_cir300pt_fade"
 
int StartingConditional()
{
    event eParms = GetCurrentEvent();                // Contains all input parameters
    int nType = GetEventType(eParms);               // GET or SET call
    string strPlot = GetEventString(eParms, 0);         // Plot GUID
    int nFlag = GetEventInteger(eParms, 1);          // The bit flag # being affected
    object oParty = GetEventCreator(eParms);      // The owner of the plot table for this script
    object oConversationOwner = GetEventObject(eParms, 0); // Owner on the conversation, if any
    int nResult = FALSE; // used to return value for DEFINED GET events
    object oPC = GetHero();
    object oTarg;
    object oVal = UT_GetNearestCreatureByTag(oPC,"valeria_npc");
 
    plot_GlobalPlotHandler(eParms); // any global plot operations, including debug info
 
    if(nType == EVENT_TYPE_SET_PLOT) // actions -> normal flags only
    {
        int  nValue = GetEventInteger(eParms, 2);        // On SET call, the value  about to be written (on a normal SET that should be '1', and on a  'clear' it should be '0')
        int  nOldValue = GetEventInteger(eParms, 3);     // On SET call, the current  flag value (can be either 1 or 0 regardless if it's a set or clear  event)
        // IMPORTANT: The flag value on a SET event is set only AFTER this script finishes running!
        switch(nFlag)
        {
            case VALERIA_DEMON_HOSTILE:
            {
//when we speak to her, we might manage to make her realize she's in a nightmare, or not,
//if she realizes she'll help battling the demon pretending to be her sister
                    if(WR_GetPlotFlag(PLT_VALERIA_FADE_NIGHTMARE, VALERIA_REALISES_DEMON) == TRUE)
                    { //Set to friendly
                        SetGroupId(oVal, GROUP_FRIENDLY);
                        WR_SetFollowerState(oVal, FOLLOWER_STATE_ACTIVE);
                    }
                    else
                    { //If Valeria hasn't realised it is a demon set to neutral
                        SetGroupId(oVal, GROUP_NEUTRAL);
                    }
                    //Make Victoria into Incubus
//in the nightmare area I've created, 19741 is Victoria, Valeria's dead sister,
// and 19742 (initially inactive) is the Demon holding her
                    UT_TeamAppears(19741, FALSE);
                    UT_TeamAppears(19742, TRUE);
                    UT_TeamGoesHostile(19742, TRUE);
                    break;
            }
            case VALERIA_RETURNS_TO_PARTY:
            {
                WR_SetObjectActive(oVal, FALSE, BASE_ANIMATION_SLEEP, FADE_VFX_TELEPORT);
                if(WR_GetPlotFlag(PLT_VALERIA_FADE_NIGHTMARE, VALERIA_REALISES_DEMON) == TRUE)
                { //Set to friendly
                    WR_SetFollowerState(oVal, FOLLOWER_STATE_UNAVAILABLE);
//                    WR_SetFollowerState(oVal, FOLLOWER_STATE_ACTIVE);
//                    WR_SetPlotFlag(PLT_VALERIA_FADE_NIGHTMARE,VALERIA_SLEEP,FALSE);
//                    WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY,TRUE);
                }
//the nightmare variables are set in the function check_plot_changed(see above), 
//what we are setting here, are the plot variables that will change the 
//actual area in the fade map to done. you can see those variables below
                if (WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_HAS_NIGHTMARE_A))
                {
                     WR_SetPlotFlag(PLT_CIR300PT_FADE_PORTAL, FADE_PORTAL_GREEN_1_COMPLETE, TRUE, TRUE);
                }
                else if (WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_HAS_NIGHTMARE_B))
                {
                     WR_SetPlotFlag(PLT_CIR300PT_FADE_PORTAL, FADE_PORTAL_GREEN_2_COMPLETE, TRUE, TRUE);
                }
                else if (WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_HAS_NIGHTMARE_C))
                {
                     WR_SetPlotFlag(PLT_CIR300PT_FADE_PORTAL, FADE_PORTAL_GREEN_3_COMPLETE, TRUE, TRUE);
                }
                break;
            }
        }
     }
     else // EVENT_TYPE_GET_PLOT -> defined conditions only
     {
 
        switch(nFlag)
        {
 
 
        }
 
    }
 
    plot_OutputDefinedFlag(eParms, nResult);
 
    return nResult;
}

Forgot(told ye I would). Here's how to add the right journal entries, and also, how to define the plot properties (see the object inspector on the right). Notice this plot has to be a subplot to cir300pt_fade so what we get the correct Journal behavior as well (like the game does). Notice when the quest is done and we set this as the final entry (Final=Yes).

Nightmare11.JPG

Nightmare12.JPG

Taking care of Valeria's dialog in her nightmare area

I am using a stage for this, with the appropriate options set in the dialog. I won't go into it. You can do that.

The flags in VALERIA_FADE_NIGHTMARE are important here. Notice the check for the SLEEP flag here. Also notice we only run this dialog once.

Nightmare1.JPG

Now you can do whatever you want, but I find this approach best. Notice the first of two options, checks to see if the PC has 3 ranks in persuasion. If that happens, Valeria snaps out of it right away.

Nightmare2.JPG

Now notice how the REALISES_DEMON variable is set, so that Valeria will help in the battle with the demon acting as her sister.

Nightmare3.JPG

Notice how we set the flag VALERIA_DEMON_HOSTILE in the plot valeria_fade_nightmare (see the script). It will initiate the battle.

Nightmare4.JPG

Notice that if she's a mage she snaps out of it (second way). Notice how this links back up so we set the flag VALERIA_DEMON_HOSTILE in the plot valeria_fade_nightmare again.

Nightmare5.JPG

Otherwise, you fight the demon alone. Notice how this links back so we set the flag VALERIA_DEMON_HOSTILE in the plot valeria_fade_nightmare again as we did above.

Nightmare6.JPG

Set the VALERIA_TALKED_TO_IN_THE_FADE flag (so she'll help in the final demon battle). We show this option only once per game.

Nightmare9.JPG

Set the VALERIA_RETURNS_TO_PARTY flag (removes Valeria from party). See the valeria_fade_nightmare plot script.

Nightmare10.JPG

Valeria Nightmare Area specifics

Create a duplicate copy of a game companion nightmare area resource. I call it valeria_fade_nightmare(.are). Mine is using the predefined area layout lak526d for this (similar to Morrigan's nightmare I think, I probably duplicated her nightmare area and edited it). You need to do this so you'll have the fade pedistal in the area and atmosphere sounds, setting etc. You can remove some of the waypoints etc and edit it to what you want the nightmare to be. That's up to you can't help you there. Make sure the entry waypoint is tagged "start".

Set the script of the area to "valeria_nightmare". It's a copy of the core game resource tailored to what we want to do. First here's the area script.

#include "utility_h"
#include "party_h"
#include "plt_valeria_fade_nightmare"
void main()
{
    event   evEvent         = GetCurrentEvent();            // Event
    int     nEventType      = GetEventType(evEvent);        // Event Type
    object  oEventCreator   = GetEventCreator(evEvent);     // Event Creator
    object  oPC             = GetHero();
    object  oParty          = GetParty( oPC );
    object  oThis           = OBJECT_SELF;
    int     bEventHandled   = FALSE;
    object oTarg, oFollower;
//I pass handling to the standard script first here for things like
// polymorphing handling (rat, golem etc), and standard behavior.
    HandleEvent( evEvent, R"cir000ar_fade_core.ncs" );
    switch ( nEventType )//we capture the following events though
    {
        case EVENT_TYPE_AREALOAD_PRELOADEXIT:
        {
// When: for things you want to happen while the load screen is
// still up, things like moving creatures around.
            int         nIndex;
            int         nArraySize, nSpiritArraySize;
            int         bArcaneFormActive;
            int         bBurningFormActive;
            int         bGolemFormActive;
            int         bMouseFormActive;
            int         bSpiritFormActive;
            int         bPCHasArcaneForm;
            int         bPCHasBurningForm;
            int         bPCHasGolemForm;
            int         bPCHasMouseForm;
            int         bPCHasSpiritForm;
            object []   arFireBases, arSpiritBases;
 
            nArraySize         = GetArraySize( arFireBases );
            // VALERIA'S NIGHTMARE
            if  ( GetTag(oThis) == "valeria_fade_nightmare" &&  WR_GetPlotFlag(PLT_VALERIA_FADE_NIGHTMARE,VALERIA_RETURNS_TO_PARTY) ==  FALSE )
            {
                oFollower = Party_GetFollowerByTag("valeria_npc");
                WR_SetObjectActive(oFollower, TRUE);
                UT_LocalJump(oFollower,"valeria_spot");
//this is the waypoint Valeria appears in the area, so move her there(see pictures)
            }
            break;
        }
 
        case EVENT_TYPE_TEAM_DESTROYED:
        {
            int nTeamID = GetEventInteger(evEvent, 0); // Team ID
            if(nTeamID==19742)
//capture when the demon dies and talk to Valeria
            {
                WR_SetPlotFlag(PLT_VALERIA_FADE_NIGHTMARE,VALERIA_DEMON_KILLED,TRUE);
                oFollower = Party_GetFollowerByTag("valeria_npc");
                UT_Talk(oFollower,oPC);
            }
 
            break;
        }
 
 
    }
 
}

My setup in the area. I don't know if you can see the inactive (red box) and active (yellow) Victoria versions.

Nightmare7.JPG

The pedestal.

Nightmare8.JPG

So you think you're done, think again. Changes in Event Handling

We finally need to capture when the PC selects the right nightmare area (the one area that contains Valeria, it will be loaded appropriately)

Add this in the Event Handling part of the module event script:

        case EVENT_TYPE_BEGIN_TRAVEL://EVENT_TYPE_WORLD_MAP_USED:
        {
            string sSource = GetEventString(ev, 0); // the area tag the player is travelling from
            string sTarget = GetEventString(ev, 1); // the area tag the player is travelling to
            string sWPOverride = GetEventString(ev, 2);
//we pick to sent the PC clicking to the right nightmare area here
//it's funny actually, all tags for the core game nightmare companion
//map pins are given the same value "Fade_Follower". What distinguishes
//them is sWPOverride(thank god for that otherwise we'd be in trouble)
//if I remember correctly, the companion nightmare areas in the fade 
//map, go 1,2,3 counterclockwise starting from the one on the left
//valeria_fade_nightmare is the name of the area for Valeria's 
//nightmare, start is the waypoint tag
            if(sWPOverride=="1" && sTarget=="Fade_Follower" && WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_HAS_NIGHTMARE_A))
                DoAreaTransition("valeria_fade_nightmare","start");
 
            if(sWPOverride=="2" && sTarget=="Fade_Follower" && WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_HAS_NIGHTMARE_B))
                DoAreaTransition("valeria_fade_nightmare","start");
 
            if(sWPOverride=="3" && sTarget=="Fade_Follower" && WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_HAS_NIGHTMARE_C))
                DoAreaTransition("valeria_fade_nightmare","start");
 
            break;
        }

Phew, I hope, I didn't forget anything.

Active Party Compatible Companion Mod Creation Joining The Party