Scripting

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=114

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=114

 

Variable scope

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=114

Campaign variables

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

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.

Add From Template

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.

Conversation Switch

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;
    }

}

Area OnClientEnter Cutscene

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);
}

Common Tasks

Here are some common scripting tasks.

Adjusting Characters when they enter a module

A common task that can be more confusing that it should be is adjusting the level, equipment or gold of a character when entering a module. A solid understanding of what scripts are called when the PC enters is a good place to start. Much of this information is taken from this post: http://nwn2forums.bioware.com/forums/viewtopic.html?topic=528278&forum=1...

What happens when a PC enters a module

When a PC logins in, the first events to fire are the OnAcquireItem and OnPlayerEquipItem events. At that point, the PC object exists and can have inventory. But it has no location.

Then OnClientEnter fires. The PC object is complete, but still has no location. (the lack of location is what causes most scripting issues)
Once the PC has been assigned an area, OnPCLoaded is called.

Then OnEnter and OnClientEnter are called for the area the PC ends up in. (OnEnter is called for all creatures entering an area - including NPC right after onModuleLoad, OnClientEnter is just for PCs)

A trigger would be called last.

Transitioning between modules in a campaign

One more thing I noticed: when you transition to a new module in a campaign (StartNewModule and LoadNewModule), OnPCLoaded is not called. OnClientEnter is called, and so are the area's OnEnter and OnClientEnter. (onClientLeave is also not called)

OnPCLoaded is called when a PC logs out and back in.

Adjusting the new characters

The best way I've found to adjust a new PC is to use an OnClientEnter Script. Go to View > Module Properties to get to the Module Properties window. Under the Scripts heading, look for the On Client Enter Script.

The following script is an example that checks to see if this is the first time the PC has entered the current campaign, removes all inventory, then sets the XP and gold for a level 3 character.

// example_oncliententer
/*
Description:
Onclient enter to call
Also has additional functions to set up first time
entry scripts for the pc.
FRW xp and gold guidelines:
Level XP Gold
2nd 1,000 450
3rd 3,000 1,350
4th 6,000 2,700
5th 10,000 4,500
6th 15,000 6,500
7th 21,000 9,500
8th 28,000 13,500
9th 36,000 18,000
10th 45,000 24,500
11th 55,000 33,000
12th 66,000 44,000
13th 78,000 55,000
14th 91,000 75,000
15th 105,000 100,000
16th 120,000 130,000
17th 136,000 170,000
18th 153,000 220,000
19th 171,000 290,000
20th 190,000 380,000
*/
// Vendalus Nov 15, 2008

// Removes all items from the players inventory
void RemoveAllItems( object oPC )
{
DestroyObject(GetItemInSlot(INVENTORY_SLOT_ARMS, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_ARROWS, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_BELT, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_BOLTS, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_BOOTS, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_BULLETS, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_CHEST, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_CLOAK, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_HEAD, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_LEFTRING, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_NECK, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC));
DestroyObject(GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oPC));

object oItem = GetFirstItemInInventory();
while( GetIsObjectValid(oItem) ){
DestroyObject(oItem);
oItem = GetNextItemInInventory();
}

}

// Checks to see if this player has already entered the campaign for the first time
int GetHasEnteredThisCampaign( object oPC )
{
return GetGlobalInt("ENTERED_"+GetName(oPC));
}

// Notes that this player entered the module for the first time
void SetHasEnteredThisCampaign( object oPC )
{
SetGlobalInt("ENTERED_"+GetName(oPC), TRUE);
}

// Gives some starting gold
void GiveStartingGold( object oPC, int nAmount )
{
int nGold = GetGold(oPC);
TakeGoldFromCreature(nGold, oPC, TRUE);
GiveGoldToCreature(oPC, nAmount);
}

void main()
{

// get our entering player
object oPC = GetEnteringObject();

// Only run if this is a pc who is entering for the first time
// and who is not a DM.
if( GetIsPC(oPC) && !GetHasEnteredThisCampaign(oPC) && !GetIsDM(oPC)){

// Note that this pc has entered for the first time
SetHasEnteredThisCampaign(oPC);

// Remove all items from the entering player
RemoveAllItems(oPC);

// Give them some starting gold for level 3
GiveStartingGold(oPC, 1350);

// Adjust to level 3
SetXP(oPC, 3000);

}

}

Obviously in a case like this, you'd want to present the character with a store immediately. Note also that there is a chart at the top of the script showing the XP and total gold guidelines per level as outlined by the Forgotten Realms Weave.

What happens when a PC leaves the module

Similarly, when the PC logs out OnClientLeave gets called. At that time, the client information is no longer associated with the object, just the physical characteristics. (which is another scripting issue) Then OnExit for the area is called (unless the PC logs out dead, then it is not called). Usually, there is nothing to do here and there have been some reports of this not firing correctly all the time.

Ambient Animations

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

Changing area music with a trigger

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=114

 

Death and Respawn

UPDATE: Kaldor Silverwand recently posted a good walkthrough on how to use the death and bleeding scripts from SoZ on the official forums. If you've got that installed, it might be a nice reference.

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.

GetGoldPieceValue()

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's Find and replace a word in a string

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".

Making a creature lay down

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.

Making an invisible object cast a spell at the PC

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

NWScript: Giving gold in a conversation

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

NWScript: Levers and doors

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

NWScript: Object Creation

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

Scripted Waypoints

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=114

Examples

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”.

 

Sitting

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

Starting a conversation with a placeable

This apparently still uses the old "nw_g0_convplac" from NWN1. Follow these steps:

  1. Start by copying the placeable you want to use. Right click on it and choose "copy blueprint > module". Find it at the bottom of the list and click on it to access its properties.
  2. Change the name, tag, resref as needed in order to keep track of it.
  3. Under the behavior section, set the following:
    • Plot = TRUE (optional, but suggested)
    • Static = FALSE
    • Useable? = TRUE
  4. If you have not already done so, create your conversation file and give it a name. Return to the behaviors section and either paste that name into the "Conversation" field, or use the dropdown to choose it from the list.
  5. Finally, under the Scripts section, set the On Used Script to "nw_g0_convplac"

