Difference between revisions of "On NPC Mod Creation and Compatibility, while being the least intrusive to core game resources"

From Dragon Age Toolset Wiki
Jump to: navigation, search
(Understanding the game mechanics and the module event script)
m (Redirecting to new version)
 
(137 intermediate revisions by 3 users not shown)
Line 1: Line 1:
UNDER CONSTRUCTION
+
#REDIRECT [[Compatible_Companion_Mod_Creation]]
 
+
Created by Idomeneas
+
 
+
Please do not edit this, unless you found a mistake. In that case inform me first.
+
 
+
The purpose of this wiki is to help the community tackle one of the most important problems we face: compatibility between all our mods, in particular those mods that introduce NPC followers.
+
 
+
I'll share with you my approach to handling companions, based on what I have done with my mod, [http://social.bioware.com/project/3152/ Valeria Addon], my experiments and experience. My goal has always been to be the least intrusive to the game core resources (meaning don't change them at all), while having the companion behave appropriately in most parts of the game.
+
 
+
What I'll talk about here works for Patch 1.02. If you got Awakening or Patch 1.04 you might see weird behavior and this approach might have to be altered.
+
 
+
Everything you see below is done WITHOUT changing game resources. Let us begin.
+
 
+
=Understanding the game mechanics and the module event script=
+
 
+
We first need to discuss how the game makes use of scripts, and in particular our module event scripts (every mod has one). Everytime something happens in the game, e.g., you open or close the inventory, talents, skills, or world map gui, all the module event scripts you have installed are fire and processed (probably in the order the mods where installed) as well as the core game scripts (typically these are processed first).
+
 
+
So if we want to capture the behavior of our NPC follower or a major event of the game it can be done here, e.g., the PC selected or dropped the follower in the partypicker, you begin travel, or the PC just finished talking to everyone at the gates in the climax, or the game is over and we're at the slideshow.
+
 
+
This game structure is indeed brilliant since it allows the main game and all mods installed to handle and process the same event, say EVENT_TYPE_BEGIN_TRAVEL or EVENT_TYPE_MODULE_HANDLE_GIFT or EVENT_TYPE_WORLD_MAP_CLOSED and so forth.
+
 
+
=What we can't and can do=
+
 
+
The only major resource we have to change is the partypicker, otherwise our NPC's won't show up. There is a way around this(item 4 below).
+
 
+
If you want your NPC to interject in the middle of a major dialog, that dialog resource has to be changed (I can't see any way around this one).
+
 
+
The thing to keep in mind is that everytime something happens in the game, certain flags from core game resources are set. We can capture when these are set and continue the process appropriately.
+
 
+
In this way, I'll show you how '''we can do''' the following (I've done these for my mod Valeria, WITHOUT changing any core game resources, I'll use her as a reference):
+
 
+
1) Valeria banters with the party randomly WITHOUT changing any original game scripts.
+
 
+
2) Valeria accepts specific gifts WITHOUT changing the game scripts.
+
 
+
3) Valeria is in a nightmare area WITHOUT changing the game scripts.
+
 
+
4) Valeria has a dialog option to eliminate problems with partypicker compatibility. If she doesn't show up in the partypicker she can still be picked up or sent back to camp.
+
 
+
5) Valeria has her own dialog after major cutscenes (where appropriate) of the game WITHOUT changing resources of the game.
+
 
+
6) Valeria appears in all camps (basic camp, Arl Eamon's estates, post coronation), most cutscenes from the original game and at the city gates WITHOUT changing resources of the game.
+
 
+
7) Valeria behaves appropriately if the PC gets captured WITHOUT changing resources of the game.
+
 
+
8) Slideshow post coronation WITHOUT changing the resources of the game.
+
 
+
=What do we need?=
+
 
+
Well, basic knowledge of the toolset and scripting.
+
 
+
a) The NPC mod event handler, say "valeria_event_handler"
+
 
+
b) A plot to hold all our NPC flags, call it "valeria_npc_hire". In the handler we should have the include:
+
 
+
'''#include "plt_valeria_npc_hire"'''
+
 
+
c) We need the following basic FLAGS: PARTY_VALERIA_IN_PARTY, PARTY_VALERIA_JOINED, VALERIA_CHECK_RUNIT
+
 
