diff --git a/_module/ncs/airlessaura1.ncs b/_module/ncs/airlessaura1.ncs
index 8c294006..7961ea56 100644
Binary files a/_module/ncs/airlessaura1.ncs and b/_module/ncs/airlessaura1.ncs differ
diff --git a/_module/ncs/altardeath2.ncs b/_module/ncs/altardeath2.ncs
index 7a7ab4e3..771f9243 100644
Binary files a/_module/ncs/altardeath2.ncs and b/_module/ncs/altardeath2.ncs differ
diff --git a/_module/ncs/array_example.ncs b/_module/ncs/array_example.ncs
new file mode 100644
index 00000000..4515ec5d
Binary files /dev/null and b/_module/ncs/array_example.ncs differ
diff --git a/_module/ncs/darktrigger.ncs b/_module/ncs/darktrigger.ncs
index a3f08a15..3678fcf3 100644
Binary files a/_module/ncs/darktrigger.ncs and b/_module/ncs/darktrigger.ncs differ
diff --git a/_module/ncs/darktrigger2a.ncs b/_module/ncs/darktrigger2a.ncs
index 025fa0b7..0d166dc2 100644
Binary files a/_module/ncs/darktrigger2a.ncs and b/_module/ncs/darktrigger2a.ncs differ
diff --git a/_module/ncs/horsecrettrig1.ncs b/_module/ncs/horsecrettrig1.ncs
index 94310027..55dd910b 100644
Binary files a/_module/ncs/horsecrettrig1.ncs and b/_module/ncs/horsecrettrig1.ncs differ
diff --git a/_module/ncs/horsecrettrig2.ncs b/_module/ncs/horsecrettrig2.ncs
index 46ff5170..a248b309 100644
Binary files a/_module/ncs/horsecrettrig2.ncs and b/_module/ncs/horsecrettrig2.ncs differ
diff --git a/_module/ncs/silenttrigger.ncs b/_module/ncs/silenttrigger.ncs
index 728e58e1..9e22b5b0 100644
Binary files a/_module/ncs/silenttrigger.ncs and b/_module/ncs/silenttrigger.ncs differ
diff --git a/_module/ncs/silenttrigger2.ncs b/_module/ncs/silenttrigger2.ncs
index 2274260f..90e8f4e5 100644
Binary files a/_module/ncs/silenttrigger2.ncs and b/_module/ncs/silenttrigger2.ncs differ
diff --git a/_module/ncs/spawnb_cc_dactiv.ncs b/_module/ncs/spawnb_cc_dactiv.ncs
index 7e94385e..77e80d77 100644
Binary files a/_module/ncs/spawnb_cc_dactiv.ncs and b/_module/ncs/spawnb_cc_dactiv.ncs differ
diff --git a/_module/nss/array_example.nss b/_module/nss/array_example.nss
new file mode 100644
index 00000000..53f13f8f
--- /dev/null
+++ b/_module/nss/array_example.nss
@@ -0,0 +1,143 @@
+//#include "inc_array"
+#include "nwnx_time"
+
+// nwnx_data also includes inc_array, so don't double dip.
+#include "nwnx_data"
+
+void Log(string msg) 
+{
+    WriteTimestampedLogEntry(msg);
+}
+
+void TestArrayOnModule() 
+{
+
+    string array = "test";
+
+    // By default, temporary arrays are created on the module.
+    Array_PushBack_Str(array, "BItem1");
+    Array_PushBack_Str(array, "AItem2");
+    Array_PushBack_Str(array, "AItem3");
+    Array_PushBack_Str(array, "BItem2");
+    Array_Debug_Dump(array, "After first load");
+
+    int foo = Array_Find_Str(array, "AItem3");
+    Log("Found element AItem3 at index = " + IntToString(foo));
+
+    Array_Set_Str(array, 2, "Suck it up...");
+    Array_Debug_Dump(array, "After set 2 = 'Suck it up...'");
+
+    Array_Erase(array, 1);
+    Array_Debug_Dump(array, "After delete 1");
+
+    Array_PushBack_Str(array, "MItem1");
+    Array_PushBack_Str(array, "QItem2");
+    Array_PushBack_Str(array, "NItem3");
+    Array_PushBack_Str(array, "KItem2");
+
+    Array_Debug_Dump(array, "After add more");
+    Array_SortAscending(array);
+
+    Array_Debug_Dump(array, "After sort");
+
+    Array_Shuffle(array);
+    Array_Debug_Dump(array, "After shuffle");
+
+    Log( (Array_Contains_Str(array, "NItem3")) ? "Passed.. found it"  : "Failed.. should have found it" );
+    Log( (Array_Contains_Str(array, "KItem2")) ? "Passed.. found it"  : "Failed.. should have found it" );
+    Log( (Array_Contains_Str(array, "xxxxxx")) ? "Failed.. not found" : "Passed.. should not exist" );
+
+    Array_Clear(array);
+    // Load up the array with 100 entries
+    int i;
+	
+    struct NWNX_Time_HighResTimestamp b;
+    b = NWNX_Time_GetHighResTimeStamp();
+    Log("Start Time: " + IntToString(b.seconds) + "." + IntToString(b.microseconds));
+    for (i=0; i<1000; i++) 
+    {
+        Array_PushBack_Str(array, IntToString(d100()) + " xxx " + IntToString(i));
+    }
+    b = NWNX_Time_GetHighResTimeStamp();
+    Log("Loaded 1000: " + IntToString(b.seconds) + "." + IntToString(b.microseconds));
+    Array_Shuffle(array);
+    b = NWNX_Time_GetHighResTimeStamp();
+    Log("Shuffled 1000: " + IntToString(b.seconds) + "." + IntToString(b.microseconds));
+    for (i=5; i<995; i++) 
+    {
+        // Delete the third entry a bunch of times
+        Array_Erase(array, 3);
+    }
+    b = NWNX_Time_GetHighResTimeStamp();
+    Log("Delete ~990: " + IntToString(b.seconds) + "." + IntToString(b.microseconds));
+    Array_Debug_Dump(array, "After mass insert/delete");
+
+}
+
+void TestArrayOnChicken() 
+{
+    string array="chicken";
+    // Let's create an array "on" our favorite creature: the deadly nw_chicken
+    // Note - arrays aren't really attached to the item, but the module, and they
+    // are tagged with the objects string representation.
+    object oCreature = CreateObject(OBJECT_TYPE_CREATURE, "nw_chicken", GetStartingLocation());
+    if (!GetIsObjectValid(oCreature))
+    {
+        Log("NWNX_Creature test: Failed to create creature");
+        return;
+    }
+
+    Array_PushBack_Str(array, "BItem1", oCreature);
+    Array_PushBack_Str(array, "AItem2", oCreature);
+    Array_PushBack_Str(array, "AItem3", oCreature);
+    Array_PushBack_Str(array, "BItem2", oCreature);
+    Array_Debug_Dump(array, "After Chicken array load", oCreature);
+
+}
+
+void TestNWNXArray() 
+{
+    Log("");
+    Log("Start NWNX_Data test.");
+    string array = "test2";
+
+    NWNX_Data_Array_PushBack_Str(GetModule(), array, "XItem1");
+    NWNX_Data_Array_PushBack_Str(GetModule(), array, "ZItem2");
+    NWNX_Data_Array_PushBack_Str(GetModule(), array, "ZItem3");
+    NWNX_Data_Array_PushBack_Str(GetModule(), array, "XItem2");
+    Array_Debug_Dump(array, "After first load");
+
+    int foo = NWNX_Data_Array_Find_Str(GetModule(), array, "ZItem3");
+    Log("Found element AItem3 at index = " + IntToString(foo));
+
+    NWNX_Data_Array_Set_Str(GetModule(), array, 2, "Suck it up...");
+    Array_Debug_Dump(array, "After set 2 = 'Suck it up...'");
+
+    NWNX_Data_Array_Erase(NWNX_DATA_TYPE_STRING, GetModule(), array, 1);
+    Array_Debug_Dump(array, "After delete 1");
+
+    NWNX_Data_Array_PushBack_Str(GetModule(), array, "MItem1");
+    NWNX_Data_Array_PushBack_Str(GetModule(), array, "QItem2");
+    NWNX_Data_Array_PushBack_Str(GetModule(), array, "NItem3");
+    NWNX_Data_Array_PushBack_Str(GetModule(), array, "KItem2");
+
+    Array_Debug_Dump(array, "After add more");
+    NWNX_Data_Array_SortAscending(NWNX_DATA_TYPE_STRING, GetModule(), array);
+
+    Array_Debug_Dump(array, "After sort");
+
+}
+
+// Uncomment and assign to some event click.
+/* */
+void main() 
+{
+    Log("Start");
+
+    TestArrayOnModule();
+
+    TestArrayOnChicken();
+
+    TestNWNXArray();
+}
+/* */
diff --git a/_module/nss/nwnx_damage.nss b/_module/nss/nwnx_damage.nss
index c3977cb1..352d810c 100644
--- a/_module/nss/nwnx_damage.nss
+++ b/_module/nss/nwnx_damage.nss
@@ -43,6 +43,7 @@ struct NWNX_Damage_DamageEventData
     int iCustom17; ///< Custom17 damage
     int iCustom18; ///< Custom18 damage
     int iCustom19; ///< Custom19 damage