Test it out and your should get your conversation when you use the placeable.

Treasure

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).

gp_treasure_op_de

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."

x2_inc_treasure

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:

  • X2_DTS_TYPE_DISP
  • X2_DTS_TYPE_AMMO
  • X2_DTS_TYPE_ITEM
  • X2_DTS_TYPE_GOLD
  • X2_DTS_TYPE_MAGIC
  • and X2_DTS_TYPE_MUNDANE

(Note that these match the constants uses for the treasure type.)

The default .2da files that will be used are:

  • des_treas_disp
  • des_treas_ammo
  • des_treas_gold
  • des_treas_items
  • des_treas_magic
  • des_treas_mundane

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.

Wandering Monsters

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=114

Companions

Here's some helpful resources for working with companions... (more cominig soon)

Companion Influence

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.

FRW: suggestions for dealing with 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

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=114

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:

  • A "guard this area and I'll come back for you" dialog option with the ga_henchman_remove script on it worked great to when you want to temporarily ditch them.
  • Use unique tags for each creature. If, in his example, you have a set of friendlies standing around, each one needs it's own conversation and tag. It helps to write the conversation first, then duplicate it. This will lead you to creating more distinctive NPCs anyway.
  • Set the bOverrideBehavior = TRUE if you didn't do anything with the scripts on your henchmen, or they won't follow the PC.
  • Commoner NPCs won't attack, which worked just how I wanted. I was able to create a lost little girl that the party could escort home. Thankfully, she didn't run up and try to punch people in the groin.

Kudos to OE on making such an easy to use system, and thanks again to sgt_why

Groups

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=114

 

Naming Conventions

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.

CODI naming conventions

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.

Official Documentation: Script Naming Conventions

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:

  • “g” for global scripts
  • “i_” for item related scripts (these are also global)

Item Scripts

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.

Conversation Scripts

Conversations have 2 script types.

a_<Speaker>…

actions taken

c_ <Speaker>…

conditional (Text appears when...)

Event Scripts

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

blocked

bl

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

X

lock

lo

X

X

open

op

X

X

X

perception

pe

X

physical attacked

at

X

X

X

rested

re

X

spawn

sp

X

spell cast at

ca

X

X

X

unlock*

ul

X

X

used

us

X

user defined*

ud

X

X

X

X

X

X

* These abbreviations don’t use the first 2 letters of the event name.

** Areas don’t need abbreviations.

Boldfaced events are defaults and are assumed if no event is given.

Other Scripts

Scripts not covered above should use these prefixes

inc_

include file

ai_

artificial intelligence (combat ai)

n_

extra miscellaneous script

Opening system scripts

For those of you like me who could not figure out how to open a system script except from the dropdown on creature properties, look under "File > Open Conversation/Script." The keyboard shortcut is "Ctrl + Shift + o."

It seriously took me a month to find that for some reason.

Spells and spell hooks

Thanks to DedoITA for pointing me in the direction of Lilac Soul's tutorial on spell hooks. Here's the introduction:

Since version 1.59, it has become exceedingly easy to hook spellcasting. Rather than having to edit each individual spell script, all you now how to do is create a single spell script of your own, and then have that script perform whatever you want the spell to do. In that script, you also get to set whether the original spell script should continue or not.

If you only want to edit a single spell, it may still be easier to just edit its spell script, but I still recommend using the spellhook system. Why? Because, if the spell script is updated in a future patch, your edits will still be valid, but so will the ones BioWare made.

However, the new spell-hook system is very useful for editing a number of spells (even all spells), for instance to make it so that they only work under certain conditions, or that, perhaps, the caster automatically takes a certain amount of damage whenever casting a spell. Thus, you can also use the spellhoook system to make certain creatures react to certain spells in different ways, etc. etc. The sky is the limit really.

Some of the menu options have changed for NWN2, but most of this is still valid.

Check out the full tutorial:
http://www.nwnlexicon.com/compiled/tutorial.lilacsoul-spell-hooking.html

 

 

Visual Effects

Creating visual effects through scripting works much the same way in NWN2 as it did in NWN1. Use EffectVisualEffect() to create an effect, then ApplyEffectToObject() or ApplyEffectAtLocation() to place that effect in the game.

From the official NWN2 Documentation:

"Visual effects are composed of effect files, which are individual components of an effect (a particle system, a trail, a billboard, etc.), and SEF files, which point to the effect files and organize them into a more complex visual effect."

Effect constants start with "VFX_".

  • Apply VFX_IMP_ constants instantly, to a target.
  • Apply VFX_DUR_ constants for a duration, either permanent or temporary, to a target.
  • Apply VFX_FNF_ constants instantly, and only at a location.

NWN Lexicon: visual effects samples

The following samples for creating visual effects through scripting are from the NWN1 Lexicon. (Currently the lexicon is at http://nwn1.nwn2lexicon.com/, but I'm unsure what's in store for that site.)

// Sample code for applying a VFX_IMP_ visual, a AC-bonus
// (Mage armor) visual, to a target.
void main()
{
    // This is the Object to apply the effect to.
    object oTarget = OBJECT_SELF;

    // Create the visual portion of the effect.
    effect eVis = EffectVisualEffect(VFX_IMP_AC_BONUS);

    // Apply the visual effect to the target.
    // - We apply VFX_IMP_ constants instantly, to a target.
    ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
}

// Sample code for applying a VFX_DUR_ visual, a 
// stoneskin visual, to a target.
void main()
{
    // This is the Object to apply the effect to.
    object oTarget = OBJECT_SELF;

    // Create the visual portion of the effect
    effect eDur = EffectVisualEffect(VFX_DUR_PROT_STONESKIN);

    // Apply the visual effect to the target.
    // - We apply VFX_DUR_ constants for a duration, here 
    //    it is permanent (could be temporary), to a target.
    ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDur, oTarget);
}