+
These are there to handle Valeria(is she in party?, has she joined? do we need to run the script? it has to be done if we're in a camp site. you'll see what script)
+
 
+
d) Other flags to set when needed: VALERIA_WYNNE_KILLED, VALERIA_RESPONSE_TO_BAD, VALERIA_ENTER_FADE, VALERIA_HAS_NIGHTMARE_A, VALERIA_HAS_NIGHTMARE_B, VALERIA_HAS_NIGHTMARE_C, VALERIA_SLOTH_DEMON_ATTACKS, VALERIA_SLOTH_DEMON_DEFEATED, VALERIA_WYNNE_KILLED_AT_CULLEN, VALERIA_DEN_CAPTURED_PC_CAPTURED, VALERIA_CLI_MAIN_PC_FINISHED_SETTING_FINAL_PARTY, VALERIA_EPI_JUMP_TO_SLIDE_SHOW, VALERIA_FIGHTING_ARCHDEMON
+
 
+
If you drop the valeria_ prefix (FOR YOUR MOD USE A DIFFERENT PREFIX EVERYWHERE) you'll recognize some flags from the original game. Everytime those are set, we'll need to set a corresponding flag in our NPC plot, so we don't run things twice.
+
 
+
I'll tell you which plots from the main game we need depending on what we're trying to do.
+
 
+
=Basic structure of the module event handler=
+
 
+
//core includes here
+
 
+
//core plot includes
+
 
+
//valeria function, plots etc, includes
+
 
+
'''#include "plt_valeria_npc_hire"'''
+
 
+
//constant definitions here
+
 
+
//function definitions here
+
 
+
(all this part 1)
+
 
+
void main()
+
{
+
    object oPC = GetHero(),me=OBJECT_SELF,ohire;
+
    event ev = GetCurrentEvent();
+
    int nEventType = GetEventType(ev);
+
    //function calls here, other important checks (part 2)
+
    //capture events
+
    switch(nEventType)
+
    {
+
    .....(part 3)
+
    }
+
}
+
 
+
//functions here (part 4)
+
 
+
For what follows, I'll break the event script into four parts for quick reference: Includes, (Main) Body, (Event) Handling, Functions
+
 
+
=Capturing Main Campaign Event/Flag Changes, the function check_plot_changed()=
+
 
+
The next script can be placed in the event handler, and allows us to capture and process main plot changes.
+
 
+
I'll do this structure a first time here
+
 
+
In 'includes' add:
+
 
+
void check_plot_changed(object oVal);
+
 
+
In 'body' add:
+
 
+
...
+
object oVal=UT_GetNearestObjectByTag(oPC,"valeria_npc");
+
...
+
 
+
check_plot_changed(oVal);
+
...
+
 
+
In 'functions' add:
+
 
+
//check if plot flags changed and act appropriately
+
 
+
void check_plot_changed(object oVal)
+
{
+
    object oPC=GetHero();//if NPC has not joined return
+
    if (!WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_JOINED))
+
        return;
+
 
+
//here depending on which core flag changed, act appropriately
+
.....
+
 
+
}
+
 
+
We will be changing this function appropriately below, depending on what we wish to do.
+
 
+
=Random banters=
+
 
+
==What we need==
+
A dialog resource, say "valeria_random_banter.dlg", a script "valeria_random_banters.nss" and a plot "valeria_random_banters"
+
 
+
You will notice that they are done in order of appearance in the .dlg file. Of course one can change that. Meaning if Alistair is in, all his banters will go through first, then Moriggan, then Leliana ,etc, the way I had them in my file. To change that you can add a random chance to see if you initiate dialog with the party member.(see the script below, it is trivial)
+
 
+
==valeria_random_banters.nss==
+
First create this script. You can see what flags you need in plt_valeria_random_banters below (basically a flag for each companion you want Valeria to banter with). Alternatively, you can use the core plots to check who is in the party, but I find this more efficient, since we got full control over the script and plot resource.
+
 
+
//file valeria_random_banters.nss
+
 
+
'''#include "wrappers_h"'''
+
 
+
'''#include "plt_valeria_random_banter"'''
+
 
+
void  main()
+
{
+
    WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, ALISTAIR_IN_PARTY, FALSE);
