#include "inc_utility" #include "inc_sql" //defining directories //must be changed to each install //it will use a local string on the module named NWN_DIR if set const string DB_NAME = "prcnwnxleto"; const string DB_GATEWAY_VAR = "prcnwnxleto"; //set this to true if using build 18 or earlier of letoscript.dll //again this is a PRC switch /*YOU MUST ADD THE FOLLOWING TO YOUR ON CLIENT EXIT EVENT object oPC = GetExitingObject(); LetoPCExit(oPC); */ /*YOU MUST ADD THE FOLLOWING TO YOUR ON CLIENT ENTER EVENT object oPC = GetExitingObject(); LetoPCEnter(oPC); */ /* local copies for easy redistribution out of the PRC //these are the names of local variables, normally ints, to set on the module //set this if using any letoscript const string PRC_USE_LETOSCRIPT = "PRC_USE_LETOSCRIPT"; //* Set this to 1 if using build 18 const string PRC_LETOSCRIPT_PHEONIX_SYNTAX = "PRC_LETOSCRIPT_PHEONIX_SYNTAX"; //* Letoscript needs a string named PRC_LETOSCRIPT_NWN_DIR set to the //* directory of NWN. If it doesnt work, try different slash options: // \\ / \ const string PRC_LETOSCRIPT_NWN_DIR = "PRC_LETOSCRIPT_NWN_DIR"; //* Switch so that Unicorn will use the SQL database for SCO/RCO //* Must have the zeoslib.dlls installed for this //* //* UNTESTED!!! const string PRC_LETOSCRIPT_UNICORN_SQL = "PRC_LETOSCRIPT_UNICORN_SQL"; //* This is a string, not integer. //* If the IP is set, Letoscript will use ActivatePortal instead of booting. //* The IP and Password must be correct for your server or bad things will happen. //* - If your IP is non-static make sure this is kept up to date. //* //* See the Lexicon entry on ActivatePortal for more information. //* //* @see PRC_LETOSCRIPT_PORTAL_PASSWORD const string PRC_LETOSCRIPT_PORTAL_IP = "PRC_LETOSCRIPT_PORTAL_IP"; //* This is a string, not integer. //* If the IP is set, Letoscript will use ActivatePortal instead of booting. //* The IP and Password must be correct for your server or bad things will happen. //* - If your IP is non-static make sure this is kept up to date. //* //* See the Lexicon entry on ActivatePortal for more information. //* //* @see PRC_LETOSCRIPT_PORTAL_IP const string PRC_LETOSCRIPT_PORTAL_PASSWORD = "PRC_LETOSCRIPT_PORTAL_PASSWORD"; //* If set you must be using Unicorn. //* Will use getnewest bic instead of filename reconstruction (which fails if //* multiple characters have the same name) const string PRC_LETOSCRIPT_GETNEWESTBIC = "PRC_LETOSCRIPT_GETNEWESTBIC"; // * Set this if you are using SQLite (the built-in database in NWNX-ODBC2). // * This will use transactions and SQLite specific syntax. const string PRC_DB_SQLLITE = "PRC_DB_SQLLITE"; const int PRC_SQL_ERROR = 0; const int PRC_SQL_SUCCESS = 1; // Function defintions int GetPRCSwitch(string sSwitch); void PRC_SQLExecDirect(string sSQL); void PRC_SQLInit() { int i; // Placeholder for ODBC persistence string sMemory; for (i = 0; i < 8; i++) // reserve 8*128 bytes sMemory += "................................................................................................................................"; SetLocalString(GetModule(), "NWNX!ODBC!SPACER", sMemory); } void PRC_SQLExecDirect(string sSQL) { //PrintString(sSQL); SetLocalString(GetModule(), "NWNX!ODBC!EXEC", sSQL); } int PRC_SQLFetch() { string sRow; object oModule = GetModule(); SetLocalString(oModule, "NWNX!ODBC!FETCH", GetLocalString(oModule, "NWNX!ODBC!SPACER")); sRow = GetLocalString(oModule, "NWNX!ODBC!FETCH"); if (GetStringLength(sRow) > 0) { SetLocalString(oModule, "NWNX_ODBC_CurrentRow", sRow); return PRC_SQL_SUCCESS; } else { SetLocalString(oModule, "NWNX_ODBC_CurrentRow", ""); return PRC_SQL_ERROR; } } string PRC_SQLGetTick() { string sTick; if(GetPRCSwitch(PRC_DB_SQLLITE)) sTick = ""; else sTick = "`"; return sTick; } string PRC_SQLGetData(int iCol) { int iPos; string sResultSet = GetLocalString(GetModule(), "NWNX_ODBC_CurrentRow"); // find column in current row int iCount = 0; string sColValue = ""; iPos = FindSubString(sResultSet, "¬"); if ((iPos == -1) && (iCol == 1)) { // only one column, return value immediately sColValue = sResultSet; } else if (iPos == -1) { // only one column but requested column > 1 sColValue = ""; } else { // loop through columns until found while (iCount != iCol) { iCount++; if (iCount == iCol) sColValue = GetStringLeft(sResultSet, iPos); else { sResultSet = GetStringRight(sResultSet, GetStringLength(sResultSet) - iPos - 1); iPos = FindSubString(sResultSet, "¬"); } // special case: last column in row if (iPos == -1) iPos = GetStringLength(sResultSet); } } return sColValue; } string ReplaceSingleChars(string sString, string sTarget, string sReplace) { if (FindSubString(sString, sTarget) == -1) // not found return sString; int i; string sReturn = ""; string sChar; // Loop over every character and replace special characters for (i = 0; i < GetStringLength(sString); i++) { sChar = GetSubString(sString, i, 1); if (sChar == sTarget) sReturn += sReplace; else sReturn += sChar; } return sReturn; } int GetPRCSwitch(string sSwitch) { return GetLocalInt(GetModule(), sSwitch); } void DoDebug(string sString, object oAdditionalRecipient = OBJECT_INVALID) { SendMessageToPC(GetFirstPC(), sString); if(oAdditionalRecipient != OBJECT_INVALID) SendMessageToPC(oAdditionalRecipient, sString); WriteTimestampedLogEntry(sString); } //*/ //instanty runs the letoscript sScript //for sType see abive //if sType is POLL, PollThread is atomatically started //and sPollScript is passed as the script name string LetoScript(string sScript, string sType = "SCRIPT", string sPollScript = ""); //This command adds the script to the cuttent superscript //to run the superscript use StackedLetoScripRun void StackedLetoScript(string sScript); //poll an existing thread //when the thread is finished, script sScript is run void PollThread(string sThreadID, string sScript); //credit to demux //gets a bicpath of a pc //must be servervault to work string GetBicPath(object oPC); //credit to demux //gets the filename of a PCs bic //must be servervault string GetBicFileName(object oPC); //This will automatically add the required code before and after, and will //adapt based on PC/NPC/etc. //This overwites the existing object which will break stored references //such as henchmen. The new object is returned. //the result of the script is stored on the module in LetoResult for 1 second //if nDestroyOriginal is set then PCs will be booted and non-pcs will be destroyed object RunStackedLetoScriptOnObject(object oObject, string sLetoTag = "OBJECT", string sType = "SCRIPT", string sPollScript = "", int nDestroyOriginal = TRUE); //const int DEBUG = TRUE; string GetNWNDir() { string sReturn = GetLocalString(GetModule(), PRC_LETOSCRIPT_NWN_DIR); /* if(GetStringRight(sReturn, 1) != "\" && GetStringRight(sReturn, 1) != "/") sReturn += "\"; //" this is here so textpad doesnt go screwy becasue it escapes the quotes above. */ return sReturn; } //credit to demux string GetBicFileName(object oPC) { string sChar, sBicName; string sPCName = GetStringLowerCase(GetName(oPC)); int i, iNameLength = GetStringLength(sPCName); for(i=0; i < iNameLength; i++) { sChar = GetSubString(sPCName, i, 1); if (TestStringAgainstPattern("(*a|*n|*w|'|-|_)", sChar)) { if (sChar != " ") sBicName += sChar; } } return GetStringLeft(sBicName, 16); } //credit to demux string GetBicPath(object oPC) { // Gets a local var stored on oPC on "event client enter". I do this because // "on even client leave", function GetPCPlayerName() can not be used. Since // a .bic file can not be changed while the owner is logged in, it is typical // to execute leto scripts when the client leaves (on event client leave). string PlayerName = GetLocalString(oPC, "PlayerName"); if(PlayerName == "") PlayerName = GetPCPlayerName(oPC); // Retruns the full path to a .bic file. return GetNWNDir()+"servervault/"+PlayerName+"/"+GetBicFileName(oPC)+".bic"; } void VoidLetoScript(string sScript, string sType = "SCRIPT", string sPollScript = "") { LetoScript(sScript,sType,sPollScript); } string LetoScript(string sScript, string sType = "SCRIPT", string sPollScript = "") { string sAnswer; if (DEBUG) DoDebug(sType+" >: "+sScript); SetLocalString(GetModule(), "NWNX!LETO!"+sType, sScript); sAnswer = GetLocalString(GetModule(), "NWNX!LETO!"+sType); if (DEBUG) DoDebug(sType+" <: "+sAnswer); if(sType == "SPAWN") DelayCommand(1.0, PollThread(sAnswer, sPollScript)); return sAnswer; } void LetoPCEnter(object oPC) { SetLocalString(oPC, "Leto_Path", GetBicPath(oPC)); SetLocalString(oPC, "PCPlayerName", GetPCPlayerName(oPC)); DeleteLocalString(oPC, "LetoScript"); } void LetoPCExit(object oPC) { string sScript = GetLocalString(oPC, "LetoScript"); if(sScript != "") { string sPath = GetLocalString(oPC, "Leto_Path"); if(sPath == "") if (DEBUG) DoDebug("Path is Null"); if(GetPRCSwitch(PRC_LETOSCRIPT_PHEONIX_SYNTAX)) { //pheonix syntax sScript = ">"+sScript; sScript += ">"; sScript += ""; } else { if(GetPRCSwitch(PRC_LETOSCRIPT_GETNEWESTBIC)) { sScript = "%char = FindNewestBic('"+GetNWNDir()+"servervault/"+GetLocalString(oPC, "PCPlayerName")+"'); "+sScript; sScript += "%char = '>'; "; sScript += "close %char; "; } else { //unicorn syntax sScript = "%char= q{"+sPath+"}; "+sScript; sScript += "%char = '>'; "; sScript += "close %char; "; } } string sScriptResult = LetoScript(sScript); SetLocalString(GetModule(), "LetoResult", sScriptResult); AssignCommand(GetModule(), DelayCommand(1.0, DeleteLocalString(GetModule(), "LetoResult"))); } } void StackedLetoScript(string sScript) { if (DEBUG) DoDebug("SLS :"+sScript); SetLocalString(GetModule(), "LetoScript", GetLocalString(GetModule(), "LetoScript")+ sScript); } void PollThread(string sThreadID, string sScript) { if(GetLocalInt(GetModule(), "StopThread"+sThreadID) == TRUE) return; if (DEBUG) DoDebug("Polling: "+sThreadID); //add blank space to capture error messages string sResult = LetoScript(sThreadID+" " +" " +" " +" " +" " +" " +" " +" " +" ", "POLL"); if(sResult == "Error: "+sThreadID+" not done.") { DelayCommand(1.0, PollThread(sThreadID, sScript)); return; } else { if (DEBUG) DoDebug("Poll: Executing: "+sScript); SetLocalInt(GetModule(), "StopThread"+sThreadID, TRUE); DelayCommand(6.0, DeleteLocalInt(GetModule(), "StopThread"+sThreadID)); location lLoc = GetLocalLocation(GetModule(), "Thread"+sThreadID+"_loc"); DelayCommand(1.0, DeleteLocalLocation(GetModule(), "Thread"+sThreadID+"_loc")); if (DEBUG) DoDebug("Thread"+sThreadID+"_loc"); if (DEBUG) DoDebug(GetName(GetAreaFromLocation(lLoc))); object oReturn; if(GetPRCSwitch(PRC_LETOSCRIPT_PHEONIX_SYNTAX)) { oReturn = RetrieveCampaignObject(DB_NAME, DB_GATEWAY_VAR, lLoc); } else { if(GetPRCSwitch(PRC_LETOSCRIPT_UNICORN_SQL)) { string sSQL = "SELECT blob FROM "+DB_NAME+" WHERE "+DB_GATEWAY_VAR+"="+DB_GATEWAY_VAR+" LIMIT 1"; SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL); oReturn = RetrieveCampaignObject("NWNX", "-", lLoc); } else { oReturn = RetrieveCampaignObject(DB_NAME, DB_GATEWAY_VAR, lLoc); } } if (DEBUG) DoDebug(GetName(oReturn)); SetLocalString(GetModule(), "LetoResult", sResult); AssignCommand(GetModule(), DelayCommand(1.0, DeleteLocalString(GetModule(), "LetoResult"))); SetLocalObject(GetModule(), "LetoResultObject", oReturn); AssignCommand(GetModule(), DelayCommand(1.0, DeleteLocalObject(GetModule(), "LetoResultObject"))); SetLocalString(GetModule(), "LetoResultThread", sThreadID); AssignCommand(GetModule(), DelayCommand(1.0, DeleteLocalString(GetModule(), "LetoResultThread"))); ExecuteScript(sScript, OBJECT_SELF); } } void VoidRunStackedLetoScriptOnObject(object oObject, string sLetoTag = "OBJECT", string sType = "SCRIPT", string sPollScript = "", int nDestroyOriginal = TRUE) { RunStackedLetoScriptOnObject(oObject,sLetoTag,sType,sPollScript,nDestroyOriginal); } object RunStackedLetoScriptOnObject(object oObject, string sLetoTag = "OBJECT", string sType = "SCRIPT", string sPollScript = "", int nDestroyOriginal = TRUE) { if(!GetIsObjectValid(oObject)) { WriteTimestampedLogEntry("ERROR: "+GetName(oObject)+"is invalid"); WriteTimestampedLogEntry("Script was "+GetLocalString(GetModule(), "LetoScript")); return OBJECT_INVALID; } string sCommand; object oReturn; location lLoc; object oWPLimbo = GetObjectByTag("HeartOfChaos"); location lLimbo; if(GetIsObjectValid(oWPLimbo)) lLimbo = GetLocation(oWPLimbo); else lLimbo = GetStartingLocation(); string sScript = GetLocalString(GetModule(), "LetoScript"); DeleteLocalString(GetModule(), "LetoScript"); string sScriptResult; //check if its a DM or PC //these use bic files if(GetIsPC(oObject) || GetIsDM(oObject)) { if(!nDestroyOriginal)//dont boot { string sPath = GetLocalString(oObject, "Leto_Path"); if(GetPRCSwitch(PRC_LETOSCRIPT_PHEONIX_SYNTAX)) { sCommand = ">"; sScript = sCommand+sScript; sCommand = ""; sScript = sScript+sCommand; //unicorn } else { if(GetPRCSwitch(PRC_LETOSCRIPT_GETNEWESTBIC)) { sCommand = "%"+sLetoTag+" = FindNewestBic('"+GetNWNDir()+"servervault/"+GetLocalString(oObject, "PCPlayerName")+"'); "; } else { //unicorn syntax sCommand = "%"+sLetoTag+" = q{"+sPath+"}; "; //qq{} doesnt work for me at the moment, wrong slashes } sScript = sCommand+sScript; sCommand = "close %"+sLetoTag+"; "; sScript = sScript+sCommand; } sScriptResult = LetoScript(sScript, sType, sPollScript); } else//boot { //this triggers the OnExit code to fire the letoscript SetLocalString(oObject, "LetoScript", GetLocalString(oObject, "LetoScript")+sScript); if(GetLocalString(GetModule(), PRC_LETOSCRIPT_PORTAL_IP) == "") { BootPC(oObject); } else { ActivatePortal(oObject, GetLocalString(GetModule(), PRC_LETOSCRIPT_PORTAL_IP), GetLocalString(GetModule(), PRC_LETOSCRIPT_PORTAL_PASSWORD), "", //waypoint, may need to change TRUE); } return oReturn; } } //its an NPC/Placeable/Item, go through DB else if(GetObjectType(oObject) == OBJECT_TYPE_CREATURE || GetObjectType(oObject) == OBJECT_TYPE_ITEM || GetObjectType(oObject) == OBJECT_TYPE_PLACEABLE || GetObjectType(oObject) == OBJECT_TYPE_STORE || GetObjectType(oObject) == OBJECT_TYPE_WAYPOINT) { if(GetPRCSwitch(PRC_LETOSCRIPT_PHEONIX_SYNTAX)) { //Put object into DB StoreCampaignObject(DB_NAME, DB_GATEWAY_VAR, oObject); // Reaquire DB with new object in it sCommand += ">"; //Extract object from DB sCommand += ""; sCommand += ""; sCommand += ""; } else { if(GetPRCSwitch(PRC_LETOSCRIPT_UNICORN_SQL)) { //unicorn //Put object into DB string sSQL = "SELECT "+DB_GATEWAY_VAR+" FROM "+DB_NAME+" WHERE "+DB_GATEWAY_VAR+"="+DB_GATEWAY_VAR+" LIMIT 1"; PRC_SQLExecDirect(sSQL); if (PRC_SQLFetch() == PRC_SQL_SUCCESS) { // row exists sSQL = "UPDATE "+DB_NAME+" SET val=%s WHERE "+DB_GATEWAY_VAR+"="+DB_GATEWAY_VAR; SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL); } else { // row doesn't exist // assume table doesnt exist too sSQL = "CREATE TABLE "+DB_NAME+" ( "+DB_GATEWAY_VAR+" TEXT, blob BLOB )"; PRC_SQLExecDirect(sSQL); sSQL = "INSERT INTO "+DB_NAME+" ("+DB_GATEWAY_VAR+", blob) VALUES" + "("+DB_GATEWAY_VAR+", %s)"; SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL); } StoreCampaignObject ("NWNX", "-", oObject); // Reaquire DB with new object in it //force data to be written to disk sSQL = "COMMIT"; PRC_SQLExecDirect(sSQL); sCommand += "sql.connect 'root', '' or die $!; "; sCommand += "sql.query 'SELECT blob FROM "+DB_NAME+" WHERE "+DB_GATEWAY_VAR+"="+DB_GATEWAY_VAR+" LIMIT 1'; "; sCommand += "sql.retrieve %"+sLetoTag+"; "; } else { //Put object into DB StoreCampaignObject(DB_NAME, DB_GATEWAY_VAR, oObject); sCommand += "%"+sLetoTag+"; "; //Extract object from DB sCommand += "extract '"+GetNWNDir()+"database/"+DB_NAME+".fpt', '"+DB_GATEWAY_VAR+"', %"+sLetoTag+" or die $!;"; } } //store their location lLoc = GetLocation(oObject); if(!GetIsObjectValid(GetAreaFromLocation(lLoc))) lLoc = GetStartingLocation(); sScript = sCommand + sScript; sCommand = ""; //destroy the original if(nDestroyOriginal) { AssignCommand(oObject, SetIsDestroyable(TRUE)); DestroyObject(oObject); //its an NPC/Placeable/Item, go through DB if(GetPRCSwitch(PRC_LETOSCRIPT_PHEONIX_SYNTAX)) { sCommand = ">"; sCommand += ""; sCommand += ""; sCommand += ""; sCommand += ""; } else { if(GetPRCSwitch(PRC_LETOSCRIPT_UNICORN_SQL)) { //unicorn sCommand += "sql.query 'SELECT blob FROM "+DB_NAME+" WHERE "+DB_GATEWAY_VAR+"="+DB_GATEWAY_VAR+" LIMIT 1'; "; sCommand += "sql.store %"+sLetoTag+"; "; sCommand += "close %"+sLetoTag+"; "; } else { sCommand += "inject '"+GetNWNDir()+"database/"+DB_NAME+".fpt', '"+DB_GATEWAY_VAR+"', %"+sLetoTag+" or die $!;"; sCommand += "close %"+sLetoTag+"; "; } } } else { if(GetPRCSwitch(PRC_LETOSCRIPT_PHEONIX_SYNTAX)) sCommand += ""; else sCommand += "close %"+sLetoTag+"; "; } sScript = sScript + sCommand; sScriptResult = LetoScript(sScript, sType, sPollScript); if(nDestroyOriginal && sType != "SPAWN") { if(GetPRCSwitch(PRC_LETOSCRIPT_PHEONIX_SYNTAX)) { if(GetObjectType(oObject) == OBJECT_TYPE_CREATURE) { oReturn = RetrieveCampaignObject(DB_NAME, DB_GATEWAY_VAR, lLimbo); AssignCommand(oReturn, JumpToLocation(lLoc)); } else oReturn = RetrieveCampaignObject(DB_NAME, DB_GATEWAY_VAR, lLoc); } else { if(GetPRCSwitch(PRC_LETOSCRIPT_UNICORN_SQL)) { string sSQL = "SELECT blob FROM "+DB_NAME+" WHERE "+DB_GATEWAY_VAR+"="+DB_GATEWAY_VAR+" LIMIT 1"; SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL); if(GetObjectType(oObject) == OBJECT_TYPE_CREATURE) { oReturn = RetrieveCampaignObject("NWNX", "-", lLimbo); AssignCommand(oReturn, JumpToLocation(lLoc)); } else oReturn = RetrieveCampaignObject("NWNX", "-", lLoc); } else { if(GetObjectType(oObject) == OBJECT_TYPE_CREATURE) { oReturn = RetrieveCampaignObject(DB_NAME, DB_GATEWAY_VAR, lLimbo); AssignCommand(oReturn, JumpToLocation(lLoc)); } else oReturn = RetrieveCampaignObject(DB_NAME, DB_GATEWAY_VAR, lLoc); } } } else if(nDestroyOriginal && sType == "SPAWN") { SetLocalLocation(GetModule(), "Thread"+IntToString(StringToInt(sScriptResult))+"_loc", lLoc); if (DEBUG) DoDebug("Thread"+IntToString(StringToInt(sScriptResult))+"_loc"); } } SetLocalString(GetModule(), "LetoResult", sScriptResult); AssignCommand(GetModule(), DelayCommand(1.0, DeleteLocalString(GetModule(), "LetoResult"))); return oReturn; }