// Sample code for applying a VFX_FNF_ visual, a fireball, 
// to a location.
void main()
{
    // This is the Target to apply the effect at.
    location lTarget = GetLocation(OBJECT_SELF);

    // Create the visual portion of the effect
    effect eAOE = EffectVisualEffect(VFX_FNF_FIREBALL);

    // Apply the visual effect to the target.
    // - We apply VFX_FNF_ constants instantly, and only
    //   at a location.
    ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eAOE, lTarget);
}

Non-functional Visual Effects

Jassper asked a question in the official forums about certain VFX constants not working. Cracking open the visualeffects.2da, it was discovered that a lot of the effects listed don't have .sef files associated with them.

From loudent2 :

It turns out a lot of the effects have been deprecated. It isn't immedietely apparant which ones (I had a similar problem using the tornado effect). Not sure if this is the problem in your case.

And from Ravine_HU:

I've read somewhere about this... coz the VFXs totally changed in nwn2 - everything is in the sef-files. U can check the visualeffects.2da for the list - find the row, and if there is no xy.sef references in it...unlucky.

I personally miss some nice effect from nwn1, like this raise dead for example. I can't even find that kind of effect which is only visible to the PC whom attached to (for example, no effect for blindness...).

However, the visual effect editor is cool.

Oh yeah, and check the "broken functions list" topic too, there are some issues with applying visualeffects.

More from Jassper

After looking in the 2da file for spells, NWN2 uses visual sp_conjuration_conjure.sef for raise dead. I copied that to the visualeffects.2da under the heading VFX_IMP_RAISE_DEAD and vola! I have an effect - altough it is not the NWN1 style raise dead.

So that indeed seems to be the issue - these effects simply wern't included, or haven't been replaced yet.

BTW, quesion about saveing changes in the 2da's. Will the new 2da be local to that mod only, or is it now a global change for all modules?

And finally, from mykael22000

Saved .2das get written out to your override directory - meaning they'll affect all of your games, but aren't stored inside the module you edited so if you send it to someone else, they won't have a copy...

Get the nwnhak utility from NWN1, create a new hak and move the .2da into it. Then park it in your Hak directory and tell the module to use it. It still won't be sent to anyone with the module, but at least it'll complain and tell them that they are missing it. it also won't stuff up all of your other games.

Switches

Here is x2_inc_switches in its entirety. It's a great reference to have when trying to set options on objects via "switch" variables.

The most commonly used is probably the creature section for setting ambient animations, etc.

//------------------------------------------------------------------------------
//   x2_inc_switches::   Interface for switching subsystem functionality
//------------------------------------------------------------------------------
/*
   This file provides a basic interface for switching different Hordes of
   the Underdark subsystems on/off  and allows centralized access to certain
   "expert"  functionality like overriding AI or Spellscripts.

   Changing any of these switches from their default position is considered
   unsupported and done at your own risk - please do NOT send any bug reports
   about problems caused by these switches.
*/
//------------------------------------------------------------------------------
// Copyright (c) 2003 Bioware Corp. * Created By: Georg Zoeller * On: 2003-07-16
//------------------------------------------------------------------------------
// ChazM (OEI) 10/20/05 - added MODULE_SWITCH_ENABLE_SEPERATE_ITEM_SCRIPTS, GetEventPostfix(),
//					modified GetUserDefinedItemEventScriptName()
// ChazM (OEI) 4/4/06 - added support for 32 character script names for item scripts.
// BMA-OEI 8/21/06 -- Moved campaign flags from kinc_globals.nss
// ChazM 8/25/06 - Shortened campaign variable names to fit in 32 char limit.


//void main(){}

//------------------------------------------------------------------------------
//									C A M P A I G N
//------------------------------------------------------------------------------

// These are stored as globals - note that global names can only be 32 chars long.
// Force kills dominated group members if no valid members remain - checked on HeartBeat ( nw_g0_dominate )
const string CAMPAIGN_SWITCH_FORCE_KILL_DOMINATED_GROUP = "N2_S_KILL_DOM_GROUP";

// Removes effect and prevents transition if object is dominated - checked on transition ( ginc_transition )
const string CAMPAIGN_SWITCH_REMOVE_DOMINATED_ON_TRANSITION = "N2_S_REMOVE_DOM_ON_TRAN";

// This global determines whether or not the campaign uses the personal reputation system, which affects
//	whether or not neutrals can be damaged by spells
const string CAMPAIGN_SWITCH_USE_PERSONAL_REPUTATION = "N2_S_USE_PERSONAL_REP";


//------------------------------------------------------------------------------
//                                    M O D U L E
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// * Force Use Magic Device Skillchecks, Default = FALSE except for GAME_DIFFICULTY_CORE_RULES+
// * If switched to TRUE, a rogue has to succeed in a UMD check against DC 25+SpellLevel
// * in order to use a scroll. See x2_pc_umdcheck.nss for details
//------------------------------------------------------------------------------
const string MODULE_SWITCH_ENABLE_UMD_SCROLLS    = "X2_SWITCH_ENABLE_UMD_SCROLLS";

//------------------------------------------------------------------------------
// * Toggle on/off the Item Creation Feats, Default = O
// * Disable the Item Creation Feats that come with Hordes of the Underdark for the
// * module.
//------------------------------------------------------------------------------
const string MODULE_SWITCH_DISABLE_ITEM_CREATION_FEATS    = "X2_SWITCH_DISABLE_ITEMCREATION_FEATS";