+
    WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, MORRIGAN_IN_PARTY, FALSE);
+
    WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, LELIANA_IN_PARTY, FALSE);
+
    WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, WYNNE_IN_PARTY, FALSE);
+
    WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, ZEVRAN_IN_PARTY, FALSE);
+
    WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, DOG_IN_PARTY, FALSE);
+
    object [] arParty = GetPartyList(GetHero());
+
    int i;
+
    int nSize = GetArraySize(arParty);
+
    int which=Random(nSize)+1;
+
    if(GetTag(arParty[which]) == "gen00fl_alistair")
+
        WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, ALISTAIR_IN_PARTY, TRUE);
+
    if(GetTag(arParty[which]) == "gen00fl_morrigan")
+
        WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, MORRIGAN_IN_PARTY, TRUE);
+
    if(GetTag(arParty[which]) == "gen00fl_leliana")
+
        WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, LELIANA_IN_PARTY, TRUE);
+
    if(GetTag(arParty[which]) == "gen00fl_wynne")
+
        WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, WYNNE_IN_PARTY, TRUE);
+
    if(GetTag(arParty[which]) == "gen00fl_zevran")
+
        WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, ZEVRAN_IN_PARTY, TRUE);
+
    if(GetTag(arParty[which]) == "gen00fl_dog")
+
        WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, DOG_IN_PARTY, TRUE);
+
}
+
 
+
 
+
//to add a random chance for the party member to banter with Valeria you can simply change
+
...
+
    if(GetTag(arParty[which]) == "gen00fl_dog" && Random(10)==1)
+
        WR_SetPlotFlag(PLT_VALERIA_RANDOM_BANTER, DOG_IN_PARTY, TRUE);
+
...
+
 
+
This way it won't circle through all the banters of Alistair, then Morrigan's etc, but it will skip them with some probability (90%).
+
 
+
==Changes in valeria_event_handler==
+
I'll do this structure again(last time), the rest will be the same:
+
 
+
In 'includes' add:
+
 
+
void valeria_banter();
+
 
+
In 'body' add:
+
 
+
//valeria banter: run this only if valeria is in active party
+
    if (WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY))
+
        valeria_banter();
+
 
+
In 'functions' add:
+
 
+
void valeria_banter()
+
{
+
    if(Random(400)!=1)//1 in 400 chance to start banter
+
        return;
+
    if(GetCombatState(GetHero()))//don't start if in combat
+
        return;
+
    object [] arParty = GetPartyList(GetHero());
+
    int i;
+
    int nSize = GetArraySize(arParty);
+
    if(nSize<=2)//if only valeria and the PC no banter
+
        return;
+
    for(i=0;i<nSize;i++)
+
        if(!ClearAmbientDialogs(arParty[i]))
+
            return;
+
    object oval=UT_GetNearestObjectByTag(GetHero(),"valeria_npc");
+
    UT_Talk(oval,GetHero(),R"valeria_random_banter.dlg");
+
}
+
 
+
==Structure of the file valeria_random_banter.dlg==
+
Everything should be set to AMBIENT. First notice where we call the random_banter_script.
+
 
+
[[image:Banters1.JPG]]
+
 
+
Now, notice how we structure all the npc's we want Valeria to banter with. First is Alistair, if he's in the party, and so forth.
+
 
+
[[image:Banters2.JPG]]
+
 
+
Second is Morrigan and so forth.
+
 
+
[[image:Banters5.JPG]]
+
 
+
Notice that once a banter starts, it will never be shown again.
+
 
+
[[image:Banters3.JPG]]
+
 
+
An example with 3 NPC's (this is a dog banter with Valeria, and Zevran, if he's in the party(notice the condition C, using the valeria_random_banters flag) he joins in).
+
 
+
[[image:Banters4.JPG]]
+
 
+
=End game Slideshow=
+
 
+
==What we need==
+
The core plot file epipt_main, contains the variable EPI_JUMP_TO_SLIDE_SHOW, which is set once you talk to the guard post-coronation at the doors and the end slideshow begins.
+
 