+    int iSpellId; ///< The spell id associated with the damage or -1 if not known.
 };
 
 /// @struct NWNX_Damage_AttackEventData
@@ -222,6 +223,7 @@ struct NWNX_Damage_DamageEventData NWNX_Damage_GetDamageEventData()
     data.iCustom17    = NWNX_GetReturnValueInt();
     data.iCustom18    = NWNX_GetReturnValueInt();
     data.iCustom19    = NWNX_GetReturnValueInt();
+    data.iSpellId     = NWNX_GetReturnValueInt();
 
     return data;
 }
diff --git a/_module/nss/nwnx_httpclient.nss b/_module/nss/nwnx_httpclient.nss
new file mode 100644
index 00000000..ea10d20a
--- /dev/null
+++ b/_module/nss/nwnx_httpclient.nss
@@ -0,0 +1,113 @@
+/// @addtogroup httpclient HTTPClient
+/// @brief NWNX HTTPClient
+/// @{
+/// @file nwnx_httpclient.nss
+#include "nwnx"
+
+const string NWNX_HTTPClient = "NWNX_HTTPClient"; ///< @private
+
+/// @name Request Types
+/// @anchor request_types
+///
+/// @{
+const int NWNX_HTTPCLIENT_REQUEST_METHOD_GET     = 0;
+const int NWNX_HTTPCLIENT_REQUEST_METHOD_POST    = 1;
+const int NWNX_HTTPCLIENT_REQUEST_METHOD_DELETE  = 2;
+const int NWNX_HTTPCLIENT_REQUEST_METHOD_PATCH   = 3;
+const int NWNX_HTTPCLIENT_REQUEST_METHOD_PUT     = 4;
+const int NWNX_HTTPCLIENT_REQUEST_METHOD_OPTION  = 5;
+const int NWNX_HTTPCLIENT_REQUEST_METHOD_HEAD    = 6;
+///@}
+
+/// @name Content Types
+/// @anchor content_types
+///
+/// @{
+const int NWNX_HTTPCLIENT_CONTENT_TYPE_HTML            = 0;
+const int NWNX_HTTPCLIENT_CONTENT_TYPE_PLAINTEXT       = 1;
+const int NWNX_HTTPCLIENT_CONTENT_TYPE_JSON            = 2;
+const int NWNX_HTTPCLIENT_CONTENT_TYPE_FORM_URLENCODED = 3;
+const int NWNX_HTTPCLIENT_CONTENT_TYPE_XML             = 4;
+///@}
+
+/// @name HTTP Authentication Types
+/// @anchor auth_types
+///
+/// @{
+const int NWNX_HTTPCLIENT_AUTH_TYPE_NONE         = 0;
+const int NWNX_HTTPCLIENT_AUTH_TYPE_BASIC        = 1;
+const int NWNX_HTTPCLIENT_AUTH_TYPE_DIGEST       = 2;
+const int NWNX_HTTPCLIENT_AUTH_TYPE_BEARER_TOKEN = 3;
+///@}
+
+/// A structure for an HTTP Client Request
+struct NWNX_HTTPClient_Request
+{
+    int nRequestMethod; ///< A @ref request_types "Request Type"
+    string sTag; ///< A unique tag for this request
+    string sHost; ///< The host domain name/IP address
+    string sPath; ///< The path for the url (include the leading /)
+    string sData; ///< The data being sent
+    int nContentType; ///< A @ref content_types "Content Type"
+    int nAuthType; ///< An @ref auth_types "Authentication Type"
+    string sAuthUserOrToken; ///< The authentication username or token
+    string sAuthPassword; ///< The authentication password (ignored if just using a token)
+    int nPort; ///< The host port
+    string sHeaders; ///< Pipe (|) delimited header pairs, e.g. "User-Agent: My NWNX HTTP Client|Accept: application/vnd.github.v3+json"
+};
+
+/// @brief Sends an http method to the given host.
+/// @param s The structured NWNX_HTTPClient_Request information.
+/// @return A unique identifier for the request for later access in the REQUEST_ID event data.
+int NWNX_HTTPClient_SendRequest(struct NWNX_HTTPClient_Request s);
+
+/// @brief Returns an NWNX_HTTP_Client_Request structure
+/// @param nRequestId The request id returned from NWNX_HTTPClient_SendRequest()
+/// @return The structured NWNX_HTTPClient_Request information
+struct NWNX_HTTPClient_Request NWNX_HTTPClient_GetRequest(int nRequestId);
+
+/// @}
+
+int NWNX_HTTPClient_SendRequest(struct NWNX_HTTPClient_Request s)
+{
+    string sFunc = "SendRequest";
+    NWNX_PushArgumentString(s.sHeaders);
+    NWNX_PushArgumentInt(s.nPort);
+    NWNX_PushArgumentString(s.sAuthPassword);
+    NWNX_PushArgumentString(s.sAuthUserOrToken);
+    NWNX_PushArgumentInt(s.nAuthType);
+    NWNX_PushArgumentString(s.sData);
+    NWNX_PushArgumentInt(s.nContentType);
+    NWNX_PushArgumentString(s.sPath);
+    NWNX_PushArgumentString(s.sHost);
+    NWNX_PushArgumentInt(s.nRequestMethod);
+    NWNX_PushArgumentString(s.sTag);
+    NWNX_CallFunction(NWNX_HTTPClient, sFunc);
+
+    return NWNX_GetReturnValueInt();
+}
+
+struct NWNX_HTTPClient_Request NWNX_HTTPClient_GetRequest(int nRequestId)
+{
+
+    string sFunc = "GetRequest";
+
+    NWNX_PushArgumentInt(nRequestId);
+    NWNX_CallFunction(NWNX_HTTPClient, sFunc);
+
+    struct NWNX_HTTPClient_Request s;
+
+    s.sTag             = NWNX_GetReturnValueString();
+    s.nRequestMethod   = NWNX_GetReturnValueInt();
+    s.sHost            = NWNX_GetReturnValueString();
+    s.sPath            = NWNX_GetReturnValueString();
+    s.nContentType     = NWNX_GetReturnValueInt();
+    s.sData            = NWNX_GetReturnValueString();
+    s.nAuthType        = NWNX_GetReturnValueInt();
+    s.sAuthUserOrToken = NWNX_GetReturnValueString();
+    s.sAuthPassword    = NWNX_GetReturnValueString();
+    s.nPort            = NWNX_GetReturnValueInt();
+    s.sHeaders         = NWNX_GetReturnValueString();
+
+    return s;
+}
diff --git a/_module/nss/nwnx_item.nss b/_module/nss/nwnx_item.nss
index 71d4091f..01cd4ac0 100644
--- a/_module/nss/nwnx_item.nss
+++ b/_module/nss/nwnx_item.nss
@@ -69,7 +69,12 @@ void NWNX_Item_SetBaseItemType(object oItem, int nBaseItem);
 ///
 /// [1] When specifying per-part coloring, the value 255 corresponds with the logical
 /// function 'clear colour override', which clears the per-part override for that part.