//------------------------------------------------------------------------------
// * Toggle Area of Effect Spell behaviour
// * If set to TRUE, AOE Spells will hurt NPCS that are neutral to the caster if they are
// * caught in the effect
//------------------------------------------------------------------------------
const string MODULE_SWITCH_AOE_HURT_NEUTRAL_NPCS = "X0_G_ALLOWSPELLSTOHURT";

//------------------------------------------------------------------------------
// * For balancing reasons the crafting system will create 50 charges on a new wand
// * instead it will create 10 + casterlevel charges. if you want to be "hard core rules compliant"
// * 50 charges, enable thiis switch
//------------------------------------------------------------------------------
const string MODULE_SWITCH_ENABLE_CRAFT_WAND_50_CHARGES    = "X2_SWITCH_ENABLE_50_WAND_CHARGES";

//------------------------------------------------------------------------------
// * Some epic spells, namely Hellball, do damage to the caster. We found this too confusing
// * in testing, so it was disabled. You can reactivate using this flag
//------------------------------------------------------------------------------
const string MODULE_SWITCH_EPIC_SPELLS_HURT_CASTER = "X2_SWITCH_EPIC_SPELLS_HURT_CASTER";

//------------------------------------------------------------------------------// * Deathless master touch is not supposed to affect creatures of size > large
// * but we do not check this condition by default to balance the fact that the slain
// * creature is not raised under the command of the pale master.
// * by setting this switch to TRUE, the ability will no longer effect creatures of
// * huge+ size.
//------------------------------------------------------------------------------
const string MODULE_SWITCH_SPELL_CORERULES_DMASTERTOUCH = "X2_SWITCH_SPELL_CORERULE_DMTOUCH";

//------------------------------------------------------------------------------
// * By default, all characters can use the various poisons that can be found to poison their weapons if
// * they win a Dex check. Activating this flag will restrict the use of poison to chars with the UsePoison
// * feat only
//------------------------------------------------------------------------------
const string MODULE_SWITCH_RESTRICT_USE_POISON_TO_FEAT = "X2_SWITCH_RESTRICT_USE_POISON_FEAT";

//------------------------------------------------------------------------------
// * Multiple Henchmen: By default, henchmen will never damage each other with AoE spells.
// * By activating this switch, henchmen will be able to damage each other with AoE spells
// * and potentially go on each other's throats.
// * Warning: Activating this switch has the potential of introducing game breaking bugs. Do
// *          not use on the official SoU campaign. Use at your own risk. Really, its dangerous!
//------------------------------------------------------------------------------
const string MODULE_SWITCH_ENABLE_MULTI_HENCH_AOE_DAMAGE = "X2_SWITCH_MULTI_HENCH_AOE_MADNESS";

//------------------------------------------------------------------------------
// * Spell Targeting: Pre Hordes of the underdark, in hardcore mode, creatures would not hurt each other
// * with their AOE spells if they were no PCs. Setting this switch to true, will activate the correct
// * behaviour. Activating this on older modules can break things, unless you know what you are doing!
//------------------------------------------------------------------------------
const string MODULE_SWITCH_ENABLE_NPC_AOE_HURT_ALLIES = "X2_SWITCH_ENABLE_NPC_AOE_HURT_ALLIES";

//------------------------------------------------------------------------------
// * If set to TRUE, the Bebilith Ruin Armor ability is going to actually destroy
// * the armor it hits. Would be very annoying for players...
//------------------------------------------------------------------------------
const string MODULE_SWITCH_ENABLE_BEBILITH_RUIN_ARMOR = "X2_SWITCH_BEBILITH_HARDCORE_RUIN_ARMOR";

//------------------------------------------------------------------------------
// * Setting this switch to TRUE will make the Glyph of warding symbol disappear after 6 seconds, but
// * the glyph will stay active....
//------------------------------------------------------------------------------
const string MODULE_SWITCH_ENABLE_INVISIBLE_GLYPH_OF_WARDING = "X2_SWITCH_GLYPH_OF_WARDING_INVISIBLE";

//------------------------------------------------------------------------------
// * Setting this switch to TRUE will enable the allow NPCs running between waypoints using the WalkWaypoints
// * function to cross areas, like they did in the original NWN. This was changed in 1.30 to use only
// * waypoints in one area.
//------------------------------------------------------------------------------
const string MODULE_SWITCH_ENABLE_CROSSAREA_WALKWAYPOINTS = "X2_SWITCH_CROSSAREA_WALKWAYPOINTS";

//------------------------------------------------------------------------------
// * Setting this switch to TRUE will disable the glow of a newly found secret door
// * used in some locations in XP2
//------------------------------------------------------------------------------
const string MODULE_SWITCH_DISABLE_SECRET_DOOR_FLASH = "X2_SWITCH_DISABLE_SECRET_DOOR_FLASH";

//------------------------------------------------------------------------------
// * Setting this switch to TRUE will disable execution of tagbased scripts that are enabled
// * by default when using the standard module events (x2_mod_def_*)
//------------------------------------------------------------------------------
const string MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS = "X2_SWITCH_ENABLE_TAGBASED_SCRIPTS";

//------------------------------------------------------------------------------
// * Only applies if MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS is true.
// This switch determines which type of tagbased scrtipting to use 
// (used in module events g_mod_*, x2_s3_hitcastspell, and x2_inc_spellhook)
// FALSE = use X2 version wherein all item events are in 1 script.  	
// TRUE = use seperated scripts named "i__<2 letter postfix>"
//------------------------------------------------------------------------------
const string MODULE_SWITCH_ENABLE_SEPERATE_ITEM_SCRIPTS = "NWN2_SEPERATE_ITEM_SCRIPTS";


//------------------------------------------------------------------------------
// * Setting thsi switch to TRUE will enable the XP2 Wandering Monster System
// * for this module (if you are using the default rest script and you have set
// * up the correct variables for each area
//------------------------------------------------------------------------------
const string MODULE_SWITCH_USE_XP2_RESTSYSTEM = "X2_SWITCH_ENABLE_XP2_RESTSYSTEM";