+
Add the variable VALERIA_EPI_JUMP_TO_SLIDE_SHOW in the companion VALERIA_NPC_HIRE plot, so we only do this once. We also need the dlg file with the slide dialog and pretty pictures. I use my main dialog file for this, you don't have to: valeria_npc.dlg
+
 
+
To implement:
+
 
+
In includes, add:
+
 
+
'''#include "plt_epipt_main"'''
+
 
+
In the function check_plot_changed() add:
+
 
+
...
+
//valeria in the epilogue
+
    if (WR_GetPlotFlag(PLT_EPIPT_MAIN,EPI_JUMP_TO_SLIDE_SHOW) && !WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_EPI_JUMP_TO_SLIDE_SHOW))
+
    {
+
        WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_EPI_JUMP_TO_SLIDE_SHOW,TRUE);
+
        BeginSlideshow(R"valeria_npc.dlg");
+
    }
+
...
+
 
+
 
+
==Structure of the file valeria_npc.dlg==
+
 
+
Note the check to see if the end game slides have run. I'm assuming the game ends after, so this runs only once and it's done. If another mod has the same setup (with different names for plots, flags etc) that mod script should run and continue the slideshow until the bioware credits run.
+
 
+
[[image:Slideshow1.JPG]]
+
 
+
Now display the appropriate text and picture (it is set on the slideshow tab) depending on which flags from the companion mod are true (depending on whether the PC helped the companion and so forth)
+
 
+
[[image:Slideshow2.JPG]]
+
 
+
=Valeria at the gates in the climax=
+
In the climax, you get a last chance to switch your party, where everyone parades in front of you. Well... the right behavior is to first talk to Riordan, he opens the partypicker, you may choose whom you want and then the parade begins.
+
 
+
Once you close the partypicker, Valeria will speak before/or after everyone else is done, so it runs smoothly. Now since many mods might skrew up the partypicker (by not including certain chars), you might have Valeria not showing up. At this stage you won't be allowed to use the dialog option anymore, to get her to join.
+
 
+
You could be intrusive here and force her in the partypicker, but if everyone does it we're in trouble. So I don't reccommend it and I didn't implement it this way. It is best to handle partypicker compatibility by including all waypoints for all NPC's in the char_stage.
+
 
+
==What we need==
+
 
+
The plot clipt_main contains the important flag CLI_MAIN_PC_FINISHED_SETTING_FINAL_PARTY, and as it says, it becomes true when the PC is done setting the final party. So, create in the VALERIA_NPC_HIRE plot the flag VALERIA_CLI_MAIN_PC_FINISHED_SETTING_FINAL_PARTY, so we do this only once.
+
 
+
To implement:
+
 
+
In includes, add:
+
 
+
'''#include "plt_clipt_main"'''
+
 
+
In the function check_plot_changed() add:
+
 
+
 
+
//valeria at the gates
+
    if (WR_GetPlotFlag(PLT_CLIPT_MAIN,CLI_MAIN_PC_FINISHED_SETTING_FINAL_PARTY) && !WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_CLI_MAIN_PC_FINISHED_SETTING_FINAL_PARTY))
+
    {
+
//this bolded part takes care of her, regardless of whether you added
+
//her to the party. this way she will show up when your companions
+
//defend the gate, choose another location of course
+
'''      location ValeriaLoc;'''
+
      ''' object curArea=GetArea(oPC);'''
+
        '''ValeriaLoc=Location(curArea, Vector(123.24, 717.48, 0.0), -64.92);'''
+
        '''AddCommand(oVal, CommandJumpToLocation(ValeriaLoc));'''       
+
WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_CLI_MAIN_PC_FINISHED_SETTING_FINAL_PARTY,TRUE);
+
        if(WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY))
+
        {
+
//this flag is important for the slideshow at the end
+
            WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_FIGHTING_ARCHDEMON,TRUE);
+
            WR_SetFollowerState(oVal, FOLLOWER_STATE_ACTIVE);
+
            SetLocalInt(oVal, CREATURE_REWARD_FLAGS, 0);  //Allows the follower to gain XP
+
        }
+
        if(!WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY))
+
        {
+
            WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_FIGHTING_ARCHDEMON,FALSE);
+
            WR_SetFollowerState(oVal, FOLLOWER_STATE_AVAILABLE);
+
        }
+
        SetObjectActive(oVal,1);