-void NWNX_Item_SetItemAppearance(object oItem, int nType, int nIndex, int nValue);
+/// @param oItem The item
+/// @param nType The type
+/// @param nIndex The index
+/// @param nValue The value
+/// @param bUpdateCreatureAppearance If TRUE, also update the appearance of oItem's possessor. Only works for armor/helmets/cloaks. Will remove the item from the quickbar as side effect.
+void NWNX_Item_SetItemAppearance(object oItem, int nType, int nIndex, int nValue, int bUpdateCreatureAppearance = FALSE);
 
 /// @brief Return a string containing the entire appearance for an item.
 /// @sa NWNX_Item_RestoreItemAppearance
@@ -187,10 +192,11 @@ void NWNX_Item_SetBaseItemType(object oItem, int nBaseItem)
     NWNX_CallFunction(NWNX_Item, sFunc);
 }
 
-void NWNX_Item_SetItemAppearance(object oItem, int nType, int nIndex, int nValue)
+void NWNX_Item_SetItemAppearance(object oItem, int nType, int nIndex, int nValue, int bUpdateCreatureAppearance = FALSE)
 {
     string sFunc = "SetItemAppearance";
 
+    NWNX_PushArgumentInt(bUpdateCreatureAppearance);
     NWNX_PushArgumentInt(nValue);
     NWNX_PushArgumentInt(nIndex);
     NWNX_PushArgumentInt(nType);
diff --git a/_module/nss/nwnx_object.nss b/_module/nss/nwnx_object.nss
index 03a99e4e..acab7d82 100644
--- a/_module/nss/nwnx_object.nss
+++ b/_module/nss/nwnx_object.nss
@@ -418,6 +418,21 @@ int NWNX_Object_GetLastSpellInstant();
 /// @param oCreator The new creator of the trap. Any non-creature creator will assign OBJECT_INVALID (similar to toolset-laid traps)
 void NWNX_Object_SetTrapCreator(object oObject, object oCreator);
 
+/// @brief Return the name of the object for nLanguage.
+/// @param oObject an object
+/// @param nLanguage A PLAYER_LANGUAGE constant.
+/// @param nGender   Gender to use, 0 or 1.
+/// @return The localized string.
+string NWNX_Object_GetLocalizedName(object oObject, int nLanguage, int nGender = 0);
+
+/// @brief Set the name of the object as set in the toolset for nLanguage.
+/// @note You may have to SetName(oObject, "") for the translated string to show.
+/// @param oObject an object
+/// @param sName New value to set
+/// @param nLanguage A PLAYER_LANGUAGE constant.
+/// @param nGender   Gender to use, 0 or 1.
+void NWNX_Object_SetLocalizedName(object oObject, string sName, int nLanguage, int nGender = 0);
+
 /// @}
 
 int NWNX_Object_GetLocalVariableCount(object obj)
@@ -1036,3 +1051,27 @@ void NWNX_Object_SetTrapCreator(object oObject, object oCreator)
     NWNX_PushArgumentObject(oObject);
     NWNX_CallFunction(NWNX_Object, sFunc);
 }
