Squatting Monk's instructions should work, so long as you're using the NWNX loader to fire up the module as a server. You can still play by yourself that way, but nwserver MUST be running in order for NWNX to interface.
As for #2 and #3, we use nwnx_leto for most of it, though we do use a more 'modern' way of finding the bic file of the character, which can be found in nwnx_funcs, courtesy of acaos:
string GetPCFileName (object oPC) {
if (!GetIsPC(oPC))
return "";
SetLocalString(oPC, "NWNX!FUNCS!GETPCFILENAME", " ");
return GetLocalString(oPC, "NWNX!FUNCS!GETPCFILENAME");
}
We use that to set the bic file path on characters as they log in, loading from the database, or finding it freshly if necessary. Here's the relevant chunk of code, which executes from onenter of the first area of the mod which they're guaranteed to be in for more than a few seconds:
void OnSubraceEnter (object oPC) {
string sPath = GetLocalString(oPC, "BicFilePath");
if (sPath == "") {
sPath = GetBicFilePath(oPC);
if (sPath != "") {
SetLocalString(oPC, "BicFilePath", sPath);
if (!GetCommandable(oPC))
SetCommandable(TRUE, oPC);
} else {
ExportSingleCharacter(oPC);
FloatingTextStringOnCreature("File System Error! Attempting to fix, please wait. " +
"If you get this message more than 3 times in a row, please contact a DM.",
oPC, FALSE);
if (GetCommandable(oPC))
SetCommandable(FALSE, oPC);
DelayCommand(5.0f,
FloatingTextStringOnCreature("Accents or other unusual symbols in character names " +
"prevent the server from locating your character for subrace and other edits.",
oPC, FALSE));
/* if the bic hasn't updated yet, try again at an extended delay */
DelayCommand(10.0f, OnSubraceEnter(oPC));
return;
}
}
}
It relies on this include, which also has the renaming code, should you want to use this method instead of SMs (I don't particularly recommend it, but it works):
const string NWN_DEV_SERVER = "100";
const string NWN_VAULT_LIVE = "/home/funkyswerve/nwn/servervault/";
const string NWN_VAULT_DEV = "/home/funkyswerve/nwn-dev/servervault/";
const string NWN_SOURCE_DIR = "/home/funkyswerve/nwn/source/";
string LetoScript (string sScript) {
SetLocalString(GetModule(), "NWNX!LETO!SCRIPT", sScript);
return GetLocalString(GetModule(), "NWNX!LETO!SCRIPT");
}
string GetBicFilePath (object oPC) {
string sVaultPath = NWN_VAULT_LIVE;
if (GetLocalString(GetModule(), "ServerNumber") == NWN_DEV_SERVER)
sVaultPath = NWN_VAULT_DEV;
return sVaultPath + GetPCPlayerName(oPC) + "/" + GetPCFileName(oPC) + ".bic";
}
string VerifyBicFileName (object oPC) {
string sChar, sBicFile;
string sName = GetStringLowerCase(GetName(oPC));
int i, nLen = GetStringLength(sName);
for (i = 0; i < nLen; i++) {
sChar = GetSubString(sName, i, 1);
if (TestStringAgainstPattern("(*a|*n|*w|'|-|_)", sChar)) {
if (sChar != " ")
sBicFile += sChar;
}
}
return GetStringLeft(sBicFile, 15);
}
void RepeatFloatingTextStringOnPC (object oPC, string sString, float fDelay=3.0, int nTimes=-1) {
if (!GetIsObjectValid(oPC) || GetPCPlayerName(oPC) == "")
return;
if (--nTimes != 0)
DelayCommand(fDelay, RepeatFloatingTextStringOnPC(oPC, sString, fDelay, nTimes));
FloatingTextStringOnCreature(sString, oPC, FALSE);
}
string AddFeatAtCurrentLevel (object oPlayer, int nFeat = -1) {
if (nFeat < 0)
return "";
return "add /FeatList/Feat, type => gffWord, value => " + IntToString(nFeat) + ";" + "add /LvlStatList/[" +
IntToString(GetHitDice(oPlayer) - 1) + "]/FeatList/Feat, type => gffWord, value => " + IntToString(nFeat) + ";";
}
void ApplyLetoScriptToPC (string sScript, object oPC) {
string sBicFile = GetLocalString(oPC, "BicFilePath");
sScript =
"$RealFile = q<" + sBicFile + ">;" +
"$EditFile = $RealFile + '.utc';" +
"FileRename $RealFile, $EditFile;" +
"%bic = $EditFile or die;" +
sScript +
"%bic = '>';" +
"close %bic;" +
"FileRename $EditFile, $RealFile;";
SetLocalString(oPC, "LetoScript", sScript);
WriteTimestampedLogEntry("LETO : " + GetPCPlayerName(oPC) + " : " + GetName(oPC) + " : " + sScript);
/* Ask player to relog rather than booting them since they could crash
* server if they log out after boot delaycommanded but before executed */
RepeatFloatingTextStringOnPC(oPC, "Please relog for character edits.");
}
void DeleteFile (string sFile) {
WriteTimestampedLogEntry("DELETE : " + sFile);
LetoScript("FileDelete q<" + sFile + ">");
}
string SetPCName (object oPC, string sBicPath, string sFirstName, string sLastName) {
string sNewBicName, sChar, sName = GetStringLowerCase(sFirstName + sLastName);
int i, nLen = GetStringLength(sName);
for (i = 0; i < nLen; i++) {
sChar = GetSubString(sName, i, 1);
if (TestStringAgainstPattern("(*a|*n|*w|'|-|_)", sChar)) {
if (sChar != " ")
sNewBicName += sChar;
}
}
/* actually rename the PC */
string sNewPath = GetPCPlayerName(oPC) + "/" + GetStringLeft(sNewBicName, 16) + ".bic";
if (GetLocalString(GetModule(), "ServerNumber") == NWN_DEV_SERVER)
sNewPath = NWN_VAULT_DEV + sNewPath;
else
sNewPath = NWN_VAULT_LIVE + sNewPath;
string sScript =
"$RealFile = q<" + sBicPath + ">;" +
"$EditFile = $RealFile + '.utc';" +
"FileRename $RealFile, $EditFile;" +
"%bic = $EditFile or die;" +
"/FirstName = q~" + sFirstName + "~;" +
"/LastName = q~" + sLastName + "~;" +
"%bic = '>';" +
"close %bic;" +
"$FinalFile = q<" + sNewPath + ">;" +
"FileRename $EditFile, $FinalFile;";
SetLocalString(oPC, "LetoScript", sScript);
WriteTimestampedLogEntry("LETO : " + GetPCPlayerName(oPC) + " : " + GetName(oPC) + " : " + sScript);
RepeatFloatingTextStringOnPC(oPC, "Please relog for name change.");
return sNewPath;
}
string GetFirstName (string sBicPath) {
string sLeto = "%bic = q<" + sBicPath + ">; print /FirstName; close %bic;";
string sName = LetoScript(sLeto);
return sName;
}
string GetLastName (string sBicPath) {
string sLeto = "%bic = q<" + sBicPath + ">; print /LastName; close %bic;";
string sName = LetoScript(sLeto);
return sName;
}
Obviously you'll want to swap out the vaultpath string there with your own.
LMK if you have questions.
Funky