+
        UT_Talk(oVal,oPC);
+
    }
+
 
+
==Changing the Valeria dialog appropriately: valeria_npc.dlg==
+
Use the flag here, and depending on whether the PC added Valeria to the party, follow a different path.
+
 
+
[[image:Gates1.JPG]]
+
 
+
She complains if she wasn't selected to fight the archdemon, and she has every right.
+
[[image:Gates2.JPG]]
+
 
+
=Valeria initiates dialog at any point we feel like. Example: Killing Wynne(NOOO!!!)=
+
 
+
I love this char, no-one should ever kill her, but if they do the char should lose approval unless it is that b... Morrigan, or Zevran etc.
+
 
+
==What we need==
+
 
+
Wynne could die in two places in the Circle: when you first pick her up or at Cullen.
+
 
+
The plot CIR000PT_MAIN contains the two important flags for this: WYNNE_KILLED and WYNNE_KILLED_AT_CULLEN. So, create in the VALERIA_NPC_HIRE plot the flags VALERIA_WYNNE_KILLED and VALERIA_WYNNE_KILLED_AT_CULLEN, so we do this only once.
+
 
+
To implement:
+
 
+
In includes, add:
+
 
+
'''#include "plt_cir000pt_main"'''
+
 
+
In the function check_plot_changed() add:
+
 
+
    //fighting wynne
+
    if (WR_GetPlotFlag(PLT_CIR000PT_MAIN,WYNNE_KILLED) && !WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_WYNNE_KILLED))
+
    {
+
        if(WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY))
+
        {
+
          WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_WYNNE_KILLED,TRUE);
+
            WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_RESPONSE_TO_BAD,TRUE);
+
            AdjustFollowerApproval(oVal,-10,TRUE);
+
            UT_Talk(oVal,oPC);
+
            valeria_approval();
+
        }
+
    }
+
    //fighting wynne
+
    if (WR_GetPlotFlag(PLT_CIR000PT_MAIN,WYNNE_KILLED_AT_CULLEN) && !WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_WYNNE_KILLED_AT_CULLEN))
+
    {
+
        if(WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY))
+
        {
+
            WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_WYNNE_KILLED_AT_CULLEN,TRUE);
+
            WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_RESPONSE_TO_BAD,TRUE);
+
            AdjustFollowerApproval(oVal,-10,TRUE);
+
            UT_Talk(oVal,oPC);
+
            valeria_approval();
+
        }
+
    }
+
 
+
 
+
The flag: VALERIA_RESPONSE_TO_BAD can be used in valeria_npc.dlg as shown below. The function valeria_approval() is explained in the gift handling section.
+
 
+
==Changes in valeria_npc.dlg==
+
Use the flag VALERIA_RESPONSE_TO_BAD, first to enter the bad reaction node in the dialog.
+
[[image:wynne1.JPG]]
+
Reset the flag
+
[[image:wynne2.JPG]]
+
Depending on which situation we're in display the appropriate node (once per game). Showing what happens when Wynne is killed when first meeting her. But you should never do that...
+
[[image:wynne3.JPG]]
+
 
+
=Valeria's behavior when the PC is captured(while saving Anora)=
+
While saving Anora, you may end up fighting Sir Cauthrien. If you lose you get captured and sent to prison. Valeria must be removed from the party otherwise she'll still be following the PC around. She cannot be chosen to go save the PC without changing the game resources, unless you capture it after the initial selection is done. Haven't done it but I'm guessing it could be done if you find the right flag.
+
 
+
==What we need==
+
 
+
The plot DENPT_CAPTURED contains the important flag for this: DEN_CAPTURED_PC_CAPTURED. So, create in the VALERIA_NPC_HIRE plot the flags VALERIA_DEN_CAPTURED_PC_CAPTURED, so we do this only once.
+
 
+
To implement:
+
 
+
In includes, add:
+
 
+
'''#include "plt_denpt_captured"'''
+
 
+
In the function check_plot_changed() add:
+
 
+
//pc captured by cauthrien
+
    if (WR_GetPlotFlag(PLT_DENPT_CAPTURED,DEN_CAPTURED_PC_CAPTURED) && !WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_DEN_CAPTURED_PC_CAPTURED))
