A great place to get started learning how to script with NWN2 is http://www.nwn2scripting.com. He's got some great tutorials on getting started, including a "first program" article and some general tips on using the Toolset.
Jassper posted a great list of resourced for those looking to get more familiar with scripting:
Noobs Corner is laid out to help the newbe scripter. Also, here are some links from nwn1 scripting/toolset which can relate to nwn2:
Check out the full thread where he posted these:
http://nwn2forums.bioware.com/forums/viewtopic.html?topic=521085&forum=1...
Thanks also to Grumpy Badger who recently let me know about this stickied thread on the NWN2 forums:
Helpful Scripts - http://nwn2forums.bioware.com/forums/viewtopic.html?topic=573084&forum=1...
KublaKhan1797 asked a question about variable scope on the official boards, and ScarfaceDM gave a nice summary:
A standard variable defined outside (and above) any function or void main will be global within that script, a standard variable defined within a function or void main will not be global within that script, it will be usable within the function or void main that it was defined in.
A local variable is stored on an object and is retrievable from any script as long as it exists, they are not persistent unless stored on items in a PCs inventory and then the PCs character exported.
A global variable is not persistent in any way, it is simply 'global', it is not stored on any object thus making it retrievable via its sVarName and not from an object.
A campaign variable is stored in the database folder in your NWN2 directory and also not on any object, and is persistent and retrievable from any script as long as it exists.
The thread is here:
http://nwn2forums.bioware.com/forums/viewtopic.html?topic=539873&forum=1...
These get get saved to the Bioware Database, which is included in NWN2. They can be accessed via the SetCampaign* and GetCampaign* functions. This is a physical write to the hard drive, and can bog down servers if there are a lot of requests happening at once.
It is worth noting that variable names cannot be longer than 32 characters, or the setting of the variable will fail.
A good system for managing campaign variables is Knat's NBDE.
Globals get saved to a globals.xml file that gets placed in your save game directory when you save the game. You can set and get global variables with the SetGlobal* and GetGlobal* functions.
These variables exist at a level similar to the campaign folder, meaning they exist in the context of a saved game and can cross from one module to another. They are not physically written to the xml file until a save happens, however.
A good way to check that your variables are getting saved correctly is to save the game an open the file.
One important thing to note is that global variable names can have spaces, but they cannot be more than 32 characters long.
Here are the script templates you can add by right clicking in the Scripts window and choosing "Add From Template." I found it nice to have them available outside of the Toolset for ease of reference.
I'll be adding the rest later.
Use this template when you are creating a script that will control actions during a conversation. Pass the variable nAction from the conversation to tell the script which case you want to perform. For instance, pass 100 as the nAction argument to execute the code for case 100.
If you need more information on what a case statement is, see the Wikipedia entry on case statements.
A common way to control the action in conversation switches is with ActionPauseCutscene() and AssignCutsceneActionToObject(). A cut scene action is an action that has a special flag that tells the pause function to resume once the action has been complete. An example from the OC would is:
ActionPauseCutscene(1000, FALSE); AssignCutsceneActionToObject(oRegulator, ActionWait(1.0f));
This example uses a regulator because the example it comes from (a romance script in module 3500) needed to keep both speakers available for other actions. For best results, a regulator is an Ipoint speaker, which can be found under Blueprints > Placeables > Misc Props. I found that removing the heartbeat from the Ipoint and also using it as the speaker is a good way to make sure that you don't run into conflicting action assignments.
Once you have the pauses set and you static cameras place on your converations nodes, you can add custom functions in the case that will control the movement of other characters. For instance, in the following example you can see that there are a number of custom functions that are included in the file that get referenced.
case 6: ActionPauseCutscene(15000, FALSE); AssignCutsceneActionToObject(oRegulator, ActionWait(15.0f)); MoveCameraman1Again(); PlayRomanceStinger(); MovePC(); DelayCommand(11.0f, FadeOut()); break;
and further down:
void MoveCameraman1Again()
{
object oCameraman = GetTarget("3501_cameraman1");
object oWP = GetTarget("3501_wp_camerman2");
AssignCommand(oCameraman, ClearAllActions(TRUE));
DelayCommand(1.0f, AssignCommand(oCameraman, ActionForceMoveToObject(oWP, FALSE)));
}
Note that I usually use GetNearestObjectByTag instead of GetTarget, but that's just because I'm not sure what GetTarget really does.
Obviously you can go pretty crazy with this. Here is the base code from the template for reference:
//
/*
master script for conversation X
*/
//
#include "ginc_actions"
void main(int nAction)
{
object oPC = GetPCSpeaker();
switch ( nAction )
{
case 100: //
break;
case 200: //
break;
case 300: //
break;
case 400: //
break;
case 500: //
break;
}
}
The onClientEnter cut scene script is a great way to start a cut scene conversation. Create either an Ipoint with a new tag or use a creature as the main speaker. Also create a conversation. Then plug these two into number three (below).
The condition to play and the additional cutscene scripting are great places to use variables, either on the module or campaign level, to control whether the cut scene should fire. For instance, if you set a quest tracking variable called "sidequest2" to be 10 when the party got the quest, and you wanted to test for condition sidequest2 == 10, you'd place that in step two below. Then in the additional cut scene scripting you'd update the quest tracking variable to 20 so that the game knew not to play this cutscene again.
// Area OnClientEnter Cutscene.nss
/*
Area OnClientEnter event handler template. Setup cutscene(s) to fire when the party enters a new area.
This script will execute after a group area transition using JumpPartyToArea().
bCutsceneCondition will determine if a cutscene should play, but each cutscene is restricted to play only once.
HOW TO SETUP A CUTSCENE:
0. Copy and paste script block into "CLIENT ENTER CUTSCENES"
1. Specify a title for your cutscene
2. Replace (FALSE) with condition to play cutscene
3. Specify Speaker and Dialog of conversation
4. Add additional cutscene scripting
// Cutscene: 1. Example Title
if (GetIsCutscenePending(stCI) == FALSE)
{
bCutsceneCondition = (FALSE); // 2. Replace (FALSE) with condition to play
sSpeakerTag = ""; // 3. Specify Speaker and Dialog
sDialogName = "";
stCI = SetupCutsceneInfo(stCI, sSpeakerTag, oPC, sDialogName, bCutsceneCondition);
if (GetIsCutscenePending(stCI) == TRUE)
{
// 4. Additional cutscene setup
}
}
*/
// BMA-OEI 2/3/06
// BMA-OEI 2/7/06 added speaker == PC check
// ChazM 2/7/06 modified implementation
// ChazM 2/7/06 moved funcs to ginc_cutscene
// BMA-OEI 2/7/06 support multiple cutscenes
// BMA-OEI 2/7/06 revert original controlled char
// BMA-OEI 2/8/06 added comments, Group Area Transition restriction
#include "ginc_cutscene"
int StartingConditional()
{
// Do not execute if OnClientEnter was not fired from a group area transition
if (FiredFromPartyTransition() == FALSE) return (FALSE);
// Get party leader, force control of owned PC
object oPC = GetFirstEnteringPC();
object oLeader = GetFactionLeader(oPC);
oPC = SetOwnersControlledCompanion(oLeader);
// Initialize temp CutsceneInfo
struct CutsceneInfo stCI;
stCI = ResetCutsceneInfo(stCI);
int bCutsceneCondition;
string sSpeakerTag;
string sDialogName;
// *** START CLIENT ENTER CUTSCENES ***
// Cutscene: 1. Example Title
if (GetIsCutscenePending(stCI) == FALSE)
{
bCutsceneCondition = (FALSE); // 2. Replace (FALSE) with condition to play
sSpeakerTag = ""; // 3. Specify Speaker and Dialog
sDialogName = "";
stCI = SetupCutsceneInfo(stCI, sSpeakerTag, oPC, sDialogName, bCutsceneCondition);
if (GetIsCutscenePending(stCI) == TRUE)
{
// 4. Additional cutscene setup
}
}
// *** END CLIENT ENTER CUTSCENES ***
// Cue cutscene or revert control to original character
if (GetIsCutscenePending(stCI) == TRUE)
{
FireAndForgetConversation(stCI.oSpeaker, oPC, stCI.sDialog);
}
else
{
SetOwnersControlledCompanion(oPC, oLeader);
}
// If cutscene is pending, fade to black
return GetIsCutscenePending(stCI);
}
Here are some common scripting tasks.
A good place to start with general animations is the switches page here:
http://wendersnaven.com/node/43
The short answer is that you can set the following variables on a creature:
Ambient Animations
X2_L_SPAWN_USE_AMBIENT = 1
Immobile Animations
X2_L_SPAWN_USE_AMBIENT_IMMOBILE
With no waypoints, "X2_L_SPAWN_USE_AMBIENT" includes the familiar wandering around near their spawn point and doing some basic interactions with other NPCs. "X2_L_SPAWN_USE_AMBIENT_IMMOBILE" causes them to do the same interactions (waving, force talking, etc.), but they stay in one place. They will turn to face other NPCs, however.
There are a few waypoints that cause additional behaviors. From x0_i0_amins:
Creatures will move randomly between objects in their
area that have the tag "NW_STOP". (I think there is a % chance of them doing this or the standard wander around.)
UPDATE: After some testing, it appears the behavior in the next two paragraphs does not work. Generally speaking, waypoints across areas don't work at all in NWN2. They tended to be inconsistent in NWN1, also. Begin incorrect documentation:
Creatures who are spawned in an area with the "NW_HOME" tag
will mark that area as their home, leave during the day,
and return at night.
Creatures who are spawned in an outdoor area (for instance,
in city streets) will go inside areas that have one of the
interior waypoints (NW_TAVERN, NW_SHOP), if those areas
are connected by an unlocked door. They will come back out
as well.
End incorrect documentation.
There is a good conversation about ambient animations scripts happening here:
http://nwcitadel.forgottenrealmsweave.org/showthread.php?t=1184
qwertyuiop666 asked a question about how one would go about changing the background music in an area when a player walks over a trigger. Here's his question:
I would like to have the music change when the player walks down a path. I am assuming that the best option is to do it from a generic ground trigger to change the music playing when I step on it? I managed to make a trigger that stops the current song. That was easy. I made the ground trigger and then in the "On Enter Script" I just used the "ga_music_stop" script from the drop down.
Shaughn78 and Melirinda were kind enough to give some helpful suggestions. Here is the script that Shaughn78 eventually posted with some minor tweaks for readaiblity:
void main()
{
object oEnter = entering object;
object oArea = GetArea(oEnter);
int nMusic = GetLocalInt (OBJECT_SELF,"music");
int nDefault = GetLocalInt (OBJECT_SELF,"default");
int nCurrent = Get Current Music;
int nOnce = GetLocalInt (OBJECT_SELF,"once");
if(!GetIsPC(oEnter))
{
return;
}
if (nOnce == 0)
{
SetLocalInt (OBJECT_SELF,"once",1);
SetLocalInt (OBJECT_SELF,"default",nCurrent);
}
if(nCurrent != nMusic)
{
MusicBackgroundChangeDay(oArea, nMusic);
}
else
{
MusicBackgroundChangeDay(oArea, nDefault);
}
}
See the full thread here:
http://nwn2forums.bioware.com/forums/viewtopic.html?topic=645938&forum=1...
There are a number of options out there for dealing with death, but when dealing with respawn, there is one very important thing to know:
The death and respawn event scripts never on Respawn fire when you use the GUI_DEFAULT_DEATH_SCREEN with DisplayGuiScreen. This is the call for nwn_o0_death, the default script for death events.
Both nwn_o0_death and nwn_o0_respawn contain respawn code, but the gui actually uses a callback script named gui_death_respawn. Opening this file through the File > Open script/conversation menu, you'll see that the default death script is:
// gui_death_respawn.nss
/*
Death GUI 'Respawn' callback: wake up groggy
*/
// BMA-OEI 6/29/06
#include "ginc_death"
void main()
{
// Resurrect PC
object oPC = OBJECT_SELF;
WakeUpCreature( oPC );
RemoveDeathScreens( oPC );
// Apply Groggy penalty
effect eGroggy = EffectDazed();
//eGroggy = EffectLinkEffects( EffectSlow(), eGroggy );
ApplyEffectToObject( DURATION_TYPE_TEMPORARY, eGroggy, oPC, RoundsToSeconds( 2 ) );
}
This results in the character standing up right where the died. Generally, it is better to send them to a safe place, such as a standard waypoint with the tag "NW_DEATH_TEMPLE".
For example, here is how Lordniah did his respawn callback script in the FRW base module.
// gui_death_respawn.nss
/*
Death GUI 'Respawn' callback: wake up groggy
*/
// BMA-OEI 6/29/06
#include "ginc_death"
void main()
{
// Resurrect PC
object oPC = OBJECT_SELF;
WakeUpCreature( oPC );
RemoveDeathScreens( oPC );
// Apply Groggy penalty
effect eGroggy = EffectDazed();
//eGroggy = EffectLinkEffects( EffectSlow(), eGroggy );
ApplyEffectToObject( DURATION_TYPE_TEMPORARY, eGroggy, oPC, RoundsToSeconds( 2 ) );
//Jump respawner to temple waypoint if it exists
object oTemple = GetObjectByTag("NW_DEATH_TEMPLE");
if(GetIsObjectValid(oTemple))
{
ClearAllActions(TRUE);
ActionJumpToLocation(GetLocation(oTemple));
}
}
Thanks to Lordniah for putting that comment in his k_module_respawn script that it never fires. Hopefully this info will help someone else out there.
Thanks to cdaulepp and loudent2 for pointing this one out:
When you are using GetGoldPieceValue() on an item to get the value, if the item is not identified the function will return 1.
See the full post here:
GetGoldPieceValue() Does it work correctly?
Lugoun posted this handy script in the Helpful Scripts thread in the official forums. It allows you to do a find and replace on a string. Just pass in the text you want to perform the find and replace on, the string to find and the string you want to replace it with. His script is below and I've added comments.
// Returns a string based on sStartString where sCurrentWord is replaced by sNewWord
string FindReplaceSubString(string sStartString, string sCurrentWord, string sNewWord)
{
int nLength = GetStringLength(sStartString);
int nCurrSubLoc = FindSubString(sStartString, sCurrentWord);
string sLeftSide = GetStringLeft(sStartString, nCurrSubLoc);
int nCurrentLength = GetStringLength(sCurrentWord);
int nLeftSideLength = nCurrSubLoc + nCurrentLength;
int nRightSideLength = nLength - nLeftSideLength;
string sRightSide = GetStringRight(sStartString, nRightSideLength);
string sNewString = sLeftSide + sNewWord + sRightSide;
return sNewString;
}
So for instance, you can do a call like this:
string sStartString = "the quick brown fox is quick";
string sNewString = FindReplaceSubString(sStartString, "quick", "slow");
Which would assign sNewString a value of "the slow brown fox is slow".
You can make a creature lay down by placing the following two commands on a dialog node:
ga_play_ca_snd with the sAnim set to "laydownB"
ga_play_custom_animation with the sAnim set to "proneB"
Make them stand up by using:
ga_play_custom_animation with sAnim set to "standupB"
Note that this only work for NPCs you can select via a tag.
Thanks to John B. Gardner's Animation Viewer, where I figured out how to do this.
In this thread, puiwaihin was trying to figure out why his invisible creature couldn't cast spells at the PCs. In the end they figured out that anything that is invisible or hidden can't do it.
jackyo123 suggested making the creatures super small instead.
Check it out here:
http://nwn2forums.bioware.com/forums/viewtopic.html?topic=576056&forum=114
Bemmu over at NWScript.com has created a tutorial that will take you through creating a basic conversation with an attached script to give the PC Speaker some gold.
Tutorial level: very basic
Following along with this tutorial should only take a few minutes. The goal will be to create a new Neverwinter Nights 2 module with one creature in it that you can interact with to receive gold from it.
Click here to view the tutorial:
http://www.nwscript.com/how-to-make-an-npc-give-you-gold.php
Bemmu over at NWScript has created a tutorial for attaching a script to a lever that will open a door.
Tutorial level: very basic
The goal in this tutorial is to create a lever that will cause a door to open when pulled. Following along should be very easy and there will only be about three lines of nwscript (the scripting language in Neverwinter Nights 1 & 2) involved.
Head over to NWScript to check it out:
http://www.nwscript.com/making-a-lever-operated-door.php
Bemmu over at NWScript.com has created a tutorial for creating multiple objects in a line using vectors. The object in question happens to be beer!
Tutorial level: basic
A nice feature of virtual worlds is that things can be created out of thin air without cost. In this tutorial we will play a bit with this by creating a sort of beer vending machine, where pulling a lever will add a new beer bottle to the end of an endlessly expanding row of them.
Head over to NWScript to check it out:
http://www.nwscript.com/infinite-beer.php
A great way to control ambient movement of NPCs is using scripted waypoints. See the sticky thread here:
http://nwn2forums.bioware.com/forums/viewtopic.html?topic=512800&forum=1...
You can see scripted waypoints at work in the Official Campaign's 1700_Merchant_Quarter.mod and 3000_Neverwinter_A3.mod merchant exterior areas. In module 1700 the road network method is used for ambient commoners (although I could not find the actual script) and in 3000 the dock workers use it.
Here's what Charles Mead (Gameplay Scripter) posted:
Here's some info on a new feature called Scripted Waypoints.
Scripted waypoints builds on the current waypoints system by making the arrival at each waypoint call a script. There are no special requirements for preparing this – it will work with any NPC using the regular default scripts and any new or previously placed set of walkway points.
Quick refresher of the standard waypoint system:
Creatures will automatically walk along a sequence of waypoints that are tagged according to the following convention: “WP_<creature’s tag>_##”. The NPC will begin the journey at waypoint 1 and continue traveling sequentially to waypoint n, and then back again in reverse.
Basic Useage:
In the new “scripted waypoint” system, whenever a creature reaches a waypoint, it calls a script that is named very similarly to the tag used for the waypoints, like this: “wp_<creature’s tag>”. Notice there is just one script for the entire set of waypoints. If the script does not exist, then nothing additional happens and the NPC simply walks back and forth through the set of waypoints.
Below are some examples of “scripted waypoints” scripts:
Example 1.
This script tells the NPC to stop at every waypoint and face in the direction the waypoint is facing and pause 1 second before continuing to the next waypoint.
Notice the inclusion of “ginc_wp” which has a number of helpful functions such as FaceAndPause().
#include "ginc_wp"
void main()
{
int iCurrentWP = GetCurrentWaypoint();
FaceAndPause(iCurrentWP, 1.0f);
}
Note how we call GetCurrentWaypoint() which returns the waypoint we have just reached. This will typically always be the first thing called in a “scripted waypoints” script.
Example 2.
This is a more complex script that completely overrides the normal waypoint behavior and demonstrates how each waypoint can be individually scripted. It accomplishes the following:
When the NPC reaches waypoint 1, he will sit down for a few seconds, and then randomly travel to one of the other waypoints in the set other than the first.
When the NPC reaches waypoint 2, he will play a “get low” animation, and then proceed to waypoint 1.
When the NPC reaches waypoint 3, he will simply head back to waypoint 1.
If there are more than 3 waypoints in the set, then when the NPC reaches one of those he will behave in the default way.
#include "ginc_wp"
#include "ginc_wp"
void main()
{
int iCurrentWP = GetCurrentWaypoint();
int iNextWP;
switch (iCurrentWP)
{
case 1:
iNextWP = Random(GetNumWaypoints()-1) + 2;
SetNextWaypoint(iNextWP);
ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 1.0, 7.0);
break;
case 2:
SetNextWaypoint(1);
ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW, 1.0, 4.0);
break;
case 3:
SetNextWaypoint(1);
break;
}
}
Note in this example the use of SetNextWaypoint(). The “next” waypoint is the one the NPC is enroute to. SetNextWaypoint() allows us to override this and redirect the NPC to an alternate waypoint.
The script template “wp_tag” is available in the script templates directory in source safe. Simply copy and paste it with the proper script name, replacing the word “tag” with the tag of the creature that will be walking waypoints.
*Advanced Usage*
How to have two or more creatures with different tags walk the same set of waypoints:
To override the initial waypoint set a creature uses, simply set a local string variable “WP_TAG” on the creature with the value of the desired waypoint set to use. So, if you have a bear waypoint set for a creature tagged “bear”, you could get a chicken to also walk these waypoints by setting the chicken's local variable WP_TAG to “bear”.
Note that this variable is only checked when the creature is first spawned, so changing this value via script will have no effect.
How to change the set of waypoints a creature walks dynamically:
You can change the set of waypoints a creature walks at any time via script using this function:
object SetWWPController(string sWalkWayPointsTag, object oCreature=OBJECT_SELF);
So from the previous example, we could have our chicken walk the bear’s waypoints by calling this function in script:
SetWWPController(“bear”);
How to pause/restart a creature walking waypoints.
To stop a creature from walking waypoints, use:
SetWalkCondition(NW_WALK_FLAG_PAUSED, TRUE);
You may also want to clear all actions, otherwise, the creature will still complete his pending actions which will take him to whatever the next waypoint is.
To Restart a creature walking waypoints, use:
SetWalkCondition(NW_WALK_FLAG_PAUSED, TRUE);
The creature should get started on his next heartbeat.
How to create a road network:
The basic outline for a script like this is available in the script template “wp_road_walker” and reads as follows:
#include "ginc_wp"
const int REDIRECTOR_WP = 1;
void main()
{
int iCurrentWP = GetCurrentWaypoint();// where we are
int iNextWP;
switch (iCurrentWP)
{
case 1:
// Node 1 should be placed off somewhere out of the way. This is where the creatures
// will hang out in between reaching the end of the path and starting on a new path.
// Creatures will change their appearance in between travels.
// 1st param is the list of nodes they may reappear at. Must follow the pattern XX,YY,ZZ...
// 2nd param is the length of time to wait before reappearing.
StandardRedirectorNode("02,03", 5.0f);
break;
case 2:
// Nodes 2 on up define the network the road walkers travel. They will never return to the node
// they just came from. When they reach an end node (typically a door or a route out of the area)
// they will be be "redirector node" - typically node 1.
// 1st param is the list of nodes they may travel to. Must follow the pattern XX,YY,ZZ...
// (end nodes are those with only 1 element in the list)
// 2nd param is the redirector WP
StandardRoadNetworkNode("03", REDIRECTOR_WP);
break;
case 3:
StandardRoadNetworkNode("02", REDIRECTOR_WP);
break;
}
}
Where can I learn more about other thing I can do?
Many useful functions are listed in the prototypes of the include file “ginc_wp”.
Patcha posted a script to let PCs sit on objects.
I'm providing you the way to let object be "sittable" and the script that allow PC to sit on them!
The file contains:
- the script (in a .erf to import)
- the English tutorial to bring "sittable" objects
- the Italian tutorial (the same)
- a module with many brench and chairs, to test this mothod
Check it out on the vault:
http://nwvault.ign.com/View.php?view=NWN2Scripts.Detail&id=43
This apparently still uses the old "nw_g0_convplac" from NWN1. Follow these steps:
Test it out and your should get your conversation when you use the placeable.
The default treasure generation script seems to be gp_treasure_op_de. It seems to check variables set on the container it is called from. It includes some of the X2 scripts from HotU. I spent some time walking through how this all works.
Note: After playing with this system a bit more, I've found that:
1. The base chance is 50%. This means that unless you set the base chance on an area, you'll get just a few gold just over 50% of the time.
2. The magic treasure type tends to give one or two +1 weapons for a level 1 PC. This may be a bit over-powered for low-level mods.
3. For better customization, consider lordniah's FRW base module's implementation of the NWN1: SoU treasure scripts (For more see http://www.wendersnaven.com/node/98).
Here is the header comment, which explains the basics:
// gp_treasure_op_de
/*
Spawns in general purpose treasure and gold based on variables:
TreasureClass - one of three values. Default is low
const int X2_DTS_CLASS_LOW = 0; //Treasure Class Low
const int X2_DTS_CLASS_MEDIUM = 1; //Treasure Clas Medium
const int X2_DTS_CLASS_HIGH = 2; //Treasure Class High
TreasureType - add desired types together. For example, gold + disposable = 5
Defualt is 5 (gold + disp)
Note that you cannot add the same type more than once (i.e. no gold+gold).
const int X2_DTS_TYPE_DISP = 1;
const int X2_DTS_TYPE_AMMO = 2;
const int X2_DTS_TYPE_GOLD = 4; // actually gold and gems
(not allowed) const int X2_DTS_TYPE_ITEM = 8; // char specific Item (ignores treasure class)
const int X2_DTS_TYPE_MAGIC = 16; // random magic items
const int X2_DTS_TYPE_MUNDANE = 32; // random mundane items
This script should be placed in the container's OnOpen and OnDeath events.
If bashed, disposeable will be dropped and broken item generated.
If no treasures are generated, 1d20 gold will be created.
*/
This allows module authors to quickly create random treasure of different types by setting both the "TreasureClass" integer variable and the "TreasureType" integer variable on the container. "TreasureClass" uses a simple 0, 1, or 2. "TreasureType" uses a single number that adds up the type values you want for the container. For example, ammo (2) + gold (4) + magic items (16) would be a value of 22.
An important thing to note is that X2_DTS_TYPE_ITEM (8) is not allowed. Here is the reason given in a comment further down in the script: "these don't scale and are to dangerous for balance reasons to have in the standard treasure generation."
Our script then calls DTSGenerateTreasureOnContainer(), which is located in "x2_inc_treasure." This script first calls gets a random number of items to create based on the call to DTSGetMaxItems().
Looking at DTSGetMaxItems(), we see that it first looks for the module-level integer variable "X2_DTS_MAXITEMS." If it can't find it, it uses a default of 2. Note that this variable is not set in the switches file or in the default "On Module Load" file, which means the value will be 2 unless you have specifically set it.
Returning to DTSGenerateTreasureOnContainer(), the next step is to call DTSGenerateTreasureItems() for each item we are going to create. This function checks the chances for each of the types of items we told it to create with the variables set on the container. The chance is defined by DTSGetBaseChance(). The default is defined as 50 in the constants at the top of the file, but these can be overridden at the area and module level with the variable "X2_DTS_BASECHANCE." If a class and chance condition is met based on a randomized number, an item is created by DTSGetRandomItemResRef().
Looking at DTSGetRandomItemResRef(), we see that we first have to get the name of the .2da file we want to reference using DTSGet2DANameByType(). This file looks for a module-level string variable, and uses a default if it can't find one.
The variables it looks for are:
(Note that these match the constants uses for the treasure type.)
The default .2da files that will be used are:
Remember that all these files can be found in Data/2da.zip, in your NWN2 programs files ("C:\Program Files\Atari\Neverwinter Nights 2\Data" by default). Extracting the .zip files to your desktop and opening the .2da files in Microsoft Excel, will reveal how these are laid out. For example, looking at the random magic .2da file (des_treas_magic.2da), will show that the low class items are all +1, the medium class items are +1 and +2, and the high class items are +2, +3 and +4. There is also a very high column, which is probably left over from the epic levels in NWN1: HotU.
Controllore posted a concise answer to using the old HotU "wantering encounter while resting" script.
View the module properties, in the On Module Load event you will see "x2_mod_def_load". Uncomment the line (around 123)
SetModuleSwitch ( MODULE_SWITCH_USE_XP2_RESTSYSTEM, TRUE )
Then where you see
SetWanderingMonster2DAFile( "des_restsystem" )
uncomment it and change "des_restsystem" with any other name you choose.
"des_restsystem" is the name of the 2da file that will be used for the wandering monsters (type, percentage of appearing at daytime/nighttime).
Edit des_restsystem.2da, do your changes and save it with the name you used in x2_mod_def_load.
That should do it.
Edit:
Into your areas, insert a variable named X2_WM_ENCOUNTERTABLE of type string. Its value must be the name used for the TableName in the des_restsystem.2da.
Read the full threat here:
http://nwn2forums.bioware.com/forums/viewtopic.html?topic=535148&forum=1...
Here's some helpful resources for working with companions... (more cominig soon)
The companion influence system used in the OC is not available by default in new modules. To get them, copy all of the kr_influence* scripts from "C:\Program Files\Atari\Neverwinter Nights 2\Campaigns\Neverwinter Nights 2 Campaign." You can then paste them into your own campaign. Note that you will need to edit the constants and companion names across all scripts to match your own companions.
In a conversation between Wayne and Lord Niah over in the FRW forums Lord Niah had some good suggestions for working with companions.
Here are some rules of thumb for dealing with companions and the roster. I don't recall some the exact names for commands and do not have acces to the toolset at the moment.
#1 You should only have one instance of a companion placed within your campaign and you should never spawn that companion using CreateObject.
#2 If you want to destroy a companion, then use the despawn command (not the DestroyObject command)
#3 If you want to spawn your companion back in, use the spawn command instead of CreateObject
#4 If you want to add your companion to the roster, but don't want players to see them in the GUI selection screen, then just mark them as Campaign NPCs. Don't waste time adding/removing the companion from the roster every time they join or leave the party. Once they are in the roster, you just have to add/remove them from the party.
Check out the forum here:
http://nwcitadel.com/forums/showthread.php?t=873
Henchmen are companions who the player cannot directly control or adjust inventory for. Thanks to sgt_why for this explanation on how to set them up.
Here's his post from http://nwn2forums.bioware.com/forums/viewtopic.html?topic=545390&forum=1...
for this example;
string sTag = "c_human"// is this a henchmen ? TRUE or FALSE
gc_henchman(sTag)// this adds them to your party as henchmen, not companion
ga_henchman_add(sTag);
/*
note: this has (4) actual parameters ... the last flag can be very useful.
sTag - tag of the creature you want to add
bForce - if set to 1, sTarget will be added even if player already has maximum henchman
sMaster - The Creature you are adding the henchman to (default is PC Speaker)
bOverrideBehavior - if set to 1, sTarget's event handlers (scripts) will be replaced with henchman ones.
*/// removes the henchmen from the party
ga_henchman_remove(sTag);- - - - -
I put all of these into the conversations and seems to work great. In fact, think if the henchman dies .. they are removed from the group as well.And you cant click on them and change thier inventory .. although I did notice there appear to be scripts out there to make the henchman show you his inventory and such, like a companion. But it would need to be during a conversation or such. (I think?)
I wanted to add a couple "friendly"(s) in my areas .. that if the PCs save them, will offer to help out for a short time .. as well as award 50xp for not letting the mobs kill them off.
Following his directions, I was able to create a few henchmen and get them to work with very little setup. Some quick things I noticed:
Kudos to OE on making such an easy to use system, and thanks again to sgt_why
Include ginc_groups to see a number script used for working with group of creatures.
Recently DaveyHavock asked a question about how to use groups to create an onDeath event.
Howdy, wondering if someone has a moment to explain how the custom group ondeath script is used. I guess what i dont understand is how to name the grp, i see it uses getgroupname, but how do i define what the group is?
Kaldor Silverwand provided a good response with an example script:
The first meeting with Neeshka makes use of groups in this way.
The functions you'll need are are in ginc_group.
Here is a bit of script that I have used for having a few halfling NPC's put into a group and then a script is started once they are dead.
Here's the code he posted. Remember to include ginc_groups. Change the group name and tags, then attach this on a dialog action and you're good to go!
string sGroupName = "HalflingBrothers";
object oNHalfling;
int i;
ResetGroup(sGroupName);
for (i = 1; i<=3; i++)
{
object oNHalfling = GetObjectByTag("NHalfling" + IntToString(i));
GroupAddMember(sGroupName, oNHalfling);
ChangeToStandardFaction(oNHalfling, STANDARD_FACTION_HOSTILE);
StandardAttack(oNHalfling, GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC));
}
GroupOnDeathExecuteCustomScript(sGroupName, "0160_a_cs_halfling_postfight");
See the full thread here:
http://nwn2forums.bioware.com/forums/viewtopic.html?topic=646321&forum=1...
It's important to name your scripts in a consistent manner so that you know what they do and you know where to find them when you need them. Here is some information to help with naming.
Barry The Hatchet posted a good roll up of the script naming conventions the CODI project came up with in NWN1. Here's the link: http://nwcitadel.forgottenrealmsweave.org/showthread.php?t=1131
Here's the actual list:
Include Scripts
Include scripts should be prefixed with INC_
Event Driven Scripts
Following the prefix for EVENT driven scripts, this is the majority; you have 1 letter denoting the object type that has the event this script goes in, they are as follows:
* N - NPC
* M - Monster/Creature
* A - Area
* O - Module
* P - Placable
* T - Trigger
* D - Door
* R - Trap
* C - Conversation
* E - Encounter
* I - Item - This script is not placed on an item per se, but called by module level scripts.
* X - No Object - Use this for a script that is called by other scripts. Executable scripts.
This character is followed by a two character short for the event it goes in, by category:
* N, M
o CO - onConversation
o DI - onDisturbed
o PA - onPhysicalAttacked
o SP - onSpawn
o UD - onUserDefined
o SC - OnSpellCastAt
o BL - onBlocked
o DM - onDamaged
o HB - onHeartbeat
o DE - onDeath
o CR - onCombatRoundEnd
o PE - onPerception
o RE - onRest
* A
o EN - onEnter
o EX - onExit
o HB - onHeartbeat
o UD - onUserDefined
* O
o CE - onClientEnter
o CL - onClientLeave
o PR - onPlayerRest
o RS - onRespawn
o DY - onPlayerDying
o DE - onPlayerDeath
o LE - onPlayerLevelUp
o IA - onItemActivated
o IQ - onItemAquired
o UI - onUnaquireItem
o ML - onModuleLoad
o HB - onHeartBeat
o UD - onUserDefined
o EI - onEquipItem
* P
o US - onUsed
o HB - onHeartBeat
o DE - onDeath
o AT - onAttacked
o SC - onSpellCastAt
o DM - onDamaged
o OP - onOpen
o OC - onClosed
o DI - onDisturbed
o LO - onLocked
o UL - onUnlocked
o UD - onUserDefined
* T
o CL - onClick
o EN - onEnter
o EX - onExit
o HB - onHeartBeat
o UD - onUserDefined
* D
o AT - onAreaTransitionClick
o OC - onClosed
o DM - onDamaged
o DE - onDeath
o FO - onFailedToOpen
o HB - onHeartBeat
o LO - onLocked
o OP - onOpened
o AT - onAttacked
o SC - onSpellCastAt
o UL - onUnlocked
o UD - onUserDefined
* R
o DI - onDisarmed
o TT - onTrapTriggered
* E
o EN - onEnter
o EX - onExit
o OE - onExhausted
o HB - onHeartBeat
o UD - onUserDefined
* C
o AT - Actions Taken
o AP - Appears When (StartingConditional Script)
o AB - onConversation Aborted
o EN - onConversation end
* I
o US - Called when the item is used.
o AQ - Called when the item is Aquired.
o UA - Called when the item is UnAquired.
Prefix examples
Module OnClientEnter script prefix: OCE_
Area OnEnter script prefix: AEN_
Placeable OnUsed prefix: PUS_
__________________
Just bend the pieces 'til they fit.
buried in the official Toolset documentation is a section about naming conventions for scripts. I've replicated it here for easy reference.
All scripts in NWN2 are are prefixed with one of the following:
Use the following for item scripts:
|
i_<Item Tag>_ac |
script to execute when item activated |
|
i_<Item Tag>_aq |
script to execute when item acquired |
|
i_<Item Tag>_ua |
script to execute when item unacquired |
|
i_<Item Tag>_eq |
script to execute when item equipped |
|
i_<Item Tag>_ue |
script to execute when item unequipped |
The module events will be written to automatically execute the proper script.
Conversations have 2 script types.
|
a_<Speaker>… |
actions taken |
|
c_ <Speaker>… |
conditional (Text appears when...) |
Scripts for events on objects should have a prefix to indicate the Object Type. If an object has multiple events, a suffix should be added each script to indicate the Event Type.
|
Object Type |
Abbreviation |
|
Area |
use area num |
|
Creature (Being) |
b |
|
Door |
d |
|
Encounter |
e |
|
Merchant |
m |
|
Placeable |
p |
|
Trigger |
tr |
|
Event |
Abbreviation |
Area** |
Creature |
Door |
Encounter |
Merchant |
Placeable |
Trigger |
|
|
X |
|
|
|
|
|
||
|
click* |
ck |
|
|
X |
|
|
|
X |
|
close* |
cs |
|
|
X |
|
X |
X |
|
|
combat round end* |
ce |
|
X |
|
|
|
|
|
|
conversation |
co |
|
X |
|
|
|
|
|
|
damaged |
da |
|
X |
X |
|
|
X |
|
|
death |
de |
|
X |
X |
|
|
X |
|
|
disturbed |
di |
|
X |
|
|
|
X |
|
|
enter |
en |
X |
|
|
X |
|
|
X |
|
exhausted* |
ed |
|
|
|
X |
|
|
|
|
exit |
ex |
X |
|
|
X |
|
|
X |
|
fail to open |
fa |
|
|
X |
|
|
|
|
|
heartbeat* |
hb |
X |
X |
X |
X |
|
X |