//------------------------------------------------------------------------------
// * if this variable is set, the AI will not use Dispel Magic against harmfull AOE
// * spells.
//------------------------------------------------------------------------------
const string MODULE_SWITCH_DISABLE_AI_DISPEL_AOE = "X2_L_AI_NO_AOE_DISPEL";

//------------------------------------------------------------------------------
// * Setting this variable to TRUE on the module will disable the call to the
// * random loot generation in most creatures' OnSpawn script.
//------------------------------------------------------------------------------
const string MODULE_SWITCH_NO_RANDOM_MONSTER_LOOT = "X2_L_NOTREASURE";

//------------------------------------------------------------------------------
//                             M I S C
//------------------------------------------------------------------------------
const string MODULE_VAR_OVERRIDE_SPELLSCRIPT ="X2_S_UD_SPELLSCRIPT";

const string MODULE_VAR_TAGBASED_SCRIPT_PREFIX ="X2_S_UD_SPELLSCRIPT";

//------------------------------------------------------------------------------
// * Variable that holds the wandering monster 2da filename
//------------------------------------------------------------------------------
const string MODULE_VAR_WANDERING_MONSTER_2DA ="X2_WM_2DA_NAME";

//------------------------------------------------------------------------------
// * This variable allows to specify a % for NOT using dispel magic against AOEs
// instead fleeing
//------------------------------------------------------------------------------
const string MODULE_VAR_AI_NO_DISPEL_AOE_CHANCE = "X2_L_AI_AOE_DISPEL_CHANCE";

//------------------------------------------------------------------------------
// * Setting this variable to TRUE will cause the Combat Expertise/Improved Combat Expertise
// * modes to be disabled whenever a player is casting a spell.
//------------------------------------------------------------------------------
const string MODULE_VAR_AI_STOP_EXPERTISE_ABUSE = "X2_L_STOP_EXPERTISE_ABUSE";


//------------------------------------------------------------------------------
//                             C R E A T U R E S
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// * see x2_ai_demo for details
//------------------------------------------------------------------------------
const string CREATURE_VAR_CUSTOM_AISCRIPT = "X2_SPECIAL_COMBAT_AI_SCRIPT";

//------------------------------------------------------------------------------
// * Setting this variable on a spellcaster creature will make its spelluse a
// * bit more random, but their spell selection may not always be appropriate
// * to the situation anymore.
//------------------------------------------------------------------------------
const string CREATURE_VAR_RANDOMIZE_SPELLUSE = "X2_SPELL_RANDOM";

//------------------------------------------------------------------------------
// * Set to 1 to make the creature activate stealth mode after spawn
//------------------------------------------------------------------------------
const string CREATURE_VAR_USE_SPAWN_STEALTH = "X2_L_SPAWN_USE_STEALTH";

//------------------------------------------------------------------------------
// * Set to 1 to make the creature activate detectmode after spawn
//------------------------------------------------------------------------------
const string CREATURE_VAR_USE_SPAWN_SEARCH = "X2_L_SPAWN_USE_SEARCH";

//------------------------------------------------------------------------------
// * Set to 1 to make the creature play mobile ambient animations after spawn
//------------------------------------------------------------------------------
const string CREATURE_VAR_USE_SPAWN_AMBIENT = "X2_L_SPAWN_USE_AMBIENT";

//------------------------------------------------------------------------------
// * Set to 1 to make the creature play immobile ambient animations after spawn
//------------------------------------------------------------------------------
const string CREATURE_VAR_USE_SPAWN_AMBIENT_IMMOBILE = "X2_L_SPAWN_USE_AMBIENT_IMMOBILE";

//------------------------------------------------------------------------------
// * Set to 1 to make the creature immune to dispel magic (used for statues)
//------------------------------------------------------------------------------
const string CREATURE_VAR_IMMUNE_TO_DISPEL = "X1_L_IMMUNE_TO_DISPEL";

//------------------------------------------------------------------------------
// * Set this variable to 1 on a creature to make it walk through other creatures
//------------------------------------------------------------------------------
const string CREATURE_VAR_IS_INCORPOREAL = "X2_L_IS_INCORPOREAL";

//------------------------------------------------------------------------------
// * Set this variable to 1 - 6 to override the number of attacks a creature has based on its BAB
//------------------------------------------------------------------------------
const string CREATURE_VAR_NUMBER_OF_ATTACKS = "X2_L_NUMBER_OF_ATTACKS";

//------------------------------------------------------------------------------
// * The value of this variable (int) is added to the chance that a creature
// * will use magic in combat. Set to 100 for always, 0 for never
//------------------------------------------------------------------------------
const string CREATURE_AI_MODIFIED_MAGIC_RATE = "X2_L_BEH_MAGIC";

//------------------------------------------------------------------------------
// * The higher value of this variable, the higher the chance that the creature
// * will use offensive abilities in combat. Set to 0 to make them flee.
//------------------------------------------------------------------------------
const string CREATURE_AI_MODIFIED_OFFENSE_RATE = "X2_L_BEH_OFFENSE";

//------------------------------------------------------------------------------
// * The higher value of this variable, the higher the chance that the creature
// * will aid friendly creatures in combat. Not that helping usually degrades
// * the overall difficulty of an encounter, but makes it more interesting.
//------------------------------------------------------------------------------
const string CREATURE_AI_MODIFIED_COMPASSION_RATE = "X2_L_BEH_COMPASSION";

//------------------------------------------------------------------------------
// * This allows you to script items that enhance a palemaster's summoned creatures. You need
// * to put the name of a script into this variable that will be run on any creature called by
// * the pale master's summon undead ability. You can use this script to add effects to the creature.
// * You can use the OnEquip/OnUnEquip event hooks set this variable.
//------------------------------------------------------------------------------
const string CREATURE_VAR_PALE_MASTER_SPECIAL_ITEM = "X2_S_PM_SPECIAL_ITEM";