+
    {
+
        WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE,VALERIA_DEN_CAPTURED_PC_CAPTURED,TRUE);
+
        if(WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY))
+
        {
+
            WR_SetFollowerState(oVal, FOLLOWER_STATE_AVAILABLE);
+
        }
+
        WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY, FALSE);
+
    }
+
 
+
=Valeria's behavior at camps=
+
 
+
=Valeria has a dialog choice to remove or add her to the active party=
+
 
+
</rant on>
+
The partypicker is the only resource what you really have to change in order to make your companion show up. No way around this. However, as more mods come out, disregarding what others have done, and changing game resources at will and without knowing what they're doing very often, installing more than one mod messes the whole game up.
+
</rant off>
+
 
+
With that pleasant thought in mind, here's is an option to make all our companions "compatible", in the sense that even if they don't show in the partypicker, you can still get them, or sent them back to camp.
+
 
+
==What we need==
+
 
+
In your npc dialog, say valeria_npc.dlg, we build an option as you see in the pretty pictures. You first notice that this option is available only if we haven't been through the last partypicker choosing  at the gates in the climax (when we talk to Riordan).
+
 
+
[[image:camp1.JPG]]
+
 
+
Notice how we call the script valeria_come_with_me, to have her join the active party.
+
[[image:camp2.JPG]]
+
Notice how we call the script valeria_go_back_to_camp, to sent her back to camp(remove from active party).
+
 
+
[[image:camp3.JPG]]
+
 
+
==The come hither script==
+
 
+
'''#include "utility_h"'''
+
 
+
'''#include "plt_valeria_npc_hire"'''
+
 
+
void main()
+
{
+
    object me=OBJECT_SELF;
+
    SetImmortal(me,0);
+
    SetLocalInt(me, CREATURE_REWARD_FLAGS, 0);
+
    SetLocalInt(me, AMBIENT_SYSTEM_STATE, 0);
+
    SetFollowerState(me, FOLLOWER_STATE_ACTIVE);
+
    SetObjectActive(me,1);
+
    AddCommand(me, CommandJumpToLocation(GetLocation(GetHero())));  //Ensures follower appears at PC's location.
+
 
+
    WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY, TRUE);
+
 
+
}
+
 
+
==The go back to camp script==
+
 
+
 
+
'''#include "utility_h"'''
+
 
+
'''#include "sys_ambient_h"'''
+
 
+
'''#include "plt_valeria_npc_hire"'''
+
 
+
void main()
+
{
+
    object me=OBJECT_SELF;
+
    object curArea=GetArea(GetHero());
+
    SetFollowerState(me, FOLLOWER_STATE_AVAILABLE);
+
    WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_IN_PARTY,FALSE);
+
    if(GetTag(curArea)=="cam100ar_camp_plains" || GetTag(curArea)=="den211ar_arl_eamon_estate_1" ||GetTag(curArea)=="cli300ar_redcliffe_castle")
+
    {
+
        location ValeriaLoc;
+
        if(GetTag(curArea)=="cam100ar_camp_plains")
+
            ValeriaLoc=Location(curArea, Vector(135.85, 123.18, -0.08), -71.33);
+
        if(GetTag(curArea)=="den211ar_arl_eamon_estate_1")
+
            ValeriaLoc=Location(curArea, Vector(28.1, 4.75, 0.0), 176.33);
+
        if(GetTag(curArea)=="cli300ar_redcliffe_castle")
+
            ValeriaLoc=Location(curArea, Vector(2.56,-14.84, 0.0), -130.38);
+
        SetObjectActive(me,1);
+
        AddCommand(me, CommandJumpToLocation(ValeriaLoc));
+
        if(GetTag(curArea)=="cam100ar_camp_plains")
+
            Ambient_Start(me, AMBIENT_SYSTEM_ENABLED, AMBIENT_MOVE_NONE, AMBIENT_MOVE_PREFIX_NONE, 70, AMBIENT_ANIM_FREQ_ORDERED);
+
        if(GetTag(curArea)=="den211ar_arl_eamon_estate_1" ||GetTag(curArea)=="cli300ar_redcliffe_castle")
+
            Ambient_Start(me, AMBIENT_SYSTEM_ENABLED, AMBIENT_MOVE_NONE, AMBIENT_MOVE_PREFIX_NONE, 56, AMBIENT_ANIM_FREQ_ORDERED);
+
    }
