How have you been doing? The Beamdog team has been hard at work on the next Neverwinter Nights: Enhanced Edition patch! Today, we are shipping release 8193.31 to the development branch of NWN:EE.
This is a huge beta patch, with many new and exciting features. We appreciate your assistance in helping us test them, and reporting any/all issues you find.
New Curated Content: A Hunt Through The Dark, by Markus Schlegel.
Scriptable UI.
Apple M1.
Many other new features, script commands & fixes.
Renderer Improvements
Many improvements have been made to the rendering and shadows engine:
Greatly reduced the amount of CPU time spent on rendering shadows (50-90% less) by moving more work to the shaders and just doing things more intelligently.
Reduced buffer transfers related to rendering shadows (perf+)
Fixed some common shadow rendering issues related to "behind" stencil tests. It does not fix every issue, however, and in rare cases, some issues may become more apparent - but all in all, the benefits should outweigh the drawbacks. The old approach can be restored with using "shadowfliporder 1" in the console.
Shadow and beam volumes now use their own unique shaders.
Fixed a number of other shadow rendering issues by doing view frustrum clipping in the vertex shaders. This (beyond improving performance) alleviates issues with shadows sometimes being cut off in odd places (and similar).
The game now uses tiledata bounding box to determine lower clip of shadow volumes. This fixes shadows not being applied to geometry below the z position of the tile.
The game now adjusts shadow alpha based on the vertical distance between the shadow plane used for drawing projections and the projection source. This gives a more correct shading.
Removed the “experimental optimized shadow rendering” toggle. It is now always enabled.
Added some minor refinements that help reduce the visual oddities caused by shadows being projected by erroneous models.
Fixed dynamic shadow rendering performance dropping significantly on certain devices due to the new streaming setup.
Added new debug outputs that help content artists to debug models from within the game. This includes rendering mesh bounding boxes, pivots (centers), emitter and light ranges, as well as shadow volumes. The options are accessible through the normal debug panel (ctrl+shift+f12).
Optimized generation of shadow volumes to better fit the size of the shadow plane used for rendering shadows. Improves performance in GPU limited situations.
Fixed an issue that made shadows fade out too quickly at low view angles.
Fixed an issue with the scene manager that made geometry culling less effective.
Made the game automatically downsize textures larger than the client GPU supports (instead of just failing to render).
Optimized the size of the baked font textures to minimize texture memory footprint.
Tweaked the subsurface light algorithm and made the normal debug outputs also apply for water.
Removed some redundant GPU data buffer uploads.
Fixed soft particles reading the screen depth at an offset position, resulting in occasional halos appearing around objects.
Fixed automatic tile texture rotation (`rotatetexture 1`) causing normal and displacement maps to malfunction.
Added a new console command ‘printvertexdata’. Prints an overview of the amount of memory currently used for vertex data (model geometry) to the client log.
Fixed rendering unlit models with envmap (chargen colour picker shader issue; Halaster model issue).
Fixed object view culling not respecting visual transforms, resulting in objects sometimes not appearing if transformed into view.
Fixed some static parts being removed after having been auto-combined, resulting in erroneous shadows for some static geometry.
Fixed skinmesh bone hierarchy to initial model layout. This solves certain issues caused by skinmeshed bodyparts being dynamically attached and removed.
Fixed low quality render mode darkening water too much.
Curated Content: A Hunt Through The Dark
by Markus Schlegel
Presenting the classic six-part campaign by Markus Schlegel - A Hunt Through The Dark!
Did you ever want to play the dark side, the bad, the evil, the worst of all, the drow? Take part in a hunting trip through the Underdark. It is on you to enslave goblins, betray comrades and spoil the plans of your leader Mistress Piwiewien – or not.
Scriptable UI
Modules can now spawn custom UI panels, either defined in NWScript or JSON datafiles.
The layouting is done via a constraints solver and currently presents as a row/layout flow.
Most native widget types available, including lower-level drawing primitives to create your own.
Data binding is two-way between clients and the module, and allows synchronising sliders, input fields, buttons, and window geometry/properties. Mouse input events are supported as well.
There is a new include file: nw_inc_nui.nss. There is also a demo script (nw_nui_demo.nss) that showcases some basic widget types. Finally, there is a more complex inspector example (nw_nui_insp.nss) that can show bind values for all open scripted windows.
See the mentioned include file and demo for starting points. The new nwscript.nss API is reproduced at the end of this document.
NWScript JSON Datatype Support
NWScript now has a new native datatype, `json`. The API is documented at the end of these notes.
The engine can also serialise game objects to/from json, similar to how the sql data binder works. There is a new data format “.caf” for area objects that combines .are and .git, so you can de/serialise areas into a single json object. The serialisation format is compatible with neverwinter.nim.
There is a small helper library in nw_inc_gff.nss so you can construct GFF data on the fly (e.g. dynamic area/object creation and spawning).
The API also supports JSON Pointer, Diff, Patch and Merge. The SQLite API has been amended for both Campaign DB and custom databases.
Other Features
Added more GUI events, some suppressible, when the player interacts.
Added device property queries to get player screen width height and scale.
Added VM commands to query the game resource manager.
PLT textures can now be phenotype-specific.
Added a setting that allows players to control their non-PC party members in the same way as DMs do (drag, ctrl+click, etc). This is off by default, and can be enabled in server settings.
Mac: Provided universal binary with a build for Apple M1. If you are on M1 and on Steam, you will have to launch the game directly, NOT via Steam, for the binary to run natively. Otherwise, it will run via Rosetta.
Mac only: GOG Galaxy SDK no longer supported on x64 and arm64 (no native M1 build for the library).
Mac only: Steam SDK/Workshop not supported on M1 (no native M1 build for the binary).
Toolset: Added support for custom caster classes.
Curated Content UI: Added support for promotion panels to show links to the Vault, Wikis, etc.
Smaller Changes
Script compiler now supports `\xFF` style escape sequences to put arbitrary bytes into a string literal. NB: \x00 will terminate the string, do not use.
Cleaned up ovr/, moved everything except scripts into keybif.
Debug UI: You can now use the RunScript/Chunk Debug UI in SP even when DebugMode is off (for testing convenience).
VM: Store/RetrieveCampaignObject, SqlBindObject, SqlGetObject, ObjectToJson, and JsonToObject now support an additional parameter; bSave/LoadObjectState; which handles local variables, effects, action queue and transition info (for doors and triggers).
Updated game credits for 2021, moved button out of OC Campaign to top level Movie menu.
Fixes
Game: Fixed savegames causing creatures in areas re-triggering the OnAreaEnter event. This was causing HotU chapter 3 to malfunction after saving and loading.
Game: Fixed a rare crash when doors were melee-attacked by a null creature (PW-specific crash).
Game: Fixed not storing fog clipping distance for areas when saving games.
Game: Fixed a crash when showing a polymorphed creature on the party bar.
Game: Fixed Steam Workshop modules not showing up if the .mod extension wasn’t all lowercase.
Toolset: Fixed inconsistencies with shadow rendering.
Toolset: Fixed second story tile fade in “Always” mode not working correctly.
Toolset: Areas are now considered modified when undo stack is changed
Toolset: Undo now works for mouse wheel object scaling
Toolset: Undo now works for Adjust Location dialog
Toolset: Fixed a crash when right-clicking a tile with a recently-selected creature on it.
Toolset: Fixed Replace All not working in backwards search mode.
VM: Fixed crash in CreateArea() due to heap-use-after-free
VM: Fixed a crash when SetResourceOverride was targeting a missing/invalid resource.
VM: Fixed a memleak every time CopyObject, CreateObject or CreateArea was called.
VM: Area serialisation: Do not save item state twice to GFF (this would needlessly duplicate temporary effects and local vars into savegames and serialised objects).
Framelimiter: Opening up the radial menu is no longer considered background/modal.
Nui: Fixed nuklear receiving input meant for the console.
Nui: Fixed nuklear eating mouse clicks that should go to the radial menu instead.
Tyrants of the Moonsea
Prevented use of the map inside the navigation cabin after traveling to a destination.
Covered some poor camera rotations when transitioning out of the sea map.
Removed an xp exploit in Ulblyn's conversation.
NWScript API Additions
int GUIEVENT_COMPASS_CLICK = 15;
int GUIEVENT_LEVELUP_CANCELLED = 16;
int GUIEVENT_AREA_LOADSCREEN_FINISHED = 17;
int GUIEVENT_QUICKCHAT_ACTIVATE = 18;
int GUIEVENT_QUICKCHAT_SELECT = 19;
int GUIEVENT_QUICKCHAT_CLOSE = 20;
int GUIEVENT_SELECT_CREATURE = 21;
int GUIEVENT_UNSELECT_CREATURE = 22;
int GUIEVENT_EXAMINE_OBJECT = 23;
int GUIEVENT_OPTIONS_OPEN = 24;
int GUIEVENT_OPTIONS_CLOSE = 25;
int JSON_TYPE_NULL = 0; // Also invalid
int JSON_TYPE_OBJECT = 1;
int JSON_TYPE_ARRAY = 2;
int JSON_TYPE_STRING = 3;
int JSON_TYPE_INTEGER = 4;
int JSON_TYPE_FLOAT = 5;
int JSON_TYPE_BOOL = 6;
// The player's gui width (inner window bounds).
string PLAYER_DEVICE_PROPERTY_GUI_WIDTH = "gui_width";
// The player's gui height (inner window bounds).
string PLAYER_DEVICE_PROPERTY_GUI_HEIGHT = "gui_height";
// The player's gui scale, in percent (factor 1.4 = 140)
string PLAYER_DEVICE_PROPERTY_GUI_SCALE = "gui_scale";
int PLAYER_LANGUAGE_INVALID = -1;
int PLAYER_LANGUAGE_ENGLISH = 0;
int PLAYER_LANGUAGE_FRENCH = 1;
int PLAYER_LANGUAGE_GERMAN = 2;
int PLAYER_LANGUAGE_ITALIAN = 3;
int PLAYER_LANGUAGE_SPANISH = 4;
int PLAYER_LANGUAGE_POLISH = 5;
int PLAYER_DEVICE_PLATFORM_INVALID = 0;
int PLAYER_DEVICE_PLATFORM_WINDOWS_X86 = 1;
int PLAYER_DEVICE_PLATFORM_WINDOWS_X64 = 2;
int PLAYER_DEVICE_PLATFORM_LINUX_X86 = 10;
int PLAYER_DEVICE_PLATFORM_LINUX_X64 = 11;
int PLAYER_DEVICE_PLATFORM_LINUX_ARM32 = 12;
int PLAYER_DEVICE_PLATFORM_LINUX_ARM64 = 13;
int PLAYER_DEVICE_PLATFORM_MAC_X86 = 20;
int PLAYER_DEVICE_PLATFORM_MAC_X64 = 21;
int PLAYER_DEVICE_PLATFORM_IOS = 30;
int PLAYER_DEVICE_PLATFORM_ANDROID_ARM32 = 40;
int PLAYER_DEVICE_PLATFORM_ANDROID_ARM64 = 41;
int PLAYER_DEVICE_PLATFORM_ANDROID_X64 = 42;
int PLAYER_DEVICE_PLATFORM_NINTENDO_SWITCH = 50;
int PLAYER_DEVICE_PLATFORM_MICROSOFT_XBOXONE = 60;
int PLAYER_DEVICE_PLATFORM_SONY_PS4 = 70;
int RESTYPE_RES = 0;
...
int RESTYPE_CAF = 2082;
// Parse the given string as a valid json value, and returns the corresponding type.
// Returns a JSON_TYPE_NULL on error.
// Check JsonGetError() to see the parse error, if any.
// NB: The parsed string needs to be in game-local encoding, but the generated json structure
// will contain UTF-8 data.
json JsonParse(string sJson);
// Dump the given json value into a string that can be read back in via JsonParse.
// nIndent describes the indentation level for pretty-printing; a value of -1 means no indentation and no linebreaks.
// Returns a string describing JSON_TYPE_NULL on error.
// NB: The dumped string is in game-local encoding, with all non-ascii characters escaped.
string JsonDump(json jValue, int nIndent = -1);
// Describes the type of the given json value.
// Returns JSON_TYPE_NULL if the value is empty.
int JsonGetType(json jValue);
// Returns the length of the given json type.
// For objects, returns the number of top-level keys present.
// For arrays, returns the number of elements.
// Null types are of size 0.
// All other types return 1.
int JsonGetLength(json jValue);
// Returns the error message if the value has errored out.
// Currently only describes parse errors.
string JsonGetError(json jValue);
// Create a NULL json value, seeded with a optional error message for JsonGetError().
json JsonNull(string sError = "");
// Create a empty json object.
json JsonObject();
// Create a empty json array.
json JsonArray();
// Create a json string value.
// NB: Strings are encoded to UTF-8 from the game-local charset.
json JsonString(string sValue);
// Create a json integer value.
json JsonInt(int nValue);
// Create a json floating point value.
json JsonFloat(float fValue);
// Create a json bool valye.
json JsonBool(int bValue);
// Returns a string representation of the json value.
// Returns "" if the value cannot be represented as a string, or is empty.
// NB: Strings are decoded from UTF-8 to the game-local charset.
string JsonGetString(json jValue);
// Returns a int representation of the json value, casting where possible.
// Returns 0 if the value cannot be represented as a float.
// Use this to parse json bool types.
// NB: This will narrow down to signed 32 bit, as that is what NWScript int is.
// If you are trying to read a 64 bit or unsigned integer, you will lose data.
// You will not lose data if you keep the value as a json element (via Object/ArrayGet).
int JsonGetInt(json jValue);
// Returns a float representation of the json value, casting where possible.
// Returns 0.0 if the value cannot be represented as a float.
// NB: This will narrow doubles down to float.
// If you are trying to read a double, you will lose data.
// You will not lose data if you keep the value as a json element (via Object/ArrayGet).
float JsonGetFloat(json jValue);
// Returns a json array containing all keys of jObject.
// Returns a empty array if the object is empty or not a json object, with GetJsonError() filled in.
json JsonObjectKeys(json jObject);
// Returns the key value of sKey on the object jObect.
// Returns a null json value if jObject is not a object or sKey does not exist on the object, with GetJsonError() filled in.
json JsonObjectGet(json jObject, string sKey);
// Returns a modified copy of jObject with the key at sKey set to jValue.
// Returns a json null value if jObject is not a object, with GetJsonError() filled in.
json JsonObjectSet(json jObject, string sKey, json jValue);
// Returns a modified copy of jObject with the key at sKey deleted.
// Returns a json null value if jObject is not a object, with GetJsonError() filled in.
json JsonObjectDel(json jObject, string sKey);
// Gets the json object at jArray index position nIndex.
// Returns a json null value if the index is out of bounds, with GetJsonError() filled in.
json JsonArrayGet(json jArray, int nIndex);
// Returns a modified copy of jArray with position nIndex set to jValue.
// Returns a json null value if jArray is not actually an array, with GetJsonError() filled in.
// Returns a json null value if nIndex is out of bounds, with GetJsonError() filled in.
json JsonArraySet(json jArray, int nIndex, json jValue);
// Returns a modified copy of jArray with jValue inserted at position nIndex.
// All succeeding objects in the array will move by one.
// By default (-1), inserts objects at the end of the array ("push").
// nIndex = 0 inserts at the beginning of the array.
// Returns a json null value if jArray is not actually an array, with GetJsonError() filled in.
// Returns a json null value if nIndex is not 0 or -1 and out of bounds, with GetJsonError() filled in.
json JsonArrayInsert(json jArray, json jValue, int nIndex = -1);
// Returns a modified copy of jArray with the element at position nIndex removed,
// and the array resized by one.
// Returns a json null value if jArray is not actually an array, with GetJsonError() filled in.
// Returns a json null value if nIndex is out of bounds, with GetJsonError() filled in.
json JsonArrayDel(json jArray, int nIndex);
// Transforms the given object into a json structure.
// The json format is compatible with what https://github.com/niv/neverwinter.nim@1.4.3+ emits.
// Returns the null json type on errors, or if oObject is not serializable, with GetJsonError() filled in.
// Supported object types: creature, item, trigger, placeable, door, waypoint, encounter, store, area (combined format)
// If bSaveObjectState is TRUE, local vars, effects, action queue, and transition info (triggers, doors) are saved out
// (except for Combined Area Format, which always has object state saved out).
json ObjectToJson(object oObject, int bSaveObjectState = FALSE);
// Deserializes the game object described in jObject.
// Returns OBJECT_INVALID on errors.
// Supported object types: creature, item, trigger, placeable, door, waypoint, encounter, store, area (combined format)
// For areas, locLocation is ignored.
// If bLoadObjectState is TRUE, local vars, effects, action queue, and transition info (triggers, doors) are read in.
object JsonToObject(json jObject, location locLocation, object oOwner = OBJECT_INVALID, int bLoadObjectState = FALSE);
// Returns the element at the given JSON pointer value.
// See https://datatracker.ietf.org/doc/html/rfc6901 for details.
// Returns a json null value on error, with GetJsonError() filled in.
json JsonPointer(json jData, string sPointer);
// Return a modified copy of jData with jValue inserted at the path described by sPointer.
// See https://datatracker.ietf.org/doc/html/rfc6901 for details.
// Returns a json null value on error, with GetJsonError() filled in.
// jPatch is an array of patch elements, each containing a op, a path, and a value field. Example:
// [
// { "op": "replace", "path": "/baz", "value": "boo" },
// { "op": "add", "path": "/hello", "value": ["world"] },
// { "op": "remove", "path": "/foo"}
// ]
// Valid operations are: add, remove, replace, move, copy, test
json JsonPatch(json jData, json jPatch);
// Returns the diff (described as a json structure you can pass into JsonPatch) between the two objects.
// Returns a json null value on error, with GetJsonError() filled in.
json JsonDiff(json jLHS, json jRHS);
// Returns a modified copy of jData with jMerge merged into it. This is an alternative to
// JsonPatch/JsonDiff, with a syntax more closely resembling the final object.
// See https://datatracker.ietf.org/doc/html/rfc7386 for details.
// Returns a json null value on error, with GetJsonError() filled in.
json JsonMerge(json jData, json jMerge);
// Get oObject's local json variable sVarName
// * Return value on error: json null type
json GetLocalJson(object oObject, string sVarName);
// Set oObject's local json variable sVarName to jValue
void SetLocalJson(object oObject, string sVarName, json jValue);
// Delete oObject's local json variable sVarName
void DeleteLocalJson(object oObject, string sVarName);
// Bind an json to a named parameter of the given prepared query.
// Json values are serialised into a string.
// Example:
// sqlquery v = SqlPrepareQueryObject(GetModule(), "insert into test (col) values (@myjson);");
// SqlBindJson(v, "@myjson", myJsonObject);
// SqlStep(v);
void SqlBindJson(sqlquery sqlQuery, string sParam, json jValue);
// Retrieve a column cast as a json value of the currently stepped row.
// You can call this after SqlStep() returned TRUE.
// In case of error, a json null value will be returned.
// In traditional fashion, nIndex starts at 0.
json SqlGetJson(sqlquery sqlQuery, int nIndex);
// This stores a json out to the specified campaign database
// The database name:
// - is case insensitive and it must be the same for both set and get functions.
// - can only contain alphanumeric characters, no spaces.
// The var name must be unique across the entire database, regardless of the variable type.
// If you want a variable to pertain to a specific player in the game, provide a player object.
void SetCampaignJson(string sCampaignName, string sVarName, json jValue, object oPlayer=OBJECT_INVALID);
// This will read a json from the specified campaign database
// The database name:
// - is case insensitive and it must be the same for both set and get functions.
// - can only contain alphanumeric characters, no spaces.
// The var name must be unique across the entire database, regardless of the variable type.
// If you want a variable to pertain to a specific player in the game, provide a player object.
json GetCampaignJson(string sCampaignName, string sVarName, object oPlayer=OBJECT_INVALID);
// Gets a device property/capability as advertised by the client.
// sProperty is one of PLAYER_DEVICE_PROPERTY_xxx.
// Returns -1 if
// - the property was never set by the client,
// - the the actual value is -1,
// - the player is running a older build that does not advertise device properties,
// - the player has disabled sending device properties (Options->Game->Privacy).
int GetPlayerDeviceProperty(object oPlayer, string sProperty);
// Returns the LANGUAGE_xx code of the given player, or -1 if unavailable.
int GetPlayerLanguage(object oPlayer);
// Returns one of PLAYER_DEVICE_PLATFORM_xxx, or 0 if unavailable.
int GetPlayerDevicePlatform(object oPlayer);
// Deserializes the given resref/template into a JSON structure.
// Supported GFF resource types:
// * RESTYPE_CAF (and RESTYPE_ARE, RESTYPE_GIT, RESTYPE_GIC)
// * RESTYPE_UTC
// * RESTYPE_UTI
// * RESTYPE_UTT
// * RESTYPE_UTP
// * RESTYPE_UTD
// * RESTYPE_UTW
// * RESTYPE_UTE
// * RESTYPE_UTM
// Returns a valid gff-type json structure, or a null value with GetJsonError() set.
json TemplateToJson(string sResRef, int nResType);
// Returns the resource location of sResRef.nResType, as seen by the running module.
// Note for dedicated servers: Checks on the module/server side, not the client.
// Returns "" if the resource does not exist in the search space.
string ResManGetAliasFor(string sResRef, int nResType);
// Finds the nNth available resref starting with sPrefix.
// * Set bSearchBaseData to TRUE to also search base game content stored in your game installation directory.
// WARNING: This can be very slow.
// * Set sOnlyKeyTable to a specific keytable to only search the given named keytable (e.g. "OVERRIDE:").
// Returns "" if no such resref exists.
string ResManFindPrefix(string sPrefix, int nResType, int nNth = 1, int bSearchBaseData = FALSE, string sOnlyKeyTable = "");
// Create a NUI window from the given resref(.jui) for the given player.
// * The resref needs to be available on the client, not the server.
// * The token is a integer for ease of handling only. You are not supposed to do anything with it, except store/pass it.
// * The window ID needs to be alphanumeric and short. Only one window (per client) with the same ID can exist at a time.
// Re-creating a window with the same id of one already open will immediately close the old one.
// * See nw_inc_nui.nss for full documentation.
// Returns the window token on success (>0), or 0 on error.
int NuiCreateFromResRef(object oPlayer, string sResRef, string sWindowId = "");
// Create a NUI window inline for the given player.
// * The token is a integer for ease of handling only. You are not supposed to do anything with it, except store/pass it.
// * The window ID needs to be alphanumeric and short. Only one window (per client) with the same ID can exist at a time.
// Re-creating a window with the same id of one already open will immediately close the old one.
// * See nw_inc_nui.nss for full documentation.
// Returns the window token on success (>0), or 0 on error.
int NuiCreate(object oPlayer, json jNui, string sWindowId = "");
// You can look up windows by ID, if you gave them one.
// * Windows with a ID present are singletons - attempting to open a second one with the same ID
// will fail, even if the json definition is different.
// Returns the token if found, or 0.
int NuiFindWindow(object oPlayer, string sId);
// Destroys the given window, by token, immediately closing it on the client.
// Does nothing if nUiToken does not exist on the client.
// Does not send a close event - this immediately destroys all serverside state.
// The client will close the window asynchronously.
void NuiDestroy(object oPlayer, int nUiToken);
// Returns the originating player of the current event.
object NuiGetEventPlayer();
// Gets the window token of the current event (or 0 if not in a event).
int NuiGetEventWindow();
// Returns the event type of the current event.
// * See nw_inc_nui.nss for full documentation of all events.
string NuiGetEventType();
// Returns the ID of the widget that triggered the event.
string NuiGetEventElement();
// Get the array index of the current event.
// This can be used to get the index into an array, for example when rendering lists of buttons.
// Returns -1 if the event is not originating from within an array.
int NuiGetEventArrayIndex();
// Returns the window ID of the window described by nUiToken.
// Returns "" on error, or if the window has no ID.
string NuiGetWindowId(object oPlayer, int nUiToken);
// Gets the json value for the given player, token and bind.
// * json values can hold all kinds of values; but NUI widgets require specific bind types.
// It is up to you to either handle this in NWScript, or just set compatible bind types.
// No auto-conversion happens.
// Returns a json null value if the bind does not exist.
json NuiGetBind(object oPlayer, int nUiToken, string sBindName);
// Sets a json value for the given player, token and bind.
// The value is synced down to the client and can be used in UI binding.
// When the UI changes the value, it is returned to the server and can be retrieved via NuiGetBind().
// * json values can hold all kinds of values; but NUI widgets require specific bind types.
// It is up to you to either handle this in NWScript, or just set compatible bind types.
// No auto-conversion happens.
// * If the bind is on the watch list, this will immediately invoke the event handler with the "watch"
// even type; even before this function returns. Do not update watched binds from within the watch handler
// unless you enjoy stack overflows.
// Does nothing if the given player+token is invalid.
void NuiSetBind(object oPlayer, int nUiToken, string sBindName, json jValue);
// Swaps out the given element (by id) with the given nui layout (partial).
// * This currently only works with the "group" element type, and the special "_window_" root group.
void NuiSetGroupLayout(object oPlayer, int nUiToken, string sElement, json jNui);
// Mark the given bind name as watched.
// A watched bind will invoke the NUI script event every time it's value changes.
// Be careful with binding nui data inside a watch event handler: It's easy to accidentally recurse yourself into a stack overflow.
int NuiSetBindWatch(object oPlayer, int nUiToken, string sBind, int bWatch);
// Returns the nNth window token of the player, or 0.
// nNth starts at 0.
// Iterator is not write-safe: Calling DestroyWindow() will invalidate move following offsets by one.
int NuiGetNthWindow(object oPlayer, int nNth = 0);
// Return the nNth bind name of the given window, or "".
// If bWatched is TRUE, iterates only watched binds.
// If FALSE, iterates all known binds on the window (either set locally or in UI).
string NuiGetNthBind(object oPlayer, int nToken, int bWatched, int nNth = 0);
// Returns the event payload, specific to the event.
// Returns JsonNull if event has no payload.
json NuiGetEventPayload();
// Get the userdata of the given window token.
// Returns JsonNull if the window does not exist on the given player, or has no userdata set.
json NuiGetUserData(object oPlayer, int nToken);
// Sets an arbitrary json value as userdata on the given window token.
// This userdata is not read or handled by the game engine and not sent to clients.
// This mechanism only exists as a convenience for the programmer to store data bound to a windows' lifecycle.
// Will do nothing if the window does not exist.
void NuiSetUserData(object oPlayer, int nToken, json jUserData);
Today, we have a development build for you. We appreciate your assistance in testing it, and reporting any issues you find.
The module compatibility has once again been bumped - to version 1.85 - due to the new script commands that allow tile/area actions in the radial menu, and a bit more GUI modification.
Fixes
Renderer: Disabled soft particles on world-aligned emitters; fixes faded/missing effects such as spell mantle.
Renderer: TSB are now generated correctly for animated/danglies.
Renderer: Removed creature preloading code; it was doing nothing useful on desktop platforms.
Renderer: Drawbucket implementation for caching transform lists; also fixes additive blending render issues (such as water making visual effects wavy; water showing geometry from own model).
Renderer: Fixed highlight gobs ("tab glow") not correctly scaling along visual transforms.
Renderer: Fixed static placeables incorrectly disabling/breaking animations of non-static placeables with the same model.
Game: Fixed a memleak in pathfinding.
Game: Fixed placeables not bumping creatures out of the way when being spawned into an area. #349
Game/VM: Fixed Get*DurationRemaining returning the wrong value due to time overflow.
Game: Fixed a rare crash when exiting the game while a NWSync download is in progress.
Startgame UI: Fixed URL escaping for URLs with long paths.
Startgame UI: Removed superfluous text warnings about "Clicking link opens browser".
Startgame UI: Improvements to the promotion panels for custom repositories.
Startgame UI: Images loaded in the new content browser are now correctly served from cache if no internet connection is available.
UI: Put most hardcoded strings in StartGame, NWSync and Config panels into the tlk (not translated yet).
Config: On Windows, codepage is now auto-detected from the system locale.
Tile/Radial actions, surfacemat.2da
You can now assign custom radial actions on tiles (There is a new module event you can access via SetEventScript).
surfacemat now support 64 materials, up from 32.
surfacemat.2da is re/loaded as part of custom content.
// Gets the ID (1..8) of the last tile action performed in OnPlayerTileAction
int GetLastTileActionId();
// Gets the target position in the module OnPlayerTileAction event.
vector GetLastTileActionPosition();
// Gets the player object that triggered the OnPlayerTileAction event.
object GetLastPlayerToDoTileAction();
New Script Commands
You can now pop up or restrict GUI panels.
We added a event that triggers clientside interaction with some panels (There is a new module event you can access via SetEventScript).
int GUI_PANEL_PLAYER_DEATH = 0;
int GUI_PANEL_MINIMAP = 2;
int GUI_PANEL_COMPASS = 3;
int GUI_PANEL_INVENTORY = 4;
int GUI_PANEL_PLAYERLIST = 5;
int GUI_PANEL_JOURNAL = 6;
int GUI_PANEL_SPELLBOOK = 7;
int GUI_PANEL_CHARACTERSHEET = 8;
int GUIEVENT_CHATBAR_FOCUS = 1;
int GUIEVENT_CHATBAR_UNFOCUS = 2;
int GUIEVENT_CHARACTERSHEET_SKILL_CLICK = 3;
int GUIEVENT_CHARACTERSHEET_FEAT_CLICK = 4;
int GUIEVENT_EFFECTICON_CLICK = 5;
int GUIEVENT_DEATHPANEL_WAITFORHELP_CLICK = 6;
int GUIEVENT_MINIMAP_MAPPIN_CLICK = 7;
int GUIEVENT_MINIMAP_OPEN = 8;
int GUIEVENT_MINIMAP_CLOSE = 9;
int GUIEVENT_JOURNAL_OPEN = 10;
int GUIEVENT_JOURNAL_CLOSE = 11;
int GUIEVENT_PLAYERLIST_PLAYER_CLICK = 12;
int GUIEVENT_PARTYBAR_PORTRAIT_CLICK = 13;
int GUIEVENT_DISABLED_PANEL_ATTEMPT_OPEN = 14;
// Gets the player that last triggered the module OnPlayerGuiEvent event.
object GetLastGuiEventPlayer();
// Gets the last triggered GUIEVENT_* in the module OnPlayerGuiEvent event.
int GetLastGuiEventType();
// Gets an optional integer of specific gui events in the module OnPlayerGuiEvent event.
// * GUIEVENT_CHATBAR_*: The selected chat channel. Does not indicate the actual used channel.
// 0 = Shout, 1 = Whisper, 2 = Talk, 3 = Party, 4 = DM
// * GUIEVENT_CHARACTERSHEET_SKILL_SELECT: The skill ID.
// * GUIEVENT_CHARACTERSHEET_FEAT_SELECT: The feat ID.
// * GUIEVENT_EFFECTICON_CLICK: The effect icon ID (EFFECT_ICON_*)
// * GUIEVENT_DISABLED_PANEL_ATTEMPT_OPEN: The GUI_PANEL_* the player attempted to open.
int GetLastGuiEventInteger();
// Gets an optional object of specific gui events in the module OnPlayerGuiEvent event.
// * GUIEVENT_MINIMAP_MAPPIN_CLICK: The waypoint the map note is attached to.
// * GUIEVENT_CHARACTERSHEET_*_SELECT: The owner of the character sheet.
// * GUIEVENT_PLAYERLIST_PLAYER_CLICK: The player clicked on.
// * GUIEVENT_PARTYBAR_PORTRAIT_CLICK: The creature clicked on.
// * GUIEVENT_DISABLED_PANEL_ATTEMPT_OPEN: For GUI_PANEL_CHARACTERSHEET, the owner of the character sheet.
object GetLastGuiEventObject();
// Disable a gui panel for the client that controls oPlayer.
// Notes: Will close the gui panel if currently open.
// Does not persist through relogging or in savegames.
// Will fire a GUIEVENT_DISABLED_PANEL_ATTEMPT_OPEN OnPlayerGuiEvent for some gui panels if a player attempts to open them.
// You can still force show a panel with PopUpGUIPanel().
// * nGuiPanel: A GUI_PANEL_* constant, except GUI_PANEL_PLAYER_DEATH.
void SetGuiPanelDisabled(object oPlayer, int nGuiPanel, int bDisabled);
Tyrants of the Moonsea
one dialog branch in Hlesson's conversation breaks his quest
fixed a rare situation in the Marel fight where the exit door wouldn't open
removed Sharalyn's Cloud of Bewilderment spell because it caused in-party hostility
the Kur-Tharsu areas are now "No PVP" to reduce in-party hostility
Darkness over Daggerford
a cutscene in smuggler's cave sometimes did not trigger
a cutscene in smuggler's cave sometimes was aborted early
Known Issues
The toolset tilefade setting "Always" doesn't work correctly.
Please note: Modules saved with the toolset of this patch are flagged as Compat 1.84, due to some new script commands.
As always, we appreciate your continued support!
Game, Logic and Rules
Shields: Now take their AC from the BaseAC column, no longer hardcoded. Still only works for the three base shield types!
Game: Reverted the MoveToObject fRange change; fRange is now once again ignored for that specific breakage. This fixes various issues in custom content modules, notably the elusive wizard in Aielund III Pt 3.
Game/baseitems: New column IsMonkWeapon replaces all hardcoded checks against BASETYPE_KAMA.
Game/baseitems: Custom content can now make Flurry Of Blows also work with ranged weapons (when IsMonkWeapon is enabled).
Game/baseitems: New column WeaponFinesseMinimumCreatureSize. Takes 0/**** or the minimum creature size type needed to finesse.
Game: Other stealthed/concealed creatures no longer pop up briefly when transitioning into a new area or into a doored-off room.
Game: Fixed creatures doing a weird shimmy/turny when coming to a stop.
Game data: Added “Neverwinter Chess” module from old Bioware demo modules.
Config
Game/Config: You can now hide player quickchat lines from the chat window (Config->UI->Feedback)
Config: Exposing codepage setting under Game->Advanced; this should make it easier for Russian language players to set the correct codepage without having to edit settings.tml.
Config: New option to enable SQLite API tracing to the server log: Show all queries, or just show queries running longer than N msec.
Tech
Core: Upgraded static openssl to 1.1.1k.
Core: Upgraded libyuv to latest master upstream.
Core: Upgraded SQLite to version 3.36.0.
Renderer: VDO optimisations in preparation for OpenGL 3.3.
Renderer: The fallback envmap is now a globally shared object, not tied to the scene.
Renderer: Fixed most visual oddities related to water refraction (fringing, edge gaps, highlight strangeness).
Renderer: Fixed a regression that prevented custom shader params being set in scripting.
Renderer: Improved TSB generation, doing away with some visual glitches when adjacent faces had opposing hardness.
Renderer: Fixed a crash when setting hilite color of party members clients haven’t seen yet.
Renderer: Fixed a crash when setting a texture override for a creature object that the client hasn’t seen yet.
Nk/Nui: Image renderer can now render webm.
Nk/Nui: Fix key repeat not working.
Nk/Nui/json: NWSync Repository CExoLocStr fields now convert to the local game encoding; should fix various special characters. This means you now need to have the json once again properly encoded as UTF-8. (e.g. module descriptions in Polish)
Nk/Nui: Copy/Paste now works with Cmd on Mac, instead of Ctrl.
Game Launcher: Repositories can now advertise websites (such as the Vault on the Curated repo).
Game Launcher: Modules can now have a list of web links.
Game Launcher: Repository Info now shows the origin URL.
NWSync Management UI: Text clarity improvements to the disk space effect of deleting manifests.
NWSync UI: Sped up manifest loading. Game Launcher and Storage UI should now appear much faster.
NWSync UI: Fixed the NWSync housekeeping UI no longer popping up when deleting multiple manifests in a row.
NWSync UI: NWSync Storage UI now remembers selected checkboxes and sorting.
NWSync: Game no longer crashes when running out of disk space while downloading manifest, shows error instead.
NWSync/SQLite: We now use soft limits on shard size, not hard limits. This fixes a very rare “out of disk” error when trying to write metadata even though plenty of free space is available.
VM: Fixed a crash in reconciling automap data when trying to load a player TURD. #309
VM: Fixed SetCampaignString(, “”) resulting in a SQL error. Now it deletes the variable from the database instead. It now also deletes entries when other types are “empty”.
VM: GetCurrentlyRunningEvent now has param bInheritParent for cases such as CreatureOnDeath/Damaged returning the wrong event ID otherwise.
VM: Fixed Area Of Effect sometimes not returning the expected objects due to local object positions in memory not updating correctly. #52
VM: Cassowary: Fixed a crash when calling AddConstraint with invalid math formula. #293
VM: We had to change how Create/Copy/DestroyArea works. Most prominently:
Areas you create via Create/Copy now have their own auto-generated resref in the nw_ namespace; they no longer have the same resref as the originating area/template. You cannot influence this. nw_ is also the same namespace that the toolset reserves for game-builtin content, so you shouldn’t be able to clobber into it. Instanced areas go into CURRENTGAME:, until they are saved into a .sav. (This also means you can CreateArea with the new template, if you really wanted, though why would you? You’d get the area as it looked like at original instantiation time)
Areas are now saved to savegames correctly and savegames no longer corrupt when the area count changes.
Savegames now also store a serialised image of the .are data.
This means that CreateArea(“originalresref”) in savegames will now instance the area as saved in the savegame, NOT as designed in the toolset. This is because modules don’t actually load anymore after a savegame; the savegame replaces the original module.
CopyArea now supports assigning a new tag and name directly on instantiation.
This is a high-risk change, as it touches significant parts of the area load codepath. We’d appreciate thorough testing.
Movies: We no longer show the Bioware credits for all modules; only the last module in each main campaign.
Movies: Fixed error/crash when calling EndGame(“”). Now the game shows no movie instead, and just quits the main menu.
New Script Commands
// Create a RunScript effect.
// Notes: When applied as instant effect, only sOnAppliedScript will fire.
// In the scripts, OBJECT_SELF will be the object the effect is applied to.
// * sOnAppliedScript: An optional script to execute when the effect is applied.
// * sOnRemovedScript: An optional script to execute when the effect is removed.
// * sOnIntervalScript: An optional script to execute every fInterval seconds.
// * fInterval: The interval in seconds, must be >0.0f if an interval script is set.
// Very low values may have an adverse effect on performance.
// * sData: An optional string of data saved in the effect, retrievable with GetEffectString() at index 0.
effect EffectRunScript(string sOnAppliedScript = "", string sOnRemovedScript = "", string sOnIntervalScript = "", float fInterval = 0.0f, string sData = "");
// Get the effect that last triggered an EffectRunScript() script.
// Note: This can be used to get creator or tag, among others, of the EffectRunScript() in one of its scripts.
// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT when called outside of an EffectRunScript() script.
effect GetLastRunScriptEffect();
// Get the script type (RUNSCRIPT_EFFECT_SCRIPT_TYPE_*) of the last triggered EffectRunScript() script.
// * Returns 0 when called outside of an EffectRunScript() script.
int GetLastRunScriptEffectScriptType();
// Hides the effect icon of eEffect and of all effects currently linked to it.
effect HideEffectIcon(effect eEffect);
// Create an Icon effect.
// * nIconID: The effect icon (EFFECT_ICON_*) to display.
// Using the icon for Poison/Disease will also color the health bar green/brown, useful to simulate custom poisons/diseases.
// Returns an effect of type EFFECT_TYPE_INVALIDEFFECT when nIconID is < 1 or > 255.
effect EffectIcon(int nIconID);
Neverwinter Nights: Enhanced Edition - Julius Borisov
Greetings, dear players and creators!
Patch 8193.26 for Neverwinter Nights: Enhanced Edition arrives today! The update includes dozens of features and fixes — plus adds two epic adventures to our trove of curated community content!
Check out full patch notes below!
Patch Highlights
New curated community content available! | Our new game launcher continues to grow! Two remarkable modules, Against the Cult of the Reptile God by Rich Barker, and Eye of the Beholder 1 & 2 by Rick Francis are now available to download with a single click!
Curated community content now playable in Multiplayer | The new curated content UI is now available when running multiplayer games
Polished fonts | Based on player feedback, we’ve spent some time improving font rendering!
Renderer | Many more performance and stability/compatibility improvements
New Curated Content Releases
We’re proud to announce two new curated content adventures, released in full cooperation with their authors.
With the latest update, you can also play them in multiplayer— and Eye of the Beholder by Rick Francis was designed to do just that. So gather your friends, and play the thrilling remake of the classic 1991 release from Strategic Simulations, Inc.
Or perhaps you're in the mood to dive into early tabletop AD&D nostalgia? Against the Cult of the Reptile God, by Rich Barker adapts the novice-series module into a smart, single-player dungeon crawl!
N1: Against The Cult of the Reptile God
By Rich Barker Terror by night! The village of Orlane is dying. Once a small and thriving community, Orlane has become a maze of locked doors and frightened faces.
Strangers are shunned, trade has withered. Rumours flourish, growing wilder with each retelling. Terrified peasants flee their homes, abandoning their farms with no explanation.
Others simply disappear...
The adventure is set in Greyhawk and is based on the original TSR AD&D module N1 : Against the Cult of the Reptile God by Douglas Niles, which was ranked the 19th best AD&D module of all time by Dungeon Magazine.
While this conversion has added considerably to the original source material, all of the core elements of the adventure remain.
It is a mixture of wilderness, underground, and urban adventure, with a number of unique monsters and items, and some class-specific treasure hoards and rewards.
It is suitable for any class, although is harder for rogues. Druids and rangers are particularly well catered for, and can talk to many of the animals.
The adventure has a duration of approximately 15 hours, and is designed for starting 1st level characters, who can be expected to progress to around 8th level by the end of the adventure.
There are no alignment restrictions, but the storyline is predominantly suitable for good or neutral characters.
Eye of the Beholder
By Rick Francis A two-part remake of the classic game by Westwood/SSI.
The Eye of the Beholder series is designed to be fully multiplayer-compatible!
Eye of the Beholder
A drow spy was captured two nights ago by Oltec, Captain of the Guard. Under interrogation, the drow revealed a disturbing plan. He claimed to be working for a being named "Xanathar". The spy claimed that "Xanathar" is an evil crime lord that inhabits the deepest part of the sewer system. Supposedly, Xanathar magically teleported this drow into the city to spy on the Lords of Waterdeep, but was unable to use his magic to teleport him back underground safely. Speak with Khelben Blackstaff to find out more information that could help you before setting out on your journey.
Legend of Darkmoon
You are summoned by the wizard Khelben Blackstaff, and sent to investigate the dreaded Temple Darkmoon. The heroes of Waterdeep are sent to investigate strange disappearances from villages north of the city in the outline areas of the dark forest. Reports have been made of discoveries of human remains in shallow graves. The clues seem to lead to the Temple Darkmoon, which appears to be a peaceful sanctuary, run by divinely-inspired priests. You must explore the temple, deal with its inhabitants, avoid traps, and battle monsters to discover the temple's evil secrets. Alas, as often happens, appearances are deceiving, and the Temple's dark secret is up to the player to unfold.
Curated Content in Multiplayer
The multiplayer UI now uses the same slick design already offered in single-player.
You can now host games with modules downloaded from the curated content list (or any other third party repository used).
The repository will be automatically shared with other players, so they will be offered the download transparently (it’s just like joining a NWSync-enabled persistent world)!
Note: Multiplayer is not supported by all modules. Some were designed for singleplayer, and may break in unexpected ways when played with multiple people. Play them at your own risk!
In addition, the Start Game UI (for both single and multiplayer) saw the following changes:
Steam Workshop modules now show up on the Community tab. Each workshop item shows up with all modules grouped as a campaign
Improved UX when deselecting campaigns or modules
Campaigns in the list now grey out properly if none of their modules are installed
Campaigns can now display screenshots
Modules can now show as published-but-unreleased by setting a future date in the version entry
Renderer
Tons of performance and stability/compatibility improvements went into this patch:
Fixed the pink screen issue for unsupported GPUs
Drastically reduced the need for buffer switching by combining vertex arrays (perf+)
Combined most post-processing shaders into single pass (perf+)
Custom shader parameters are now properly relinked when another shader is loaded (consistency)
Early-Z optimisation reenabled via ifdef guard in shaders (perf+) - Note: if using custom shaders for setting alpha on a texture, you must now supply a txi too with an alphamean entry with a value below 1.0
TSB are now calculated for legacy compiled models at runtime as well (consistency/compat)
Fixed GL CubeMap load order (compat)
Fixed env maps for PC models not updating between areas. Now allows “default” map in txi files
Now uses the default chrome environment map if the assigned map is missing. Fixes issue with metallic objects becoming transparent (consistency)
Fixed gamma functions erroneously checking all colour channels, instead of any (consistency)
Fixed disabling tile light source with TILE_SOURCE_LIGHT_COLOR_BLACK not actually removing the light
Completely disabled texture downsampling for non-compressed textures
Fixed crash when releasing PLT
Added some crash safeguards for broken models
Adaptive soft particle based on size, should fix missing/faded monk eyes
Simple shadow perf optimisation
Fixed setting ambient material for grass
Fixed a crash when downsizing internal data structures
Fonts
We spent some time improving font rendering after the feedback from the last patch:
TrueType Fonts renamed to their function, away from font names
Combined glyph maps so all languages now share the same font files. Brawler is now the default font for high res typeface, since it was the most popular
Mordred numerals no longer offset old-style
Added slight adjustments to spacing and padding to make it look better
You can now tweak font outlines and contrast per-font (via txi)
Fixed the wrong font appearing in places like the DM chooser
Increased contrast for the high-res variant
Pathfinding
Static object removal now properly removes objects from the internal object list. This addresses the cases where a lot of static objects (such as body bags) added and removed over hours, would bog down gameplay
Fixed creatures not correctly moving towards an interactable object in certain marginal situations. Added better handling of requested distance when moving towards an object
Abort attempts at pathfinding early if the target destination isn’t achievable (perf+)
Gameplay
Default weapon feats for gloves and bracers now properly set if 2da entry is missing
Recommended button in chargen/levelup can now be disabled via ruleset.2da entry
Fixed hit dice validation for RDD
Fixed creature gobs sometimes erroneously generating really large wind values
Premium Modules
We cleaned up the dataset for the Tyrants of the Moonsea premium module. All content that is already part of the base game has been removed from the DLC download. In addition, the following fixes went in:
Tyrants of the Moonsea
Camera position no longer resets to top-down when exiting the world map
Some tile appearances are now correct when "facelift" mode isactive
The Kur-Tharsu main gate is no longer difficult to attack
A spider on the Shind Road East spawns in the correct spot
Removed a minor XP exploit in Thalia's conversation
Darkness over Daggerford
Camera position no longer resets to top-down when exiting the world map
Other Fixes
UI/Nuklear: Fixed a crash that would occur when swapping child layouts (“clicking around a lot”)
Game: OnPlayerTarget now actually persisted to save games
Toolset: Fixed not rendering bounding boxes
Fonts: codepage=cp1251: Added support for a missing cyrillic character
Nui: codepage=cp1251: Fixed an issue with baking missing glyphs that would break rendered text
Fixed a regression from the previous development patch where some PLT textures were rendering incorrectly
Today's development patch is a small one: Most importantly, it fixes one crash we found in testing.
It also addresses some compatibility issues with the unofficial Cyrillic language support, that hopefully helps our Russian friends maintain their community translation. We know that some strings in the new UI scenes are still not translatable. This will be addressed in a future patch!
Fixes
Renderer: Fixed a crash in static object walkmesh calculation.
Start Game UI: Fixed the New/Existing Character UI not doing anything useful.
Fonts: codepage=cp1251: Added support for a missing Cyrillic character.
Nui: codepage=cp1251: Fixed line break calculation for Cyrillic text.
Today we are releasing patch .24 to the development branch of Neverwinter Nights: Enhanced Edition!
This patch addresses some of the concerns raised with the latest stable release, while also bringing consistency, compatibility and performance improvements.
As always, we appreciate your feedback and help in bringing these changes to the next stable release!
Start Multiplayer Game UI
The Start Game UI for multiplayer is now also using the new design that singleplayer already offers.
You can now host a game from the curated content repository, or any other third party repository used. The repository will be automatically shared with other players, so that they will be offered the download transparently, same as if joining a NWSync-enabled persistent world.
In addition, the Start Game UI (both SP and MP) saw the following changes:
NewGame UI: Steam Workshop modules now show up on the Community tab. Each workshop item shows up with all modules grouped as a campaign
NewGame UI: Allow selecting panels even if they are already selected (allows going back to the info text)
NewGame UI: Campaigns in the list now grey out properly if no contained modules are installed
NewGame UI: Campaigns can now show screenshots as well
NewGame UI: Modules can now show as published-but-unreleased by setting a future date in the version entry
Fonts
We spent some time working on font rendering after the feedback received after the latest stable release:
Fonts: TrueType Fonts renamed to their function, away from font names
Fonts: Combined glyph maps so all languages now share the same font files. Brawler is now the default font for high res typeface, since it was the most popular
Fonts: Mordred numerals no longer offset old-style
Fonts: Slight adjustments to spacing and padding to make it look better
Fonts: You can now tweak font outlines and contrast per-font (via txi)
Fonts: Fixed the wrong font appearing in places like the DM chooser
Fonts: Increased contrast for the high-res variant
Renderer
A lot of performance and stability/compatibility improvements went into this patch:
Renderer: Fixed pink screen on unsupported older GPUs
Renderer: Drastically reduced the need for buffer switching by combining vertex arrays (perf+)
Renderer: PLT are now constructed on the GPU (perf+)
Renderer: Combined all post-processing shaders into single unit (perf+)
Renderer: Custom shader parameters are now properly relinked when another shader is loaded (consistency)
Renderer: Shadows are now rendered entirely via VBO (perf+)
Renderer: Early-Z optimisation reenabled via ifdef guard in shaders (perf+)
Renderer: TSB are now calculated for legacy compiled models at runtime as well (consistency/compat)
Renderer: Fixed GL CubeMap load order (compat)
Renderer: Fixed env maps for PC models not updating between areas. Now allows “default” map in txi files, handling missing maps better (consistency)
Renderer: Fixed gamma functions erroneously checking all colour channels, instead of any (consistency)
Renderer: Fixed disabling tile light source with TILE_SOURCE_LIGHT_COLOR_BLACK not actually removing the light
Renderer: Completely disabled texture downsampling for non-compressed textures
Renderer: Fixed crash when releasing PLT
Renderer: Added some crash safeguards for broken models
Renderer: Adaptive soft particle based on size, should fix missing/faded monk eyes
Renderer: Explicitly set PLT alpha mean values to fix issues with custom content such as robes not showing properly
Renderer: Simple shadow perf optimisation
Renderer: Fix setting ambient material for grass
Renderer: Fix skinmesh bodyparts not rendering
Renderer: Fix the renderaabb command not working
Fixed a crash when downsizing internal data structures
Pathfinding
Pathfinding: Static object removal now properly removes objects from the internal object list. This fixes cases where a lot of static objects (such as body bags) added and removed over hours, would bog down gameplay
Pathfinding: Fix creatures not correctly moving towards a interactible object in certain marginal situations. Better handling of requested distance when moving towards an object
Pathfinding: Abort attempts at pathing early if the target destination isn’t achievable anyways (perf+)
Game
Game: Default weapon feats for gloves and bracers now properly set if 2da entry is missing #317
Game: Recommended button in chargen/levelup can now be disabled via ruleset.2da entry
Game: Fixed hit dice validation for RDD
Game: Fixed creature gobs sometimes erroneously generating really large wind values #320
Premium Modules
We cleaned up the data set for the Tyrants of the Moonsea premium module. All content that is also part of the basegame has been removed from the DLC download.
In addition, the following fixes went in:
Tyrants of the Moonsea
Camera position no longer resets to top-down when exiting the world map
Some tile appearances were incorrect when "facelift" mode was active
The Kur-Tharsu main gate was difficult to attack
A spider on the Shind Road East spawned in the wrong spot
Removed a minor XP exploit in Thalia's conversation
Darkness over Daggerford
Camera position no longer resets to top-down when exiting the world map
Other Fixes
UI/Nuklear: Fixed a crash that would occur when swapping child layouts (“clicking around a lot”)
Game: OnPlayerTarget now actually persisted to savegames
Neverwinter Nights: Enhanced Edition - Julius Borisov
Greetings friends!
We’re launching a new patch for Neverwinter Nights: Enhanced Edition with some of the biggest feature sets yet! Today’s update brings a new game launcher with featured community content, performance improvements, plus hundreds of fixes.
Check out full details on Patch 8193.23 below...
Patch Highlights
New Game Launcher | A new game launcher highlights some amazing community content that’s free for everyone! New Portraits | Added three new character portraits (sourced from the community portrait contest) TrueType Font | Added full TrueType font (TTF) support throughout the game, for clearer and more adaptive text (as well as fractional/decimal point UI scaling) Pathfinding Improvements | Smoother movement and navigation Performance Improvements | Enjoy smoother gameplay with significant performance improvements Bug Fixes | Hundreds of bug fixes and polish features added New Scripts | Added dozens of new script commands for use in custom servers, including improved scaling and transformation (lerping) Water Graphics | Added realistic water reflections and refractions
New Game Launcher with Community Highlights
This patch ships with a new (singleplayer) game launcher. It showcases all campaigns and premium modules with header images, descriptions, screenshots, and more.
We’ve also added a repository of community content, hosted by Beamdog. This gives all players easier access to some amazing (and free) community adventures. For example, we are honoured to offer both the full Aielund Saga by Savant, and the Auren Saga (Almraiven / Shadewood) by Fester Pot. Now all you need to do to play them is click Download (...and wait a bit).
Aielund Saga
By Savant Embark on a journey through the Kingdom of Aielund, a realm weakened by war and beset by enemies from without and within. Defend the people of Aielund against monsters from mythology, and ones that clothe themselves in a more human form. Uncover evidence of foul play, delve into the deepest caves, travel the breadth of the land and even the planes of reality. Secure the kingdom from foes more powerful than you can imagine, while you rise from simple mercenaries into the mightiest of heroic legends.
Auren Saga
By PJH The Auren Society of Weavers draw their power from a Weave that spans into the Land of the Dead. It allows such members to grasp a better understanding of the spirits that roam that realm and use it to their advantage. It is this very Weave one will use to scour murderous scenes and unravel the mysterious deaths that surround the region of Calimshan.
TrueType Fonts and UI scaling
The game now supports TrueType fonts (and fractional/decimal point UI scaling). This means when you adjust the User Interface the text scales much more clearly for readability.
Note: Currently, fonts cannot be overridden by custom content, but we are looking at options for a future release.
Water Visuals
We’ve added brand new water visuals! Water now has realistic reflections and refractions to make your adventures more beautiful.
Art Changes
Added three contest winner portraits to base game (Aragnosh: human male, Seafaref: half-elf female, Seafarem: half-elf male)
Fixed torch model flicker
Fixed forest facelift tileset (ttf02) referring to missing envmap, resulting in transparent metal textures
Pathfinding Improvements
We have been carefully making some changes to pathfinding to improve efficiency and reliability. Here are some of the technical details of the changes:
Pathfinding on Windows, macOS and Linux no longer uses simplified pathfinding at certain distances.
Fixed inaccurate calculation of grid step tolerance when using grid pathfinding.
Fixed potential endless loops with ActionMoveAwayFromLocation.
Fixed inconsistencies between when to use Z when comparing distances, which would result in certain actions failing when they shouldn't
Added lacking safety checks which was causing pathfinding to floor performance if trying to interact with a door that was unreachable
Server settings: Removed experimental enhanced pathfinding toggle (now always on).
Fixed some minor errors related to collision resulting in imprecisions
Fixed interacting with objects when Z-offset was implied (e.g. stairs). This should alleviate some troubles where having to manually force the PC near the object before it would interact
Fixed a case where creatures failed to pathfind to a near point in a straight line
New Scripting
Visual Object Transform Lerping
Script commands can now transform objects smoothly over time.
All existing visual transform types (translate, rotate, scale) can be applied this way, and there is a built in selection of algorithms available.
New Script Commands
This release adds a set of new script commands:
// Returns the currently executing event (EVENT_SCRIPT_*) or 0 if not determinable. // Note: Will return 0 in DelayCommand/AssignCommand. ExecuteScript(Chunk) will inherit their event ID from their parent event. int GetCurrentlyRunningEvent(); // Get the integer parameter of eEffect at nIndex. // * nIndex bounds: 0 >= nIndex < 8. // * Some experimentation will be needed to find the right index for the value you wish to determine. // Returns: the value or 0 on error/when not set. int GetEffectInteger(effect eEffect, int nIndex); // Get the float parameter of eEffect at nIndex. // * nIndex bounds: 0 >= nIndex < 4. // * Some experimentation will be needed to find the right index for the value you wish to determine. // Returns: the value or 0.0f on error/when not set. float GetEffectFloat(effect eEffect, int nIndex); // Get the string parameter of eEffect at nIndex. // * nIndex bounds: 0 >= nIndex < 6. // * Some experimentation will be needed to find the right index for the value you wish to determine. // Returns: the value or "" on error/when not set. string GetEffectString(effect eEffect, int nIndex); // Get the object parameter of eEffect at nIndex. // * nIndex bounds: 0 >= nIndex < 4. // * Some experimentation will be needed to find the right index for the value you wish to determine. // Returns: the value or OBJECT_INVALID on error/when not set. object GetEffectObject(effect eEffect, int nIndex); // Get the vector parameter of eEffect at nIndex. // * nIndex bounds: 0 >= nIndex < 2. // * Some experimentation will be needed to find the right index for the value you wish to determine. // Returns: the value or {0.0f, 0.0f, 0.0f} on error/when not set. vector GetEffectVector(effect eEffect, int nIndex); // Check if nBaseItemType fits in oTarget's inventory. // Note: Does not check inside any container items possessed by oTarget. // * nBaseItemType: a BASE_ITEM_* constant. // * oTarget: a valid creature, placeable or item. // Returns: TRUE if the baseitem type fits, FALSE if not or on error. int GetBaseItemFitsInInventory(int nBaseItemType, object oTarget); // Get oObject's local cassowary variable reference sVarName // * Return value on error: empty solver // * NB: cassowary types are references, same as objects. // Unlike scalars such as int and string, solver references share the same data. // Modifications made to one reference are reflected on others. cassowary GetLocalCassowary(object oObject, string sVarName); // Set a reference to the given solver on oObject. // * NB: cassowary types are references, same as objects. // Unlike scalars such as int and string, solver references share the same data. // Modifications made to one reference are reflected on others. void SetLocalCassowary(object oObject, string sVarName, cassowary cSolver); // Delete local solver reference. // * NB: cassowary types are references, same as objects. // Unlike scalars such as int and string, solver references share the same data. // Modifications made to one reference are reflected on others. void DeleteLocalCassowary(object oObject, string sVarName); // Clear out this solver, removing all state, constraints and suggestions. // This is provided as a convenience if you wish to reuse a cassowary variable. // It is not necessary to call this for solvers you simply want to let go out of scope. void CassowaryReset(cassowary cSolver); // Add a constraint to the system. // * The constraint needs to be a valid comparison equation, one of: >=, ==, <=. // * This implementation is a linear constraint solver. // * You cannot multiply or divide variables and expressions with each other. // Doing so will result in a error when attempting to add the constraint. // (You can, of course, multiply or divide by constants). // * fStrength must be >= CASSOWARY_STRENGTH_WEAK && <= CASSOWARY_STRENGTH_REQUIRED. // * Any referenced variables can be retrieved with CassowaryGetValue(). // * Returns "" on success, or the parser/constraint system error message. string CassowaryConstrain(cassowary cSolver, string sConstraint, float fStrength = CASSOWARY_STRENGTH_REQUIRED); // Suggest a value to the solver. // * Edit variables are soft constraints and exist as an optimisation for complex systems. // You can do the same with Constrain("v == 5", CASSOWARY_STRENGTH_xxx); but edit variables // allow you to suggest values without having to rebuild the solver. // * fStrength must be >= CASSOWARY_STRENGTH_WEAK && < CASSOWARY_STRENGTH_REQUIRED // Suggested values cannot be required, as suggesting a value must not invalidate the solver. void CassowarySuggestValue(cassowary cSolver, string sVarName, float fValue, float fStrength = CASSOWARY_STRENGTH_STRONG); // Get the value for the given variable, or 0.0 on error. float CassowaryGetValue(cassowary cSolver, string sVarName); // Gets a printable debug state of the given solver, which may help you debug // complex systems. string CassowaryDebug(cassowary cSolver); // Overrides a given strref to always return sValue instead of what is in the TLK file. // Setting sValue to "" will delete the override void SetTlkOverride(int nStrRef, string sValue=""); // Constructs a custom itemproperty given all the parameters explicitly. // This function can be used in place of all the other ItemPropertyXxx constructors // Use GetItemProperty{Type,SubType,CostTableValue,Param1Value} to see the values for a given itemproperty. itemproperty ItemPropertyCustom(int nType, int nSubType=-1, int nCostTableValue=-1, int nParam1Value=-1);
NWScript Debugger
We’ve revived the script debugger with several improvements:
Added back script debugger binary into bin/win32/.
Added configuration keys to UI.
Fixed incorrect address parsing resulting in it not connecting. Also fixes setting debugger addresses other than localhost.
Fixed heap overflow parsing NDB files resulting in crashes.
Now renders all internal fields of CGameEffect.
Now renders all internal state of CScriptEvent.
Now renders cswysolver and sqlquery types instead of crashing in pitiful confusion.
Will no longer attempt to launch from within nwmain, you now need to run the binary by hand. On the plus side, it should now work on Linux and Mac (via wine/wine32to64).
Other Feature Changes
In addition to the major features listed above, we’ve added dozens of minor polish features to improve the game:
GUI/NUI: Implemented floating point UI scaling.
UI: Allow exceeding safe UI scale if needed for accessibility reasons. There is a toggle under Config->UI->Accessibility->Advanced.
GUI/NUI: Implemented TTF font rendering.
Input: Added new manual config key game.language.codepage that can be used to force a different codepage (e.g. cp1251 for cyrillic). Together with a TTF override, this should allow modding in compatibility for russian language overrides.
NUI: Implemented 9-slice gui skinning.
NUI: Now using a cassowary constraint solver system to layout widgets
Floating damage numbers are now colourised based on the damage type taken.
Add config toggle that allows showing damage numbers as totals or split
NUI: Redesigned the Options UI.
NUI: Redesigned the NWSync Storage Management UI. (Now called Storage in the menu, and also accessible via the new game launcher).
NUI: Fixed up some other UI panels to layout properly even when scaled.
NUI: Added a panel to show Open Source Licences to config UI.
NUI: Made sound effects more immersive/in line with base UI usage (different click noises depending on widget).
GUI: Added a windowed/fullscreen/borderless dropdown to the Config UI and fixed the mode sometimes not initialising correctly.
Async loading of files and images via http streaming. (Backend system work)
ResMan: Movies can now be stored and played from override, ERF (hak, mod) and NWSync.
SQLite: Added a configurable busy timeout, so that just externally running something on a campaign/nwsync database doesn’t immediately abort queries running in the game. (3s default)
SQLite: Added builtin functions for NWCompressedBuf compression, base64 de/encode, and hashing. See SQLITE_Readme.md in data.
NWSync: Client now downloads wbm movie files if part of the manifest.
HTTP: Added a disk caching service to avoid repeated requests; added alias CACHE: (USERDIR/cache) by default. Configurable in settings, default max size is 100MB.
Base: Game windowed size is now 1024x768 by default, because some UI just got too big.
Base: TLK files now have a local cache, improving repeated lookup performance.
Base: API cleanup for NWNX support
Server: Removed some superfluous service updates (very minor perf).
Renderer: Tweaks to palettes for PLTs to prevent metallic looks. Tweaks to PLTs for armour to prevent alpha dithering. Check out the comparison here
Renderer: Added unique shader for particles, with soft particle rendering and enabled particle blending with fog. Check out soft particle shader here and fog rendering here
Renderer: Improved colour overflow for transparent objects. Check out the comparison here
Renderer: Unified and simplified the shader setup, allowing the default vs/fslit_nm/sm shaders to be used for all standard PBR setups
Renderer: Made displacement offset a uniform that can be passed from materials to set a base level. To use it, add parameter float DisplacementOffset in a material. By default, the shaders assume the base (zero) level of the heightmap is 1 (white), so if base level is 0.6, the value needs to be -0.4, etc
Renderer: Made alpha level adhere to Fresnel effects at high shader quality.
Renderer/Debug UI: Added a dropdown to select various rendering modes helpful for debugging (Material modes, Lighting channels, ..)
Renderer: Improvements to FBO, grant all shaders access to color and depth buffer textures
Renderer: Quickbar icons can now reload via SetTextureOverride.
Renderer: Fixed broken normal and tangent generation for skinmeshes due to invalidated render hints. Improved shader picking based on tangets/handedness.
Renderer: Shader compilation errors are now printed to the game log file.
Renderer: Separated out area wide lighting from area point lights. (Small perf improvement)
Renderer/OVT: WALKDIST and RUNDIST now scales properly with visual transforms for you, in order to keep footsteps in sync
Renderer: Added material files for icy tileset so that ice no longer appears metallic
Renderer: Added material files for male and female old human heads so their hair no longer appears metallic
Fixes
In addition to feature changes, this patch release also contains a sizeable selection of bug fixes:
Fixed main menu music not playing when intro movies are skipped.
Renderer: Fixed a bad hashing algo for PLT textures resulting in excessive cache misses.
Renderer: Fixed incorrectly initialised null textures in some cases, resulting in incorrect textures and unnecessary texture binds.
Renderer: Removed redundant skinmesh bone remapping. Slight performance improvement, especially for models with many nodes.
Renderer: Made texture management more efficient, streamline for future changes to PBR.
Renderer/ResMan: Disabled the async model loader. Addresses the bodyparts missing issue. #145
Renderer: Maximum number of bones of a single skinmesh is now 64, to match mobile. This should fix the case where the game runs with 1-2fps on some GPUs.
Renderer: Fixed crashes due to NULL textures.
Renderer: Fixed crashing due to broken skinmesh models.
Renderer: Regression: Fixed compiled light models having issues due to memory misalignment/struct packing.
Renderer: Fixed an issue that would potentially cause scenes using custom shaders to malfunction in the event that a user changes video options in game.
Renderer: Fix text input caret pulsing when paused. Fix caret colouring following text in some situations
Renderer: Safeguard against crashes when an animation was missing
Renderer: Optimised animesh buffer uploading; reduce time spent on GUI rendering.
Renderer: Make the `decal` property actually disable lighting, rather than just self-illuminating brightly. Fixed some GUI scenes by adding decal to txis.
Renderer: Fixed issue with darkness and similar negative light effects always being pushed to the end of the light priority list.
Renderer: Fixed tile lights being added to the BSP before having their radii set (causing minor imprecisions)
Renderer: Fixed an issue with setting custom shaders when objects were using just one material for all parts while having more than one part
Renderer: Fixed a minor regression with the high contrast shader
Renderer: Removed a good chunk of dead/redundant code
Nuklear: Fixed a memleak that would degrade long play sessions
VM: Fixed crash when calling DestroyArea() on a invalid object.
VM: Stopped ExecuteScriptChunk from writing “!chunk.ndb” to override.
VM: sqlite commands: Added missing newline in log output.
VM: Fixed issue in string handling (also: VM: SubString()) that would have resulted in memory leaks or crashes.
Game: Don’t use weapon AB for ranged touch attacks anymore.
Fixes to passive attack target selection. This should address Cleave etc. sometimes failing to find a followup target. #172
Game: Effect Miss Chance/Blind Fighting: Fix erroneous re-roll against target concealment, fix effect being ignored against blind fighting. #15
Game: Fixed error in levelup validation related to SkillpointModifierAbility being starred out #180
Game: Fixed ResistSpell() when called from AOE spell scripts.
Game: Fixed classstat modifiers applying incorrectly to polymorph. #185
Fixed the DM flag being set incorrectly on player characters when copying files around. #216
Fixed a rare crash in game object update netcode when writing out the party state of a player that just exited
Fixed a loop wraparound overflow game hang/crash when removing effect icons
Game: Fixed a crash when the game tried to write a log entry as it was shutting down
Emitters: fixed them not interpolating if not both size_y start and end were set (correct behaviour is to do if either is non-zero)
Game: Never allow the window size to shrink below (100, 100). This works around the case where the window was too small to spot or resize back up
Fixed a crash when clicking the Recommended button on chargen package selection with no packages available
Fixed a string type conversion error resulting in some empty strings in UI
http:// Made http errors more verbose. Verbosity is Friend
Fixed nwsync-enabled savegames not loading due to missing TLK error
Input: Character input is now correctly converted from the language charset. This should address broken polish text input
NUI: Fixed codepage glyphs cutting text off due to incorrectly-set codepage.
NUI: Fix window flickering from (0,0) to center pos on first frame after opening.
NUI: Fix window sometimes scaling down to (0,0).
NUI: Pressing Escape now closes the currently focused window.
NUI: Fixed UI not properly hiding when pressing Alt-F4/X to close the game.
NUI Skinning: Made font white in all widgets, instead of grey.
NUI Skinning: Skinned text input box.
NUI Skinning: Title bars now look like native windows.
NUI Skinning: Close/Minimise buttons match native style.
NUI Skinning: Fixed combo box style.
NUI Skinning: Window and group borders are now rendered 9-sliced.
NUI Skinning: Fixed scrollbars not aligning properly.
NUI: Fixed windows not properly auto-centering when ui-scaled.
NUI: Fixed line fitting code to no longer repeat words when word-wrapping; improved performance to only do line flowing once.
NUI/GUI: Fixed TTF font rendering for Polish. #191 #235
GUI: Fixed input caret; now back with extra cyan and more blinkage.
GUI: Fixed password input box not accepting your passwords, no matter how good they were.
Movie player: Optimised so it doesn’t create the movie texture as often.
Toolset: Fixed floating point drifting for X/YOrientation
Toolset: Now reads DEVELOPMENT: (but it might not live-reload the same as the game would).
Toolset: Fixed some backgrounds not filling the full icon pane. #240
Linux: Fixed exclusive(fullscreen) and borderless window modes.
ResMan: Fixed crash that could happen when exiting the game, especially seen after failing to load a savegame.
Renderer: Restore the check for bumpshinytexture, fixing regression where meshes appeared transparent or invisible #298
Renderer: Fixed static lights not updating properly. #290
Config UI: Don’t show horizontal scrollbar when UI scaled.
VM: Fixed SetColor() not updating in some cases (such as heads/hair)
Savegames: When loading a savegame with a missing manifest, attempt to load the newest manifest of the same UUID instead (upgrade path for New Game UI modules). Emit a message to the client log indicating such - no UI support for showing this yet.
Known Issues
Steam Workshop | Modules in the Steam Workshop do not show in the game launcher