//------------------------------------------------------------------------------
// These constants define item messages that are routed to script files with
// the item tag's through the default XP2 module scripts.
//------------------------------------------------------------------------------
const int X2_ITEM_EVENT_ACTIVATE 		= 0;
const int X2_ITEM_EVENT_EQUIP 			= 1;
const int X2_ITEM_EVENT_UNEQUIP 		= 2;
const int X2_ITEM_EVENT_ONHITCAST 		= 3;
const int X2_ITEM_EVENT_ACQUIRE 		= 4;
const int X2_ITEM_EVENT_UNACQUIRE 		= 5;
const int X2_ITEM_EVENT_SPELLCAST_AT 	= 6;

const int X2_EXECUTE_SCRIPT_CONTINUE 	=0;
const int X2_EXECUTE_SCRIPT_END 		=1;

const int SCRIPT_MAX_STRING_LENGTH 		= 32;
const int SCRIPT_ITEM_EXTENSION_LENGTH 	= 3;

const string SCRIPT_EXTENSION_ITEM_EVENT_ONHITCAST 		= "_hc";
const string SCRIPT_EXTENSION_ITEM_EVENT_ACTIVATE		= "_ac";
const string SCRIPT_EXTENSION_ITEM_EVENT_EQUIP 			= "_eq";
const string SCRIPT_EXTENSION_ITEM_EVENT_UNEQUIP		= "_ue";
const string SCRIPT_EXTENSION_ITEM_EVENT_ACQUIRE		= "_aq";
const string SCRIPT_EXTENSION_ITEM_EVENT_UNACQUIRE		= "_ua";
const string SCRIPT_EXTENSION_ITEM_EVENT_SPELLCAST_AT 	= "_ci";
	
// Set the active User Defined Item Event
// X2_ITEM_EVENT_ACTIVATE
// X2_ITEM_EVENT_EQUIP
// X2_ITEM_EVENT_UNEQUIP
// X2_ITEM_EVENT_ONHITCAST
// X2_ITEM_EVENT_ACQUIRE
// X2_ITEM_EVENT_UNACQUIRE
// X2_ITEM_EVENT_SPELLCAST_AT
void SetUserDefinedItemEventNumber(int nEvent);

// Get the active User Defined Item Event
// X2_ITEM_EVENT_ACTIVATE
// X2_ITEM_EVENT_EQUIP
// X2_ITEM_EVENT_UNEQUIP
// X2_ITEM_EVENT_ONHITCAST
// X2_ITEM_EVENT_ACQUIRE
// X2_ITEM_EVENT_UNACQUIRE
// X2_ITEM_EVENT_SPELLCAST_AT
int GetUserDefinedItemEventNumber();

//------------------------------------------------------------------------------
// * Used to switch between different rule implementations or to subsystems for the game
// * see x2_inc_switches for more detailed information on these constants
//------------------------------------------------------------------------------
void SetModuleSwitch(string sModuleSwitchConstant,int bValue);

//------------------------------------------------------------------------------
// * Returns the value of a module switch
//------------------------------------------------------------------------------
int GetModuleSwitchValue(string  sModuleSwitchConstant);

//------------------------------------------------------------------------------
//                                D O O R S
//------------------------------------------------------------------------------
const string DOOR_FLAG_RESIST_KNOCK = "X2_FLAG_DOOR_RESIST_KNOCK";

//------------------------------------------------------------------------------
// * Used to toggle custom flags on a door
// * oDoor - Door to set the switch on
// * Valid values for sDoorFlagConstant:
// * X2_FLAG_DOOR_RESIST_KNOCK -
// *        Set to 1 to prevent knock from working with feedback.
// *        Set to 2 to prevent knock from working without feedback
//------------------------------------------------------------------------------
void SetDoorFlag(object oDoor, string sDoorFlagConstant, int nValue);
int GetDoorFlag(object oDoor, string  sDoorFlagConstant);

//------------------------------------------------------------------------------
//                           W A Y P O I N T S
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// * By setting this variable to 1 on a waypoint, a creature using this
// * waypoint as part of its WalkWaypoints routine will assume the facing
// * of the waypoint upon reaching it.
//------------------------------------------------------------------------------
const string  WAYPOINT_VAR_FORCE_SETFACING = "X2_L_WAYPOINT_SETFACING";

//------------------------------------------------------------------------------
//                           I T E M S
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// NOTE: THIS NO LONGER WORKS. TO PREVENT MODIFICATION USE THE PLOT FLAG
//------------------------------------------------------------------------------
const string ITEM_FLAG_NO_CRAFT_MODIFICATION = "X2_FLAG_ITEM_CRAFT_DO_NOT_MODIFY";
void SetItemFlag(object oItem, string sItemFlagConstant, int nValue);
int GetItemFlag(object oItem, string  sItemFlagConstant);

//------------------------------------------------------------------------------
// * Execute sScript on oTarget returning an integer.
// * Do not nest this function
//------------------------------------------------------------------------------
int ExecuteScriptAndReturnInt(string sScript, object oTarget);

//------------------------------------------------------------------------------
// * Sets the return value for scripts called via ExecuteScriptAndReturnInt
// * valid values are
// * X2_EXECUTE_SCRIPT_CONTINUE - continue calling script after executed scriptis done
// * X2_EXECUTE_SCRIPT_END - end calling script after executed script is done
//------------------------------------------------------------------------------
void  SetExecutedScriptReturnValue(int nValue = X2_EXECUTE_SCRIPT_END);

//------------------------------------------------------------------------------
// * This is a security feature. If you are running a *local vault* server and you
// * have tag based script execution enabled, people could bring items into your
// * game that execute existing scripts. You can set a script prefix here to
// * prevent that. Note that you have to add this prefix to your item scripts in
// * the module to make them work.
//------------------------------------------------------------------------------
void SetUserDefinedItemEventPrefix(string sPrefix="");