+
    else
+
    {
+
        SetObjectActive(me,0);
+
    }
+
        SetImmortal(me,1);
+
 
+
}
+
 
+
=Valeria has her own nightmare in the fade/Behavior in the Fade=
+
 
+
=Valeria first joining the party (This works for ANY follower you want to have join)=
+
 
+
In my mod, valeria is allowed to have a class chosen the first time you meet her. So create in VALERIA_NPC_HIRE the flags ISMAGE,ISROGUE,ISWARRIOR and ISROGUEWARRIOR. One of these is set to TRUE through dialog. At the end of the dialog this script is called.
+
 
+
==The joining script==
+
 
+
Create the following script. I call it valeria_valjoin.nss
+
This script contains EVERYTHING you need to properly hire a follower without leveling him/her automatically.
+
 
+
Valeria in my mod is allowed to choose her class to Warrior, Rogue, Mage or Rogue-Warrior.
+
 
+
'''#include "sys_chargen_h"'''
+
 
+
'''#include "sys_rewards_h"'''
+
 
+
'''#include "plt_valeria_npc_hire"'''
+
 
+
void basic_blank_char(object oChar);//this function resets an NPC
+
 
+
void main()
+
{
+
 
+
object oPC=GetHero(), me=OBJECT_SELF;
+
WR_SetPlotFlag(PLT_VALERIA_NPC_HIRE, PARTY_VALERIA_JOINED, TRUE);
+
 
+
basic_blank_char(me);
+
//select race, otherwise skills tree wont show
+
 
+
Chargen_SelectRace(me,RACE_HUMAN);
+
int nClass;
+
//remove the rogue defaults, valeria's char is rogue when created
+
 
+
CharGen_ClearAbilityList(me,1);//remove talents
+
//CharGen_ClearAbilityList(me,2);//remove spells
+
//CharGen_ClearAbilityList(me,3);//remove skills
+
 
+
RemoveAbility(me, ABILITY_TALENT_DIRTY_FIGHTING);
+
RemoveAbility(me, ABILITY_SKILL_POISON_1);
+
//depending on which flags you set in the dialog that lead to this script, choose the appropriate class
+
 
+
if(WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,ISMAGE)==TRUE)
+
{
+
    nClass=CLASS_WIZARD;
+
    _AddAbility(me, ABILITY_SPELL_ARCANE_BOLT);
+
}
+
 
+
if(WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,ISROGUE)==TRUE)
+
{
+
    nClass=CLASS_ROGUE;
+
    _AddAbility(me, ABILITY_TALENT_DIRTY_FIGHTING);
+
    _AddAbility(me, ABILITY_SKILL_POISON_1);
+
}
+
 
+
if(WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,ISWARRIOR)==TRUE)
+
{
+
    nClass=CLASS_WARRIOR;
+
    _AddAbility(me, ABILITY_TALENT_POWERFUL);
+
}
+
 
+
if(WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,ISROGUEWARRIOR)==TRUE)
+
{nClass=CLASS_ROGUE;}
+
 
+
//apply attributes, abilities and stats for the core class
+
//SetCreatureProperty(me,PROPERTY_SIMPLE_CURRENT_CLASS, n1-3.0,PROPERTY_VALUE_BASE);
+
 
+
Chargen_SelectCoreClass(me,nClass);
+
//add mage spells
+
//_AddAbility(me, ABILITY_TALENT_HIDDEN_ASSASSIN);//this works if it's available on the core class
+
 
+
if(WR_GetPlotFlag(PLT_VALERIA_NPC_HIRE,ISROGUEWARRIOR)==TRUE)
+
{
+
    _AddAbility(me, ABILITY_TALENT_HIDDEN_WARRIOR);
+
    _AddAbility(me, ABILITY_TALENT_DIRTY_FIGHTING);
+
    _AddAbility(me, ABILITY_SKILL_POISON_1);
+
}
+
//tactics
+
 
+
Chargen_SetNumTactics(me);
+
Chargen_EnableTacticsPresets(me);
+
//SetFollowPartyLeader(me, TRUE);
+
//level needed for scaling
+
 