+
+string NWNX_Object_GetLocalizedName(object oObject, int nLanguage, int nGender = 0)
+{
+    string sFunc = "GetLocalizedName";
+
+    NWNX_PushArgumentInt(nGender);
+    NWNX_PushArgumentInt(nLanguage);
+    NWNX_PushArgumentObject(oObject);
+
+    NWNX_CallFunction(NWNX_Object, sFunc);
+    return NWNX_GetReturnValueString();
+}
+
+void NWNX_Object_SetLocalizedName(object oObject, string sName, int nLanguage, int nGender = 0)
+{
+    string sFunc = "SetLocalizedName";
+
+    NWNX_PushArgumentInt(nGender);
+    NWNX_PushArgumentInt(nLanguage);
+    NWNX_PushArgumentString(sName);
+    NWNX_PushArgumentObject(oObject);
+
+    NWNX_CallFunction(NWNX_Object, sFunc);
+}
diff --git a/_module/nss/nwnx_player.nss b/_module/nss/nwnx_player.nss
index dfb7e3d7..3f4ab659 100644
--- a/_module/nss/nwnx_player.nss
+++ b/_module/nss/nwnx_player.nss
@@ -329,7 +329,8 @@ void NWNX_Player_SetCreatureNameOverride(object oPlayer, object oCreature, strin
 /// @param oPlayer The player to display the text to.
 /// @param oCreature The creature to display the text above.
 /// @param sText The text to display.
-void NWNX_Player_FloatingTextStringOnCreature(object oPlayer, object oCreature, string sText);
+/// @param bChatWindow If TRUE, sText will be displayed in oPlayer's chat window.
+void NWNX_Player_FloatingTextStringOnCreature(object oPlayer, object oCreature, string sText, int bChatWindow = TRUE);
 
 /// @brief Toggle oPlayer's PlayerDM status.
 /// @note This function does nothing for actual DMClient DMs or players with a client version < 8193.14
@@ -396,6 +397,10 @@ void NWNX_Player_CloseStore(object oPlayer);
 /// @note Overrides will not persist through relogging.
 void NWNX_Player_SetTlkOverride(object oPlayer, int nStrRef, string sOverride, int bRestoreGlobal = TRUE);
 
+/// @brief Make the player reload it's TlkTable.
+/// @param oPlayer The player.
+void NWNX_Player_ReloadTlk(object oPlayer);
+
 /// @brief Update wind for oPlayer only.
 /// @param oPlayer The player.
 /// @param vDirection The Wind's direction.
@@ -445,6 +450,10 @@ void NWNX_Player_SendPartyInvite(object oPlayer, object oInviter, int bForceInvi
 /// @return the TURD object of oPlayer, or OBJECT_INVALID if no TURD exists
 object NWNX_Player_GetTURD(object oPlayer);
 
+/// @brief Reloads the color palettes for oPlayer
+/// @param oPlayer The player to reload the color palette for
+void NWNX_Player_ReloadColorPalettes(object oPlayer);
+
 /// @}
 
 void NWNX_Player_ForcePlaceableExamineWindow(object player, object placeable)
@@ -910,10 +919,11 @@ void NWNX_Player_SetCreatureNameOverride(object oPlayer, object oCreature, strin
     NWNX_CallFunction(NWNX_Player, sFunc);
 }
 
-void NWNX_Player_FloatingTextStringOnCreature(object oPlayer, object oCreature, string sText)
+void NWNX_Player_FloatingTextStringOnCreature(object oPlayer, object oCreature, string sText, int bChatWindow = TRUE)
 {
     string sFunc = "FloatingTextStringOnCreature";
 
+    NWNX_PushArgumentInt(bChatWindow);
     NWNX_PushArgumentString(sText);
     NWNX_PushArgumentObject(oCreature);
     NWNX_PushArgumentObject(oPlayer);
@@ -1050,6 +1060,14 @@ void NWNX_Player_SetTlkOverride(object oPlayer, int nStrRef, string sOverride, i
     NWNX_CallFunction(NWNX_Player, sFunc);
 }
 
+void NWNX_Player_ReloadTlk(object oPlayer)
+{
+    string sFunc = "ReloadTlk";
+
+    NWNX_PushArgumentObject(oPlayer);
+    NWNX_CallFunction(NWNX_Player, sFunc);
+}
+
 void NWNX_Player_UpdateWind(object oPlayer, vector vDirection, float fMagnitude, float fYaw, float fPitch)
 {
     string sFunc = "UpdateWind";
@@ -1131,6 +1149,14 @@ object NWNX_Player_GetTURD(object oPlayer)
 
     NWNX_PushArgumentObject(oPlayer);
     NWNX_CallFunction(NWNX_Player, sFunc);
-    
+
     return NWNX_GetReturnValueObject();
 }
+
+void NWNX_Player_ReloadColorPalettes(object oPlayer)
+{
+    string sFunc = "ReloadColorPalettes";
+
+    NWNX_PushArgumentObject(oPlayer);
+    NWNX_CallFunction(NWNX_Player, sFunc);
+}
diff --git a/_module/nss/nwnx_sql.nss b/_module/nss/nwnx_sql.nss
index b3198962..f3ab3d20 100644
--- a/_module/nss/nwnx_sql.nss
+++ b/_module/nss/nwnx_sql.nss
@@ -65,6 +65,12 @@ void NWNX_SQL_PreparedObjectFull(int position, object value, int base64 = TRUE);
 /// @param position The nth ? in a prepared statement.
 void NWNX_SQL_PreparedNULL(int position);
 
+/// @brief Set the Json value of a prepared statement at given position. 
+/// Convienence function to match other Prepared(type) functions.
+/// @param position The nth ? in a prepared statement.
+/// @param value The value to set.
+void NWNX_SQL_PreparedJson(int position, json value);
+
 /// @brief Like NWNX_SQL_ReadDataInActiveRow, but for full serialized objects.
 ///
 /// The object will be deserialized and created in the game. New object ID is returned.
@@ -211,6 +217,12 @@ void NWNX_SQL_PreparedNULL(int position)
     NWNX_PushArgumentInt(position);
     NWNX_CallFunction(NWNX_SQL, sFunc);
 }
+void NWNX_SQL_PreparedJson(int position, json value)
+{
+    // Dump to string and continue as a string from here.
+    // Famously assuming we're sent valid Json here.
+    NWNX_SQL_PreparedString(position, JsonDump(value));
+}
 
 
 object NWNX_SQL_ReadFullObjectInActiveRow(int column = 0, object owner = OBJECT_INVALID, float x = 0.0, float y = 0.0, float z = 0.0, int base64 = TRUE)
diff --git a/_module/nss/nwnx_store.nss b/_module/nss/nwnx_store.nss
new file mode 100644
index 00000000..00b2014d
--- /dev/null
+++ b/_module/nss/nwnx_store.nss
@@ -0,0 +1,131 @@
+/// @addtogroup store
+/// @brief Functions exposing additional store properties.
+/// @{
+/// @file nwnx_store.nss
+#include "nwnx"
+
+const string NWNX_Store = "NWNX_Store"; ///< @private
+
+/// @brief Return status of a base item purchase status.
+/// @param oStore The store object.
+/// @param nBaseItem A BASE_ITEM_* value
+/// @return TRUE if the quest has been completed. -1 if the player does not have the journal entry.
+int NWNX_Store_GetIsRestrictedBuyItem(object oStore, int nBaseItem);
+
+/// @brief Return the blackmarket mark down of a store
+/// @param oStore The store object.
+/// @return mark down of a store, -1 on error
+int NWNX_Store_GetBlackMarketMarkDown(object oStore);
+
+/// @brief Set the blackmarket mark down of a store
+/// @param oStore The store object.
+/// @param nValue The amount.
+void NWNX_Store_SetBlackMarketMarkDown(object oStore, int nValue);
+
+/// @brief Return the mark down of a store
+/// @param oStore The store object.
+/// @return mark down of a store, -1 on error
+int NWNX_Store_GetMarkDown(object oStore);
+
+/// @brief Set the mark down of a store
+/// @param oStore The store object.
+/// @param nValue The amount.
+void NWNX_Store_SetMarkDown(object oStore, int nValue);
+
+/// @brief Return the mark up of a store
+/// @param oStore The store object.
+/// @return mark up of a store, -1 on error
+int NWNX_Store_GetMarkUp(object oStore);
+
+/// @brief Set the mark up of a store
+/// @param oStore The store object.
+/// @param nValue The amount.
+void NWNX_Store_SetMarkUp(object oStore, int nValue);
+
+/// @brief Return current customer count
+/// @param oStore The store object.
+/// @return count, or -1 on error
+int NWNX_Store_GetCurrentCustomersCount(object oStore);
+
+/// @}
+
+int NWNX_Store_GetIsRestrictedBuyItem(object oStore, int nBaseItem)
+{
+    string sFunc = "GetIsRestrictedBuyItem";
+
+    NWNX_PushArgumentInt(nBaseItem);
+    NWNX_PushArgumentObject(oStore);
+
+    NWNX_CallFunction(NWNX_Store, sFunc);
+    return NWNX_GetReturnValueInt();
+}
+
+int NWNX_Store_GetBlackMarketMarkDown(object oStore)
+{
+    string sFunc = "GetBlackMarketMarkDown";
+
+    NWNX_PushArgumentObject(oStore);
+
+    NWNX_CallFunction(NWNX_Store, sFunc);
+    return NWNX_GetReturnValueInt();
+}
+
+void NWNX_Store_SetBlackMarketMarkDown(object oStore, int nValue)
+{
+    string sFunc = "SetBlackMarketMarkDown";
+
+    NWNX_PushArgumentInt(nValue);
+    NWNX_PushArgumentObject(oStore);
+
+    NWNX_CallFunction(NWNX_Store, sFunc);
+}
+
+int NWNX_Store_GetMarkDown(object oStore)
+{
+    string sFunc = "GetMarkDown";
+
+    NWNX_PushArgumentObject(oStore);
+
+    NWNX_CallFunction(NWNX_Store, sFunc);
+    return NWNX_GetReturnValueInt();
+}
+
+void NWNX_Store_SetMarkDown(object oStore, int nValue)
+{
+    string sFunc = "SetMarkDown";
+
+    NWNX_PushArgumentInt(nValue);
+    NWNX_PushArgumentObject(oStore);
+
+    NWNX_CallFunction(NWNX_Store, sFunc);
+}
+
+int NWNX_Store_GetMarkUp(object oStore)
+{
+    string sFunc = "GetMarkUp";
+
+    NWNX_PushArgumentObject(oStore);
+
+    NWNX_CallFunction(NWNX_Store, sFunc);
+    return NWNX_GetReturnValueInt();
+}
+
+void NWNX_Store_SetMarkUp(object oStore, int nValue)
+{
+    string sFunc = "SetMarkUp";
+
+    NWNX_PushArgumentInt(nValue);
+    NWNX_PushArgumentObject(oStore);
+
+    NWNX_CallFunction(NWNX_Store, sFunc);
+}
+
+int NWNX_Store_GetCurrentCustomersCount(object oStore)
+{
+    string sFunc = "GetCurrentCustomersCount";
+
+    NWNX_PushArgumentObject(oStore);
+
+    NWNX_CallFunction(NWNX_Store, sFunc);
+    return NWNX_GetReturnValueInt();
+}
diff --git a/_module/nss/nwnx_util.nss b/_module/nss/nwnx_util.nss
index e1edf7a2..ef32e355 100644
--- a/_module/nss/nwnx_util.nss
+++ b/_module/nss/nwnx_util.nss
@@ -259,6 +259,10 @@ void NWNX_Util_UpdateClientObject(object oObjectToUpdate, object oPlayer = OBJEC
 /// @return TRUE if successful, FALSE on error.
 int NWNX_Util_CleanResourceDirectory(string sAlias, int nResType = 0xFFFF);
 
+/// @brief Return the filename of the tlk file.
+/// @return The name
+string NWNX_Util_GetModuleTlkFile();
+
 /// @}
 
 string NWNX_Util_GetCurrentScriptName(int depth = 0)
@@ -646,3 +650,10 @@ int NWNX_Util_CleanResourceDirectory(string sAlias, int nResType = 0xFFFF)
     NWNX_CallFunction(NWNX_Util, sFunc);
     return NWNX_GetReturnValueInt();
 }
+
+string NWNX_Util_GetModuleTlkFile()
+{
+    string sFunc = "GetModuleTlkFile";
+    NWNX_CallFunction(NWNX_Util, sFunc);
+    return NWNX_GetReturnValueString();
+}
diff --git a/_release/Path of Ascension [PRC8-CEP3].7z b/_release/Path of Ascension [PRC8-CEP3].7z
index dce1358a..f2f5cda0 100644
Binary files a/_release/Path of Ascension [PRC8-CEP3].7z and b/_release/Path of Ascension [PRC8-CEP3].7z differ