NWN:EE Script Editing Tutorial

From NWN Lexicon
Revision as of 13:35, 5 September 2020 by Jasperre (talk | contribs) (Intellisense Setup)
Jump to: navigation, search

This is a short guide on how to get setup with NWN:EE scripting with Visual Studio Code and some other helpful in-game elements. Written by Jasperre / Finaldeath on Discord. Lots of this was taken from an existing tutorial from OldFriendlyFire, and various notes from Discord, forum posts, tool readmes and the NWN wiki.

For NWN:EE patch listings see the Patches page. For returning scripters, to find the NWN:EE specific functions they are categorised here.

If you need additional help it would be worth asking on the Beamdog Forums, NWVault Forums or NWvault Discord.

NWN Scripting Intro

A brief introduction, NWN Scripting is a c-like programming language. It has several limitations, but for most simple module scripts it works as a suitable interface to do things in the game, from conversation actions, quest tracking and opening chests and having a custom trap go off, to AI programming, complex multi-chain quests, cutscenes, and multiplayer systems.

You can usually open the toolset and edit any scripts there. In this interface you'll find a limited IDE, including search functionality (which helpfully searches include files) and a compiler. It was made a long time ago and even at the time editing the scripts outside the editor wasn't unusual.

The game has a file called nwscript.nss which we need to extract to use for some of the parts of this guide (primarily the intellisense item, the compiler does not need this). This file is the defined in-game functions, not custom ones.

In NWN:EE you can helpfully find the latest version of this file, as of writing, as an override file under the game directory, eg: Steam\steamapps\common\Neverwinter Nights\ovr. Or see the Advanced section for how to extract the game scripts.

Remember where nwscript.nss is for later, maybe take a copy to your desktop now.

Visual Studio Code Setup

Visual Studio Code is a free IDE for coding. Install it now!

The benefits to NWscripters over the toolset is:

  • Modern IDE with all that entails (dark mode, highlighting, multi-window)
  • No limitations saving/loading lots of stuff at once
  • Usable search
  • Easy to setup and work in
  • Can have open at the same time as a module area

There are several things to do to improve this tooling:

  • Sort a workspace
  • Sort highlighting
  • Sort a compiler
  • Sort intellisense

Workspace Setup

A workspace is a collection of folders loaded into VSCode in one go and available in the workspace management panel on the left.

This is going to differ a lot between peoples needs so be wary about following this exactly.

To create a new workspace folder to the default empty project, go File -> Add Folder To Workspace.

After this save the workspace by going File -> Save Workspace As.

Ignoring Certain Filetypes

Whatever instructions below you follow, you'll have problems if you keep seeing compiled scripts and all sorts of other cruft from module folders in your workspace. We just want .nss files (and maybe .txt files) really!

Go to Preferences -> Settings. On the options select Workspace. Search for Exclude. This is the file types the editor will ignore for your project. Click aAdd Pattern to add these in, or edit using the settings JSON file if you know how to do that.

If you plan to only have a folder open with .nss and compiled scripts just use the top two entries (ncs and ndb).


Folder Setup

The main thing is to setup a new folder somewhere. Once the folder is setup files need to populate it. The easiest way to do this is to open a module with scripts in it, and take them out. When you load a module in the toolset go to the /modules folder and look for the "temp0" folder. This contains all the unpacked files from the module.

Scripts are always scriptname.nss (remember there is a limit of 16 characters on the name!). Copy the files you want to your workspace folder.

This tutorial will deal with having the files copied to and from the module folder, and the use of the development folder for very quick in-game edits.

Highlighting Setup

This is easy to do, you can find extensions in the marketplace. Inside the IDE click the extensions panel and search "nwscript":


Once installed your scripts now have appropriate colours for most keywords. Before:




Intellisense Setup

Now we want to make sure functions we define are returned nicely in the intellisense popups. By default VScode does it's best, but only looks in the current file which doesn't provide us any context:


First of all create a User Snippets file. In my example I'll be calling it nss since that's the extension NWN scripts use. It'll be applied to the current workspace, but this is easily portable to new workspaces (these settings files, like the ignore settings above, are stored in files within the workspace itself).

Go to File -> Preferences -> User Snippets. Select the option "New Snippets File for WORKSPACENAME", and enter a new name - I'm calling mine "nss". This generates an empty "nss.code-snippets" file and opens it.

We are going to use NSSnippets to complete this snippets file.

Download it, and place it in the workspace root directory. Also place the nwscript.nss file here. You'll also be putting the compiler here as well. Of course anyone who wants to put them elsewhere can, but you'll have to edit the various tasks to fit. For instance this is my final working folder:


Note: notes.txt, 2.0_module_test_version and Other are all my own files. The .vscode folder contains all our workspace config files.

To sort the tool to run, you need to have it run as a task. For now I'm going to setup this, and the 2 tasks for the compiler, as the default F9 run commands.

Click Terminal -> Configure Tasks. It should bring up a new, rather empty, tasks.json file.

We want to add this configuration to it, the final file will look a little like this:

    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
            "label": "NSSnippets",
            "type": "process",
            "args": [
            "command": [
            "presentation": {
                "reveal": "never"
            "problemMatcher": []

Save this file now. If there are any errors check them out in the bottom panel.

We also need to bind a key to run commands. We'll use F9. You can setup your own favourite shortcuts yourself :)

Go to File -> Preferences -> Keyboard Shortcuts. Find the little icon in the top right that is for keybindings JSON, and add this:

// Place your key bindings in this file to override the defaults
        "key": "F9", "command": "workbench.action.tasks.runTask"

You could add the parameter , "args": "NSSnippets" to run just that particular task. Maybe you've got a load of keys you can rebind all your little tasks :)

To finish press F9 and select NSSnippets while opening a file of code, it generates it for that one plus any includes in that file, meaning you can now see some more useful snippets, ergo:


If you're wanting the contents of all your working files for a single enclosed project, you could arrange to have one .nss file that includes everything else (eg: call it nssgen.nss), and have the config point to that file to generate the intellisense help from (change the line "${file}" to "${workspaceFolder}\\nssgen.nss" to make that always be the choice).

You can also run this from Powershell or CMD outside of VSCode, but it's super handy to run inside it.

If you want the intellisense window to be larger, edit this file and add the below entries: C:\Program Files\Microsoft VS Code\resources\app\out\vs\workbench\workbench.desktop.main.css

   /* suggest-widget size */
       .monaco-editor .suggest-widget.docs-side {
           width: 1600px;
       .monaco-editor .suggest-widget.docs-side > .details {
           width: 70%;
           max-height: 800px !important;
       .monaco-editor .suggest-widget.docs-side > .tree {
           width: 30%;
           float: left;
   /* parameter-hints-widget */
       .editor-widget.parameter-hints-widget.visible {
           max-height: 800px !important;
       .monaco-editor .parameter-hints-widget > .wrapper {
           max-width: 1600px;
   /* editor-hover */
       .monaco-editor-hover .monaco-editor-hover-content {
           max-width: 1600px;

Note: this will disappear after every vscode patch (and will complain it's changed).

NWN External Script Compiler Setup

(Thanks to OldFriendlyFire's tutorial as the basis for this.

We are going to add 2 tasks to have a compiler run on the current open script, and the entire workspace folder you have open the script is in.

The compiler helps point out obvious problems with script formatting, and compiles nice .nsc files (compiled .nss files) the game can read right away. If you use the developer folder mentioned at the end of this page the compiler is key in getting files to that folder.

Download the latest release of the compiler here. This is setup for 1.1.1 so if it is newer than that read the release notes.

Once downloaded place the exe in your main workspace directory.

Now edit the tasks.json for your workspace to add in the option to run the compiler. There are two I've added to mine, one for "compile one script" and one for "compile all scripts". As it is JSON you should realise where this goes, after the NSSnippits task but before the last square bracket.

You will need to alter one line: the link to where your NWN install is. Mine is set to D:\\Steam\\steamapps\\common\\Neverwinter Nights but replace it with a full path (escaping those path slashes) to your NWN top level folder. It can work it out from there.

The rest of the options are tuned for showing errors as a new task each time - this is because the compiler is "dumb" and doesn't override past results if run in the same terminal (for whatever reason). If you notice at the bottom of the screen the terminal sessions tend to mount up over time, these can be closed safely and don't use up much memory.

           "label": "NWNSC Script Compile File",
           "type": "process",
           "args": [
               "D:\\Steam\\steamapps\\common\\Neverwinter Nights",
           "problemMatcher": {
               "fileLocation": [
               "pattern": {
                   "regexp": "(.*?(?=(?:\\()|$))\\((.*?)\\): (Warning|Error): (.*)",
                   "file": 1,
                   "line": 2,
                   "severity": 3,
                   "message": 4
               "owner": "nwnsc"
           "command": [
           "presentation": {
               "clear": true,
               "reveal": "always",
               "panel": "new"
           "group": {
               "kind": "build",
               "isDefault": true
           "label": "NWNSC Script Compile Everything in Folder",
           "type": "process",
           "args": [
               "D:\\Steam\\steamapps\\common\\Neverwinter Nights",
           "problemMatcher": {
               "fileLocation": [
               "pattern": {
                   "regexp": "(.*?(?=(?:\\()|$))\\((.*?)\\): (Warning|Error): (.*)",
                   "file": 1,
                   "line": 2,
                   "severity": 3,
                   "message": 4
               "owner": "nwnsc"
           "command": [
           "presentation": {
               "clear": true,
               "reveal": "always",
               "panel": "new"
           "group": {
               "kind": "build",
               "isDefault": true

The script compiler when run will stop when it hits some kinds of errors, so make sure you re-run it to find further errors. It will default to the "terminal" tab of the task when run, which is good for seeing when it works, but click the "Problems" tab and you'll see red on any error files and orange for any warning files.

Syntax error.PNG

You can click on an error to jump to it and it highlights the issue line. Now you need to sometimes look around it to find the real error - in this case "TryCastin" from my earlier NSSnippit test wasn't completed, so it doesn't know what to do when there are no operators or brackets after it, and it isn't a declared variable, so it actually errors the line after - ActionUseFeat() in this case.

Syntax error 2.PNG

Finally fixing the error, in my case deleting the rogue line, allows the compile to complete

Compile finished.PNG

Of course as with NSSnippits you can run the compiler using any method - Powershell, CMD, on other operating systems like Mac and Linux external to any toolchain. These are just ways of having it show the errors nicely inline in VSCode.

Advanced additions

Here be dragons! ...or helpful tips to work on things faster. Still be careful!

Associate .mod files with the toolset

To open module files quickly you can setup them to open with nwtoolset.exe. This means shortcuts to them can be created to load automatically in the toolset. Note it'll use whatever default user directory the game uses by default.

Module Open Folder

In NWN:EE when the toolset opens it generates a "temp0" folder in the modules folder. This can be renamed to be the same as the .mod filename and the toolset will use this to load from instead of unpacking each time.

For instance, create a new module called "my_mod" it generates a new temp0 folder, a my_mod.mod and my_mod.BackupMod file when it is saved. If you rename the temp0 folder to be exactly "my_mod" and go to Open it again, it will popup with a prompt; "A directory with the same name has been found. Do you want to open this directory as a module?" answer Yes and it doesn't need to unpack the .mod file.

This can be very helpful for speed, and for the ability to pack the files off to a repository (see later for some options).

Using DM Mode / Reloading an active module

Right click the game in Steam and select Dungeon Master Client. This essentially runs a multiplayer server, but just make sure to not advertise on the internet and it'll basically be a local game mode.

You can run most tests easily as a DM since the DM character still is pretty much GetFirstPC() for script purposes. They won't however generally trigger encounter triggers or traps and the AI (should) ignore them.

Being a DM makes you able to teleport, run fast, and cast any spell/heal/etc and see all combat info in the log. DM tools allow you to spawn things by blueprint, and also possess monsters to check they work fine/have the correct setup (which also can emulate a player if you want to test traps/encounters), they can toggle the plot "invincibility" flag, and make things immortal, and show the game triggers which can be very useful in some cases.

To restart a module while in game, rather than having to reload the module all the time, use this in a script, eg on a action node in a conversation. You can use GetName(GetModule()) if the module name is identical to the file name. You can also create a command line to the module you want to load from desktop: nwnmain.exe +TestNewModule "mod file name" (you can also run DM mode but this can't be used at the same time: -dmc). The bottom of this guide has other command line options for directly loading a module.

// Restarts this module
void main()
    SendMessageToPC(GetFirstPC(), "Restarting Mod in 5 seconds");
    string sModule = "MODULE FILE NAME HERE";
    DelayCommand(5.0, StartNewModule(sModule));

DM mode automatically makes you able to use debug commands from the console:

Developer Console

Press ` (grave accent/backtick key to the left of 1) to open the debug console. Or type a command using a two hash prefix into chat (or assign as a chat command).

To enable most "cheat" things you might need, type DebugMode 1 (it is case sensitive), or ##DebugMode 1 in the chat (you can assign this to a chat macro and export your character to save it for later).

You can put in then most of console comamnds (this NWN wiki page may be incomplete especially for NWNEE!). The DM client allows access to a few more noted on that page.

For scripters this can be useful primarily for two commands:

dm_runscript - eg: 'dm_runscript hello runs hello.nss if it is compiled. You won't see feedback on success or not unless your script gives feedback.

dm_dumplocals - If you have combined chat windows, it will print out all the variables and their type from a selected object. You can run dm_dumparealocals and dm_dumpmodulelocals for similar current-area and module variables.

For AI developers dm_enablecombatdebugging may also be useful, it prints all the hidden "shouts" the AI sends to each other.

Finally there are many others, you can use tab to view them all from the menu. Some custom ones for pathfinding for instance are rendertilepathnodes 1 to show how each tile links up and renderaabb 1 to show a "pathfinding view" where it strips terrain and colour codes walkable areas. Useful in some cases.

Developer Menu

Press CTRL + SHIFT + F12 to toggle this menu. It contains ways of editing the game settings cleanly in real time. A number of resource reloading tools and other things are available here. Close it with the same combo.

For scripters the main thing is checking what is loaded in the developer folder. Check udner "Resman" then open "Development". You can see how many things are detected there. You can rebuild anytime but it should auto detect changes.

Bioware NWN Script Files

So you need an example of how to use some function, but the NWN scripts are not easily accessible from your Workspace. Search only returns perhaps the nwscript.nss you added earlier, a basic description of the function.

You might also want to use an include file but don't want to keep fetching them one by one through the NWN toolset. There are over 4000 Bioware script files included in the game!

To extract the game files, use an updated NWN Explorer for NWN:EE. Then load the game files as per the readme, and look for \NWN Main Data\data\base_scripts.bif

You can then extract the folder somewhere suitable, either into your workspace main folder, or if anywhere else you can use Add Folder To Workspace to make it accessible.

You can also then copy files you wish to include for intellisense to your main folder. See the NSSnippets page for more info on why this is required.

I'd also recommend making the entire folder read-only so you don't accidentially start editing the files, VSCode helpfully points out you can't but still allows them to be searched easily.

Developer Folder

Here be dragons! Don't use this outside of development.

The development folder is in your NWN Documents folder, eg: C:\Users\NAME\Documents\Neverwinter Nights\development

It will act like a total live override folder. It takes precedence over the usual load order for scripts, which is with this folder now:

  • development folder in My Documents
  • module
  • hak file (which may have it's own order)
  • user override folder in My Documents
  • game overide folder
  • game bif (default) script folder (which has it's own order too)

If you are careful you can either use it as a working directory in your workspace, or symlink it to your actual working folder.

A module won't read this folder however for new scripts to show up, so you still have to copy any new scripts to the module to link them to conversations/event calls/whatever else. However once setup this can allow direct manipulation of scripts as they run in the game.

Basically compiled files in development folder -> read by the game when updated -> live script changes. A heartbeat script will run the old version (or module version) then pick up an updated nameofscript.nsc file the next time it is called.

However this is not to be used to release mods and will break a lot of stuff if ever used outside your own computer!

Combining with ExecuteScriptChunk

It can also be combined with ExecuteScriptChunk and the dm_runscript console command for live script edits.

Call foo.nss using trace dm_runscript foo - trace will run the command every frame so be careful. As per the example below it detects a changed int - in a non-compiled script - called from a the script that does run every frame. This means you can quickly iterate - without even compiling the scripts.

// foo.nss, compiled once, foo.ncs in development/
void main() {
    ExecuteScriptChunk("#include \"bar\"\n", OBJECT_SELF, FALSE);
// bar.nss, never compiled, just the source in development/
const int version = 1; // change when you want to run the script
void main() {
    if (GetLocalInt(OBJECT_SELF, "version") == version) return;
    SetLocalInt(OBJECT_SELF, "version", version);
    // actual code here

PrintString On Screen Debugging and Writing Logs

Please for debugging consider the use of PostString. This allows clean debug in-game, without using the very limited chat interface. You can also use WriteTimestampedLogEntry or PrintString which go in the file \Documents\Neverwinter Nights\logs\nwnclientLog1.txt

I find you sometimes need to use both - the logging is good for large amounts of repeating loops you want to check, or debugging some information en masse. The PostString is a better replacement to SendMessageToPC() to quickly get some on-screen feedback. My simple debug function below can be the basis for your own, it makes sure first of all the object calling this has the word "test" in the tag as to not keep having it overriden with multiple things using PostString(). You might need to remove that line to make it always run.

In a large script I make bNewColour be TRUE, which for the next lot of debug changes the colour, eg; at the start of a complicated function. You could also put instead a colour field or somesuch, but this keeps it a lot simpler to manage without having to remember more parameter names.

// Start a little from the top so we can use the dev console/DM console
int DebugX = 3;
const int nColourGreen = 0x00FF00FF;
const int nColourRed = 0xFF0000FF; 
const int nColourBlue = 0x00FFFFFF; // Cyan for readability
const int nColourYellow = 0xFFFF00FF;
const int nColourBlack = 0xFFFFFFFF;
int nColourNo = 1;
// 2.0 debug! fancy. This throws more lines in the top left. Only usable by one single creature.
// Note bNewColour cycles colours to make sections.
void Debug(string sDebug, int bNewColour=FALSE)
    if(FindSubString(GetTag(OBJECT_SELF), "test"))
        int nColour;
        if(bNewColour == TRUE)
            if(nColourNo == 5) { nColourNo = 1; } else { nColourNo++; }
        if(nColourNo == 1)  {
            nColour = nColourGreen;
        } else if(nColourNo == 2)  {
            nColour = nColourRed;
        }  else if(nColourNo == 3)  {
            nColour = nColourBlue;
        }  else if(nColourNo == 4)  {
            nColour = nColourYellow;
        }  else if(nColourNo == 5)  {
            nColour = nColourBlack;
        // PostString with no fade, it will disappear after 12 seconds or unless overriden.
        PostString(GetFirstPC(), sDebug, 0, DebugX, SCREEN_ANCHOR_TOP_LEFT, 12.0, nColour, nColour);
        DebugX += 1;

NWscript Debugger

Thanks to Taro94 for this section.

You can debug NWN nss scripts. It's sure not pretty, and for most users PostString will be a lot easier to debug things, but may be useful to some.

NWN:EE doesn't include the Bioware DebugServer.exe file but 1.69 does. Use a copy from 1.69 or this NWvault copy.

You need to create a folder called "utils" in the path just above the main game exe files. For instance: D:\Steam\steamapps\common\Neverwinter Nights\bin\win32\utils

Next step: go to your NWN:EE's user directory (not the installation folder), open "settings.tml" and find this part:

Debug server tml.png

You need to change it to enabled = true, it is usually false. The Default is port 5122. You shouldn't need to change this but if you do, change it now.

Then we need to generate the debug files. Use the script compiler referenced in this guide with the flag -g which is "Enable generation of .ndb debug symbols file". Or you can enable it in the toolset using this checkbox:

Debug server script editor.PNG

Once ticked it will generate .ndb files alongside the .ncs versions. Both are needed.

You then need a way to spawn in the debugger! This is the function SpawnScriptDebugger. As noted in the description of the function do not use this needlessly or without the debugger present, it just slows things down.

Now start the debugger application, and use the port suggested above. It looks like this when opened and must be set to "On". The game should be able to open it by itself mind you when it hits SpawnScriptDebugger.


Now run your module, from the toolset or in game, with the debugger in the background. I recommend running it in windowed mode. On 1.69 set the Fullscreen=0 and AllowWindowed=1 options. In NWN:EE you can press ALT+Enter.

Once it hits the given SpawnScriptDebugger start point the window should pop up with the debugger allowing you to view all the variable and step through the script.


However: Jasperre wasn't able to get this working on NwN:EE v80.8193.14 on Windows 10 in any way (while 1.69 Diamond Edition worked fine with the debugger), while Taro84 got a NwN:EE version working on Linux through WINE just fine. Weird stuff.

Repository Management

I'm no expert here, I'll just point out some options;

  • nasher.nim is the most up to date tooling to get repositories from modules or scripts
  • nwn-devbase is an older set of tooling that does similar management

There may be others, check around on github of the NWvault Discord for help.

Game Launch Options

Short reference of command line options (this won't be necessarily up to date) with the most helpful ones for builders:

nwtoolset.exe - remember, associating the .mod file type with it allows shortcuts to modules that open directly in the toolset.

  • You can set the user directory (on Windows usually in My Documents folder called "Neverwinter Nights"). Can be helpful if you want to load a specific set of hakpack files or other files for a particular mod. Of course the .mod file should be in the relevant /module folder: nwtoolset.exe -userdirectory "C:\Path"
  • You can pass the full path and name of a module file and it will load it directly: nwtoolset.exe "C:\folder\module name.mod"

The game has a number of helpful shortcut options:

  • Start the Dungeon Master Client: nwmain.exe -dmc
  • Set the user directory similarly to the nwntoolset version above: nwmain.exe -userdirectory "C:\Path"
  • You can load a module and be at the Character Select screen (to pick a specific character instead of the first), just use the module base filename: nwmain.exe +LoadNewModule "module name"
    • Tip; to just reach the Load Other Module page, leave the module name blank - it skips all intro videos
  • You can load into a module the same way F9 in the toolset does, just use the module base filename. This will load the module specified and choose the first character (alphabetically by filename in /localvault) to load in with: nwmain.exe +TestNewModule "module name"

nwserver runs the headless server version of the game and you may need this to test MP or PW modules. You can find most of the information about these parameters online, and most should be very obvious or repeats of the above:

nwserver [-module <name>] [-load <slot#> <savename> <module>] [-maxclients #] [-minlevel #] [-maxlevel #] [-pauseandplay 0/1] [-pvp 0/1/2] [-servervault 0/1] [-elc 0/1] [-ilr 0/1] [-gametype #] [-oneparty 0/1] [-difficulty #] [-autosaveinterval #] [-playerpassword <password>] [-dmpassword <password>] [-adminpassword <password>] [-servername <name>] [-publicserver 0/1] [-reloadwhenempty 0/1] [-port #] [-nwsyncurl <url>] [-nwsynchash <hash>] [-nwsyncpublishhaks] [-quiet] [-interactive] [-userdirectory <dir>]