+
int nPackage = GetPackage(me);
+
int nTargetLevel;
+
int nPlayerLevel = GetLevel(oPC);
+
if(nPlayerLevel >= 13 || nPlayerLevel == 1 )
+
  nTargetLevel = nPlayerLevel;
+
else
+
  nTargetLevel = nPlayerLevel + 1;
+
int nMinLevel = GetM2DAInt(TABLE_PACKAGES, "MinLevel", nPackage);
+
if(nMinLevel > 0 && nMinLevel > nTargetLevel)
+
  nTargetLevel = nMinLevel;
+
//xp until hero level
+
 
+
int nXp = RW_GetXPNeededForLevel(Max(nTargetLevel, 1));
+
RewardXP(me, nXp, TRUE, FALSE);
+
 
+
//add specialization
+
 
+
float count=1.;
+
if(GetLevel(GetHero())>=7)
+
{
+
    SetCreatureProperty(me, 38, count);  // 38 is the spec point ID
+
    count=count+1.;
+
}
+
if(GetLevel(GetHero())>=14)
+
    SetCreatureProperty(me, 38, count);  // 38 is the spec point ID
+
 
+
//make available in party picker
+
 
+
SetFollowerState(me, FOLLOWER_STATE_ACTIVE);
+
SetFollowerState(me, FOLLOWER_STATE_AVAILABLE);
+
//dont fire player_core scaling
+
 
+
SetLocalInt(me, FOLLOWER_SCALED, 1);
+
SetLocalInt(me, AI_FLAG_STATIONARY, 0);
+
SetLocalInt(me, AMBIENT_SYSTEM_STATE, 0);
+
SetLocalInt(me, CREATURE_REWARD_FLAGS, 0);
+
//change script to player and send hire event
+
 
+
SetEventScript(me, RESOURCE_SCRIPT_PLAYER_CORE);
+
InitHeartbeat(me, CONFIG_CONSTANT_HEARTBEAT_RATE);
+
SendPartyMemberHiredEvent(me, FALSE);
+
SetFollowerApprovalEnabled(me,TRUE);
+
//open up party picker
+
 
+
SetPartyPickerGUIStatus(2);
+
ShowPartyPickerGUI();  //to allow Valeria to join in the active party
+
}
+
 
+
 
+
void basic_blank_char(object oChar)
+
{
+
// Initialize all creature properties to default value
+
 
+
    SetCreatureProperty(oChar,PROPERTY_SIMPLE_LEVEL,1.0f,PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar,PROPERTY_SIMPLE_EXPERIENCE,0.0f,PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_STRENGTH,      CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_DEXTERITY,    CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_CONSTITUTION,  CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_WILLPOWER,    CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_INTELLIGENCE,  CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_MAGIC,        CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_ATTACK_SPEED_MODIFIER,    1.0f);
+
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_DAMAGE_SCALE,      1.0f);
+
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_RESISTANCE_MENTAL,        0.0f);
+
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_RESISTANCE_PHYSICAL,      0.0f);
+
    SetCreatureProperty(oChar,51,      1.0f);
+
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_HEALTH_COMBAT,  REGENERATION_HEALTH_COMBAT_DEFAULT);
+
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA_COMBAT, REGENERATION_STAMINA_COMBAT_DEFAULT);
+
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_HEALTH,REGENERATION_HEALTH_EXPLORE_DEFAULT, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA, REGENERATION_STAMINA_EXPLORE_DEFAULT, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_MISSILE_SHIELD,0.0);
+
    SetCreatureProperty(oChar, 38,0.0);
+
    SetCreatureProperty(oChar, PROPERTY_DEPLETABLE_HEALTH ,          1.0f, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar, PROPERTY_DEPLETABLE_MANA_STAMINA ,    0.0f, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_DEFENSE ,          0.0f, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_ATTACK,            0.0f, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_DAMAGE_BONUS,      0.0f, PROPERTY_VALUE_BASE);
+
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_FLANKING_ANGLE,  60.0f, PROPERTY_VALUE_BASE);
+
 
+
}
+
 
+
=Valeria Gift Handling=
+

Latest revision as of 15:45, 10 July 2011