//------------------------------------------------------------------------------
//                          S P E L L S C R I P T S
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Allows the module creator to specify a script that will be run before any spellscript is run
// You can call SetModuleOverrideSpellscript() at the end of the script specified by
// sScriptName. If you call this function this will prevent the original spellscript
// (and all craft item code) from being executed.
// If you do not add this line, the original spellscript and/or crafting code will
// run in addition to your script
//------------------------------------------------------------------------------
void SetModuleOverrideSpellscript(string sScriptName);

//------------------------------------------------------------------------------
//                             C R E A T U R E S
//------------------------------------------------------------------------------

void SetCreatureFlag(object oCreature, string sFlag, int nValue);
int  GetCreatureFlag(object oCreature, string sFlag);

//------------------------------------------------------------------------------
// * Define a replacement script for DetermineCombatRound
// * See x2_ai_demo for details
//------------------------------------------------------------------------------
void SetCreatureOverrideAIScript(object oCreature, string sScriptName);

//------------------------------------------------------------------------------
// * Call this at end of your custom override AI script set via CREATURE_VAR_CUSTOM_AISCRIPT
// * See x2_ai_demo for details.
//------------------------------------------------------------------------------
void   SetCreatureOverrideAIScriptFinished(object oCreature = OBJECT_SELF);
void   ClearCreatureOverrideAIScriptTarget(object oCreature = OBJECT_SELF);
object GetCreatureOverrideAIScriptTarget(object oCreature = OBJECT_SELF);

//------------------------------------------------------------------------------
// * Define the name of the 2da file which is used for the wandering monster
// * system
//------------------------------------------------------------------------------
void SetWanderingMonster2DAFile(string s2DAName = "des_restsystem");



//----------------------------------------------------------------------------
// Interface to switch on / off specific  subsystems or behaviors
// Check X2_INC_SWITCHES.NSS for details
//----------------------------------------------------------------------------
void SetModuleSwitch(string sModuleSwitchConstant,int bValue)
{
    if (bValue == 0)
    {
        DeleteLocalInt (GetModule(),sModuleSwitchConstant);
        return;
    } else if ((sModuleSwitchConstant) == MODULE_SWITCH_AOE_HURT_NEUTRAL_NPCS && bValue == TRUE)
    {
      bValue = 10;
    }
    SetLocalInt (GetModule(),sModuleSwitchConstant, bValue);
}

//----------------------------------------------------------------------------
// Return the value of a module switch set by SetModuleSwitch
// See X2_INC_SWITCHES for a list of all module switches
//----------------------------------------------------------------------------
int GetModuleSwitchValue(string  sModuleSwitchConstant)
{
    int nRet =  GetLocalInt (GetModule(),sModuleSwitchConstant);
    return nRet;
}

void SetDoorFlag(object oDoor, string sDoorFlagConstant, int nValue)
{
    if (nValue == 0)
    {
        DeleteLocalInt (oDoor,sDoorFlagConstant);
        return;
    }
    SetLocalInt (oDoor,sDoorFlagConstant, nValue);
}

int GetDoorFlag(object oDoor, string  sDoorFlagConstant)
{
    int nRet =  GetLocalInt (oDoor,sDoorFlagConstant);
    return nRet;
}

void SetItemFlag(object oItem, string sItemFlagConstant, int nValue)
{
    if (nValue == 0)
    {
        DeleteLocalInt (oItem,sItemFlagConstant);
        return;
    }
    SetLocalInt (oItem,sItemFlagConstant, nValue);
}

int GetItemFlag(object oItem, string  sItemFlagConstant)
{
    int nRet =  GetLocalInt (oItem,sItemFlagConstant);
    return nRet;
}

void SetModuleOverrideSpellscript(string sScriptName)
{
    SetLocalString(GetModule(),MODULE_VAR_OVERRIDE_SPELLSCRIPT,sScriptName);
}

string GetModuleOverrideSpellscript()
{
     string  sScript = GetLocalString(GetModule(),"X2_S_UD_SPELLSCRIPT");
     return sScript;
}

//------------------------------------------------------------------------------
// You can call this in our overridden spellscript. If you call this
// this will prevent the original spellscript (and all craft item code)
// from being executed. If you do not add this line, the original spellscript
// and/or crafting code will run in addition to your script
//------------------------------------------------------------------------------
void SetModuleOverrideSpellScriptFinished()
{
    SetLocalInt(OBJECT_SELF,"X2_L_BLOCK_LAST_SPELL",TRUE);
}

int GetModuleOverrideSpellScriptFinished()
{
    int nRet = GetLocalInt(OBJECT_SELF,"X2_L_BLOCK_LAST_SPELL");
    DeleteLocalInt(OBJECT_SELF,"X2_L_BLOCK_LAST_SPELL");
    return nRet;
}

void SetCreatureOverrideAIScript(object oCreature, string sScriptName)
{
    SetLocalString(oCreature,CREATURE_VAR_CUSTOM_AISCRIPT,sScriptName);
}

void SetCreatureOverrideAIScriptFinished(object oCreature = OBJECT_SELF)
{
 //   WriteTimestampedLogEntry("Custom AI Finished");
    SetLocalInt(oCreature,"X2_SPECIAL_COMBAT_AI_SCRIPT_OK",TRUE);
}

object GetCreatureOverrideAIScriptTarget(object oCreature = OBJECT_SELF)
{
    object oRet= GetLocalObject(oCreature,"X2_NW_I0_GENERIC_INTRUDER");
    return oRet;
}

void ClearCreatureOverrideAIScriptTarget(object oCreature = OBJECT_SELF)
{
    DeleteLocalObject(oCreature,"X2_NW_I0_GENERIC_INTRUDER");
}

void SetCreatureFlag(object oCreature, string sFlag, int nValue)
{
    if (sFlag == CREATURE_VAR_IMMUNE_TO_DISPEL)
    {
        if (nValue != 0)
        {
            nValue = 10;
        }
    }

    SetLocalInt(oCreature,sFlag ,nValue);
}

int GetCreatureFlag(object oCreature, string sFlag)
{
    int nRet = GetLocalInt(oCreature,sFlag);
    return nRet;
}

//----------------------------------------------------------------------------
// Get the current UserDefined Item Event Number
// X2_ITEM_EVENT_ACTIVATE
// X2_ITEM_EVENT_EQUIP
// X2_ITEM_EVENT_UNEQUIP
// X2_ITEM_EVENT_ONHITCAST
// X2_ITEM_EVENT_ACQUIRE
// X2_ITEM_EVENT_UNACQUIRE
// X2_ITEM_EVENT_SPELLCAST_AT
//----------------------------------------------------------------------------
int GetUserDefinedItemEventNumber()
{
    return GetLocalInt(OBJECT_SELF, "X2_L_LAST_ITEM_EVENT");
}

//----------------------------------------------------------------------------
// Set the current UserDefined Item Event Number
// X2_ITEM_EVENT_ACTIVATE
// X2_ITEM_EVENT_EQUIP
// X2_ITEM_EVENT_UNEQUIP
// X2_ITEM_EVENT_ONHITCAST
// X2_ITEM_EVENT_ACQUIRE
// X2_ITEM_EVENT_UNACQUIRE
// X2_ITEM_EVENT_SPELLCAST_AT
//----------------------------------------------------------------------------
void SetUserDefinedItemEventNumber(int nEvent)
{
    SetLocalInt(OBJECT_SELF,"X2_L_LAST_ITEM_EVENT",nEvent);
}
	
		
string GetEventPostfix()
{
	string sPostfix = "";
	int nEvent = GetUserDefinedItemEventNumber();

	switch (nEvent)
	{
		case X2_ITEM_EVENT_ONHITCAST:
			sPostfix = SCRIPT_EXTENSION_ITEM_EVENT_ONHITCAST;
			break;
		case X2_ITEM_EVENT_ACTIVATE:
			sPostfix = SCRIPT_EXTENSION_ITEM_EVENT_ACTIVATE;
			break;
		case X2_ITEM_EVENT_EQUIP:
			sPostfix = SCRIPT_EXTENSION_ITEM_EVENT_EQUIP;
			break;
		case X2_ITEM_EVENT_UNEQUIP:
			sPostfix = SCRIPT_EXTENSION_ITEM_EVENT_UNEQUIP;
			break;
		case X2_ITEM_EVENT_ACQUIRE:
			sPostfix = SCRIPT_EXTENSION_ITEM_EVENT_ACQUIRE;
			break;
		case X2_ITEM_EVENT_UNACQUIRE:
			sPostfix = SCRIPT_EXTENSION_ITEM_EVENT_UNACQUIRE;
			break;
		case X2_ITEM_EVENT_SPELLCAST_AT:
			sPostfix = SCRIPT_EXTENSION_ITEM_EVENT_SPELLCAST_AT;
			break;
	}
	return (sPostfix);
}

//----------------------------------------------------------------------------
// Returns the name for the User Defined Item Event script for oItem,
// including possible prefixes configured by SetUserDefinedItemEventPrefix
//----------------------------------------------------------------------------
string GetUserDefinedItemEventScriptName(object oItem)
{
    string sPrefix = GetLocalString(GetModule(), MODULE_VAR_TAGBASED_SCRIPT_PREFIX);
	string sTag = sPrefix + GetTag(oItem);
	int iMaxPreExtensionLength = SCRIPT_MAX_STRING_LENGTH - SCRIPT_ITEM_EXTENSION_LENGTH;

	if (GetLocalInt(GetModule(), MODULE_SWITCH_ENABLE_SEPERATE_ITEM_SCRIPTS))
	{
	    if (GetStringLength(sTag) > iMaxPreExtensionLength)
		{
	        sTag = GetStringLeft(sTag, iMaxPreExtensionLength);
		}
	 	sTag = sTag + GetEventPostfix();
	}
    return sTag;
}


//----------------------------------------------------------------------------
// You can define a prefix for any User Defined Item Event here, to prevent
// people from executing scripts you do not like them to execute on your
// local vault server
//----------------------------------------------------------------------------
void SetUserDefinedItemEventPrefix(string sPrefix="")
{
    SetLocalString(GetModule(), MODULE_VAR_TAGBASED_SCRIPT_PREFIX, sPrefix);
}

//----------------------------------------------------------------------------
// Wrapper for Execute Script to execute a script and get an integer
// return value. Do not nest this function!
//----------------------------------------------------------------------------
int ExecuteScriptAndReturnInt(string sScript, object oTarget)
{
    DeleteLocalInt(oTarget,"X2_L_LAST_RETVAR");
    ExecuteScript(sScript,oTarget);
    int nRet = GetLocalInt(oTarget,"X2_L_LAST_RETVAR");
    DeleteLocalInt(oTarget,"X2_L_LAST_RETVAR");
    return nRet;
}

//----------------------------------------------------------------------------
// Helper function for ExecuteScriptAndReturnInt
//----------------------------------------------------------------------------
void  SetExecutedScriptReturnValue(int nValue = X2_EXECUTE_SCRIPT_CONTINUE)
{
    SetLocalInt(OBJECT_SELF,"X2_L_LAST_RETVAR",nValue);
}

//----------------------------------------------------------------------------
// Define the name of the 2da file which is used for the wandering monster
// system
//----------------------------------------------------------------------------
void SetWanderingMonster2DAFile(string s2DAName = "des_restsystem")
{
    SetLocalString(OBJECT_SELF,MODULE_VAR_WANDERING_MONSTER_2DA,s2DAName);
}