Player Resource Consortium

 

Author Topic: Archivist High-level Spells  (Read 17249 times)

0 Members and 1 Guest are viewing this topic.

March 19, 2014, 05:11:21 PM
Reply #15

Perfect. Thank you so much for the help. As I have the day off, I will just finish watching this episode of the Mentalist and then I'll try out the script and post my results!


March 19, 2014, 06:32:45 PM
Reply #16

Genius! That appears to have worked first try! I still can't select bonus spells to learn or learn them from scrolls, but it added EVERY 8th and 9th level spell to my spellbook. And the proper ones too. Haven't tried exporting yet, but I ran the script at level 1 and used the console in my new "Archivist's Sanctum" module to level up to 17 and there they are in my spellbook.

Wait, they show up in my spellbook, and I can stick them in empty slots, and then they appear in my radial menu, but I can't cast them after I rest. Self-target spells just do nothing (but the cursor displays that I am a valid target) and other spells give the message "you have no uses of acid fog remaining." Obviously I am not even trying to cast acid fog.

Damn, that seemed like it was gonna work.


March 19, 2014, 06:37:38 PM
Reply #17

The spells from levels 1-7 still work fine, can be cast, etc.


March 19, 2014, 07:44:35 PM
Reply #18
  • Jr. Associate
  • **
  • Posts: 56
  • Karma: +1/-0
  • New Member
    • View Profile

Acid fog is spell id 0, so it tends to crop up in odd places when something's going wrong.

I'll have a peek through the uses remaining stuff tomorrow and see if I can figure out what's failing. If it's the cache that's broken then it seems as though I'm going to need to pay that a little visit...

(It would perhaps make sense, too. I'd forgotten that it crops up on every spell cast as well as learning)


March 19, 2014, 07:56:15 PM
Reply #19

Thank you for going to all the trouble. I think I might start pulling my hair out soon.


March 20, 2014, 12:47:23 PM
Reply #20
  • Jr. Associate
  • **
  • Posts: 56
  • Karma: +1/-0
  • New Member
    • View Profile

EDIT: Try xwarren's below first. I don't entirely know what I'm doing here, and he does, soooo.... :P

There's also a little mistake in what I've written, but it isn't critical. If it works then I'll fix it, lol. (note to self: nRetVal is getting overwritten from ADDED)

It is unfortunately impossible for me to test anything before posting as I have never had the problems you're describing.

I won't pretend that I fully understand the PRC caching system, nor why the lookups aren't working for you. The fact that the script I gave you worked (which ignores the cache and reads the .2da files straight out the hakpaks) implies that your 2da cache is broken for some reason. From the reading I've just done it suggests that it is saved to the campaign database on a regular basis - the exact frequency (in seconds) is equal to the value of the prc switch PRC_USE_BIOWARE_DATABASE (which defaults to 300) multiplied by six - for a recache every half hour.

In theory then, it should be possible to just change the cached values on the caching golem, then force a rewrite of the database. Whether this will work, or continue working forever is something I am unsure of. If weird things happen you should just be able to delete the PRC_DATA_* files in your NWN/database folder, and that should sort anything unexpected out.

The following is a result of modifying Get2DACache so that it will throw the given columns of any given .2da file into the cache. I can't really test it, so I've added a number of debugging options that might make things a little easier if it doesn't work as expected. If it does work (I'm probably getting ahead of myself here!) and you need to run it on another 2da file, then it would be a lot easier if you were to edit it yourself, which will mean that you'll have to recompile it. I did leave a way for you to operate it without doing this, but setting all the variables correctly from the debug console is going to be rather difficult.

As you already have a PRC module set up, you can do this by...

1) Get a community made compiler. Bioware's one in the toolset dislikes the quantity of constants and other things in the PRC scripts. There's a link to the download at the top of the page: PRC downloads -> PRC script compiler. That is a command line interface, so setting up a batch file to compile stuff with is the advisable option, something like this will do...

Code: [Select]
CD C:\NeverwinterNights\NWN\modules\temp0
C:\NeverwinterNights\NWN\utils\nwnnsscomp.exe -cego *.nss
CD ..
PAUSE

You'll need to change the paths to whatever they are on your system, but that should give you the right idea.

2) Add prc_include to your module's hak list. In the toolset, go edit->module properties->custom content, and add prc_include in anywhere. Don't click OK just yet, as the toolset decides to try and build the module after this - which involves compiling all scripts. As Bioware's compiler can't handle that, unless you stop it within a few seconds the toolset will crash. After clicking OK, the toolset will unpack the hakpaks as it does when you first open the module, and it will then throw up an interface with a cancel button. Sometimes it just isn't possible to be fast enough to hit cancel, if you can't seem to do it then there are other ways to get it in the list and happy, but unfortunately you'll need other software to do this.

3) Now, go tools->script editor, and paste the script in and save it as something. The Bioware compiler will then attempt to compile it and give you a lovely "unknown state in compiler" error, which is fine. Run the batch file (or otherwise compile your script) save your module and you should be good to go.

Anyway, here's the script that may or may not work...

Code: [Select]
#include "prc_alterations"

//These are for Force2DACache
const int FORCE_CACHE_CORRECT = 1;
const int FORCE_CACHE_ADDED = 2;
const int FORCE_CACHE_WRONG = 3;

const int FORCE_CACHE_SKIP_UNCACHED = 1;

//These are for main()
const string FORCE_CACHE_DEBUG_LEVEL = "2DACacherDebugLevel"; //Int: 0 = quiet, any other value = noisy. Default 0
const string FORCE_CACHE_RATE = "2DACacherRate"; //Int: Default and maximum 100. Number of lines processed per second.
const string FORCE_CACHE_MODE = "2DACacherMode"; //Int: Flags passed to the nMode parameter of Force2DACache. Default 0
const string FORCE_CACHE_FILE = "2DACacherFile"; //String: Default cls_spell_archv
const string FORCE_CACHE_COLUMNS = "2DACacherColumns"; //Array name where columns are set.

//Sets the value of the PRC cache so that it may retrieve the given data value correctly.
//Returns one of the following:
//FORCE_CACHE_CORRECT - 2DA cache was already correct
//FORCE_CACHE_ADDED - Value not previously cached
//FORCE_CACHE_WRONG - Value was previously wrong
//nMode can accept the following flags:
//FORCE_CACHE_SKIP_UNCACHED - if set, values not already in the cache will not be inserted. Will still return FORCE_CACHE_ADDED in such cases.
int Force2DACache(string s2DA, string sColumn, int nRow, int nMode=0);
void ForceBiowareDatabaseRecache();

//With that many arguments, making a mistake in code I can't test is too risky
//This cuts down the scope for wrong ordering rather a lot
struct CacheLoopArgs
{
    int nDebug;
    float fDelay;
    int nFlags;
    string sFile;
    int bDeleteColumnArray;
    int nCorrects;
    int nAddeds;
    int nWrongs;
    int nBlanks;
    int nLine;
};


void CacheLoop(struct CacheLoopArgs sArgs);

void main()
{
    struct CacheLoopArgs sArgs;

    int nRate = GetLocalInt(OBJECT_SELF, FORCE_CACHE_RATE);
    sArgs.nDebug = GetLocalInt(OBJECT_SELF, FORCE_CACHE_DEBUG_LEVEL);
    if (nRate == 0) { sArgs.fDelay = 0.01f; }
    else
    {
        sArgs.fDelay = 1.0f/IntToFloat(nRate);
        if (sArgs.fDelay < 0.01f) { sArgs.fDelay = 0.01f; }
    }
    sArgs.nFlags = GetLocalInt(OBJECT_SELF, FORCE_CACHE_MODE);

    //This sets the columns/file to cache
    sArgs.sFile = GetLocalString(OBJECT_SELF, FORCE_CACHE_FILE);
    if (sArgs.sFile == "") { sArgs.sFile = "cls_spell_archv"; }

    if (!array_exists(OBJECT_SELF, FORCE_CACHE_COLUMNS))
    {
        array_create(OBJECT_SELF, FORCE_CACHE_COLUMNS);
        array_set_string(OBJECT_SELF, FORCE_CACHE_COLUMNS, 0, "Level");
        array_set_string(OBJECT_SELF, FORCE_CACHE_COLUMNS, 1, "FeatID");
        array_set_string(OBJECT_SELF, FORCE_CACHE_COLUMNS, 2, "IPFeatID");
        array_set_string(OBJECT_SELF, FORCE_CACHE_COLUMNS, 3, "SpellID");
        array_set_string(OBJECT_SELF, FORCE_CACHE_COLUMNS, 4, "RealSpellID");
        array_set_string(OBJECT_SELF, FORCE_CACHE_COLUMNS, 5, "ReqFeat");
        array_set_string(OBJECT_SELF, FORCE_CACHE_COLUMNS, 6, "AL");
        sArgs.bDeleteColumnArray = TRUE; //Mark that array for deletion at the end
    }

    CacheLoop(sArgs);
}

void CacheLoop(struct CacheLoopArgs sArgs)
{
    if (sArgs.nBlanks > 20)
    {
        //Probably EOF, this writes the output
        string sMessage = sArgs.sFile + ": Finished caching ";
        int i;
        for (i=0; i<array_get_size(OBJECT_SELF, FORCE_CACHE_COLUMNS); i++)
        {
            sMessage += array_get_string(OBJECT_SELF, FORCE_CACHE_COLUMNS, i) + ", ";
        }
        SendMessageToPC(OBJECT_SELF, IntToString(sArgs.nAddeds) + " values added to cache.");
        SendMessageToPC(OBJECT_SELF, IntToString(sArgs.nWrongs) + " values corrected in cache.");
        SendMessageToPC(OBJECT_SELF, IntToString(sArgs.nCorrects) + " values were already correct in cache.");
        if (sArgs.bDeleteColumnArray)
        {
            array_delete(OBJECT_SELF, FORCE_CACHE_COLUMNS);
        }

        //Aaand force the database to recache
        ForceBiowareDatabaseRecache();

        SendMessageToPC(OBJECT_SELF, "Database should now be recached.");

        return;
    }
    int i;
    int bLineIsBlank = TRUE;

    if (sArgs.nDebug)
    {
        SendMessageToPC(OBJECT_SELF, "Line " + IntToString(sArgs.nLine) + ":");
    }

    for (i=0; i<array_get_size(OBJECT_SELF, FORCE_CACHE_COLUMNS); i++)
    {
        string sColumn = array_get_string(OBJECT_SELF, FORCE_CACHE_COLUMNS, i);
        //First, test for blank lines
        if (bLineIsBlank)
        {
            string sReal = Get2DAString(sArgs.sFile, sColumn, sArgs.nLine);
            if (sReal != "")
            {
                bLineIsBlank=FALSE;
                sArgs.nBlanks = 0; //Set blank line counter to 0, if not already
            }
        }

        //Do the actual caching and increment the variables
        int nRet = Force2DACache(sArgs.sFile, sColumn, sArgs.nLine, sArgs.nFlags);
        switch (nRet)
        {
            case FORCE_CACHE_CORRECT: sArgs.nCorrects++; break;
            case FORCE_CACHE_ADDED: sArgs.nAddeds++; break;
            case FORCE_CACHE_WRONG: sArgs.nWrongs++; break;
        }

        //If heavy debugging enabled, print it all out
        if (sArgs.nDebug)
        {
            switch (nRet)
            {
                case FORCE_CACHE_CORRECT: SendMessageToPC(OBJECT_SELF, PRC_TEXT_LIGHT_BLUE + sColumn + " was correct."); break;
                case FORCE_CACHE_ADDED: SendMessageToPC(OBJECT_SELF, PRC_TEXT_ORANGE + sColumn + " was added."); break;
                case FORCE_CACHE_WRONG: SendMessageToPC(OBJECT_SELF, PRC_TEXT_RED + sColumn + " was wrong!"); break;
            }
        }
    }
    //Increment line, and keep going
    sArgs.nLine++;
    DelayCommand(sArgs.fDelay, CacheLoop(sArgs));
}

//Easy one first.
void ForceBiowareDatabaseRecache()
{
    SetLocalInt(GetModule(), "Bioware2dacacheCount", GetPRCSwitch(PRC_USE_BIOWARE_DATABASE));
    //Forcing it can't hurt - assuming the HB will fire is incorrect if it's not set as the module heartbeat for some weird reason
    ExecuteScript("prc_onheartbeat", GetModule());
}


int Force2DACache(string s2DA, string sColumn, int nRow, int nMode=0)
{
    //lower case the 2da and column
    s2DA = GetStringLowerCase(s2DA);
    sColumn = GetStringLowerCase(sColumn);

    //get the chest that contains the cache
    object oCacheWP = GetObjectByTag("Bioware2DACache");
    //if no chest, use HEARTOFCHAOS in limbo as a location to make a new one
    if (!GetIsObjectValid(oCacheWP))
    {
        if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Cache container creature does not exist, creating new one");
        //oCacheWP = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_chest2",
        //    GetLocation(GetObjectByTag("HEARTOFCHAOS")), FALSE, "Bioware2DACache");
        //has to be a creature, placeables cant go through the DB
        oCacheWP = CreateObject(OBJECT_TYPE_CREATURE, "prc_2da_cache",
                                GetLocation(GetObjectByTag("HEARTOFCHAOS")), FALSE, "Bioware2DACache");
    }

    //get the token for this file
    string sFileWPName = s2DA + "_" + sColumn + "_" + IntToString(nRow / 1000);
    //if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Token tag is " + sFileWPName);
    object oFileWP = GetObjectByTag(sFileWPName);
    //token doesnt exist make it

    //EVERYTHING above this point is taken directly from Get2DACache

    //If the FORCE_CACHE_SKIP_UNCACHED flag is set, don't do any more as we don't want to
    if (nMode & FORCE_CACHE_SKIP_UNCACHED)
    {
        return FORCE_CACHE_ADDED;
    }

    int nRetVal;

    if(!GetIsObjectValid(oFileWP))
    {
        if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Token does not exist, creating new one");

        // Use containers to avoid running out of inventory space
        int nContainer = 0;
        string sContainerName = "Bio2DACacheTokenContainer_" + GetSubString(s2DA, 0, 1) + "_";
        object oContainer     = GetObjectByTag(sContainerName + IntToString(nContainer));

        // Find last existing container
        if(GetIsObjectValid(oContainer))
        {
            if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Seeking last container in series: " + sContainerName);
            // find the last container
            nContainer = GetLocalInt(oContainer, "ContainerCount");
            oContainer = GetObjectByTag(sContainerName + IntToString(nContainer));

            if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Found: " + DebugObject2Str(oContainer));

            // Make sure it's not full
            if(GetLocalInt(oContainer, "NumTokensInside") >= 34) // Container has 35 slots. Attempt to not use them all, just in case
            {
                if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Container full: " + DebugObject2Str(oContainer));
                oContainer = OBJECT_INVALID;
                ++nContainer; // new container is 1 higher than last one
            }
        }
        // We need to create a container
        if(!GetIsObjectValid(oContainer))
        {
            if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Creating new container");

            oContainer = CreateObject(OBJECT_TYPE_ITEM, "nw_it_contain001", GetLocation(oCacheWP), FALSE, sContainerName + IntToString(nContainer));
            DestroyObject(oContainer);
            oContainer = CopyObject(oContainer, GetLocation(oCacheWP), oCacheWP, sContainerName + IntToString(nContainer));
            // store the new number of containers in this series
            if (nContainer)
                SetLocalInt( GetObjectByTag(sContainerName + "0"), "ContainerCount", nContainer);
            // else this is the first container - do nothing as this is the same as storing 0 on it.
            // Also here we still have 2 objects with the same tag so above code may get
            // the object destroyed at the end of the function if this is the first container.
        }

        if(DEBUG_GET2DACACHE) DoDebug("Get2DACache: Using container: " + DebugObject2Str(oContainer));

        // Create the new token
        /*oFileWP = CreateObject(OBJECT_TYPE_ITEM, "hidetoken", GetLocation(oCacheWP), FALSE, sFileWPName);
        DestroyObject(oFileWP);
        oFileWP = CopyObject(oFileWP, GetLocation(oCacheWP), oCacheWP, sFileWPName);*/
        oFileWP = CreateItemOnObject("hidetoken", oContainer, 1, sFileWPName);

        //SetName(oFileWP, "2da Cache - " + sFileWPName);

        // Increment token count tracking variable
        SetLocalInt(oContainer, "NumTokensInside", GetLocalInt(oContainer, "NumTokensInside") + 1);

        //Set return value to indicate that it was not already there
        nRetVal = FORCE_CACHE_ADDED;
    }

    string sReal = Get2DAString(s2DA, sColumn, nRow);
    //Convert to **** here, as that is what gets saved
    if (sReal == "") { sReal = "****"; }

    string sCurrent = GetLocalString(oFileWP, s2DA+"|"+sColumn+"|"+IntToString(nRow));

    if (sReal == sCurrent)
    {
        return FORCE_CACHE_CORRECT;
    }
    else
    {
        SetLocalString(oFileWP, s2DA+"|"+sColumn+"|"+IntToString(nRow), sReal);
        return FORCE_CACHE_WRONG;
    }
}

You can change a few things about how this runs using the debug console. The commands "SetVarInt" and "SetVarString" allow you to set variables on objects. You can use them like this (They are case sensitive):

SetVarInt 2DACacherDebugLevel 1

The game will then prompt you to target that at something - you need to set it on your character. The variables supported by the script can be found towards the top of the source above. You CAN indeed change the file and the fields to cache using this, however as the fields are set in an array, the way you set them in-game is a little different. If you use "dm_dumplocals" on your character while the script is running, you should be able to find the array produced by the script - I haven't really paid much attention to how they are stored, but it should look something like this...

2DACacherColumns - 7 [INT]
2DACacherColumns_0 - "Level" [STR]
2DACacherColumns_1 - "FeatID" [STR]
2DACacherColumns_2 - "IPFeatID" [STR]
2DACacherColumns_3 - "SpellID" [STR]
2DACacherColumns_4 - "RealSpellID" [STR]
2DACacherColumns_5 - "ReqFeat" [STR]
2DACacherColumns_6 - "AL" [STR]

The first one (it may be a string, I honestly don't remember) signifies the length of the array. You'll need to change that to a more appropriate value if you want to run the script this way, or, as I said above, you could just recompile it all yourself. The values of an array are always stored as strings, regardless of whether they are numerical or not.

Hopefully this works, if it doesn't then I'm a little out of ideas I'm afraid.
« Last Edit: March 20, 2014, 03:53:52 PM by Loggy »


March 20, 2014, 01:45:13 PM
Reply #21
  • Hero Member
  • *****
  • Posts: 1439
  • Karma: +27/-0
  • Gender: Male
    • View Profile

I really thought I have fixed this issue. Could you test my script file, please?

To check if it works please do the following:
1. make sure NWN/NWServer is not running
2. go to NWN/database directory and delate all prc*_data.* files
3. download the attached zip file and extract it somewhere
4. use nwhak.exe from NWN/utils directory and replace prc_onmodload.ncs in prc_scripts.hak (or put it in prc_2das.hak)
5. run the game and load any PRC module (new game - not a save!) - when the character selection screen loads, let the game run for a while and do not touch anything (2 - 3 minutes should be more than enough)
6 optional - exit the game and check if new prc_data files were created in NWN/database directory
7. load the game and check if your archivist can learn high level spells.

« Last Edit: March 20, 2014, 04:21:32 PM by xwarren »


March 20, 2014, 07:29:14 PM
Reply #22

Xwarren, reading on here, it seems that you are the big cheese when it comes to the PRC, correct? If so, thank you for your attention and thanks for the hard work. (Actually, thanks for the first thing no matter who you are).

I just got home and will try your fix presently. One thing, when I deleted those database files and started a new game before, I did not wait on the character selection screen (I'm supposing that means the "select premade character" menu), I just picked one real quick and started it. Perhaps that was my problem. Well, here goes nothing. Fingers crossed and all that.


March 20, 2014, 07:46:16 PM
Reply #23

@Loggy, man that was a lot of work on your part. Thank you very much. I almost hope Xwarren's solution doesn't work (not really) so I can at least give yours the attention it deserves! I'll find out shortly here and report back.

An aside:

A caching golem? Is that seriously what it's called?!
How many hit dice does that have?
Is it immune to magic?
Is it one of those rare golems with an intelligence score?

;) Sorry! That was a totally lame joke, but far too ironic to pass up considering the game we are both playing. I am picturing some fusion of d&d and tron - a mindless construct endlessly shuttling script around.


March 20, 2014, 08:44:03 PM
Reply #24

Xwarren, that made it significantly worse... the problem is still the same, but now I can only learn first level spells, and of all the spells usually available to a first level archivist as bonus spells, only like seven were selectable.

Just to clarify what I did for completeness-sake, I followed your directions to the letter and the part about the nwhak.exe, I opened that program, chose file > open, opened the prc_scripts.hak, dragged the extracted file you posted into the list, it asked to replace (or overwrite, can't remember which), I clicked yes, and then I saved and exited.

I should note that the file I put in there (the one you posted) was 50% smaller than the one that was placed there by prc35.exe. ~80kb (extracted ncs from your post) vs. ~120kb (original ncs).

Included those last two comments to give as detailed an explanation of my actions as possible. I imagine this is the process through which bugs are hammered out.

Going to replace prc_scripts.hak with the original from the PRC35 archive (the manual install) to get back to where I was, erase and recreate database files by starting a new game again, then try Loggy's solution.


March 20, 2014, 09:56:26 PM
Reply #25

Xwarren, sorry for the multiple posts, something just struck me:

Since changing that ncs file had the opposite effect, that must be the issue. That is, whatever you changed addressed PRECISELY the problem at hand, it just perhaps didn't address it in the correct manner/change the correct parameters.

Also, from reading the past posts examining this issue it seems the archivist class did not have this problem until the final version of PRC 3.5. At least confirmed through the second beta. So would it be possible to replace the onmodload.ncs in prc_scripts.hak with one from the beta or an earlier version, or would that interfere with the regular caching of the database files? Or, would it be possible for me to simply install an older version of the PRC - somewhere between 3.3H and 3.5 beta 2 - to a unmodded nwn to play my archivist? If I lose a few spells because they don't exist in the earlier versions I probably won't mind. Really a mostly complete archivist spell list with access to the runecaster and master of shrouds/fist of raziel (evil/good) prestige classes would make me a happy camper. Because I deleted my GOG version and decided to delete my saves as well I'm starting over from level 1 no matter what when this problem gets fixed. Unfortunately PRC 3.3G is the only other version I can find online and it doesn't have the archivist.

My suggestions are coming from an "out on a limb" position, however, because I really don't understand how it all works mechanically. Just some application of logic which may be misguided/uninformed.

I should say also that simply settling for an earlier version is just acceptable for me personally, but I am at your disposal to help you sort this all out if you want to get to the bottom of what is happening here.
« Last Edit: March 20, 2014, 10:02:56 PM by r00tsnatty »


March 20, 2014, 11:19:15 PM
Reply #26

Problem solved!

I replaced the PRC_scripts.hak with the one from the 3.5 manual install .rar archive.
Deleted the PRC_data.* files from the database folder.
Started a new game.
Bingo.

My only conclusion is that perhaps the prc_onmodload.ncs from the 3.5 exe installer is not correct while the one from the 3.5 manual install archive is correct. I applied the exe install to two fresh installs (different versions) of the game and the archivist did not work. The only thing I changed was replacing the prc_scripts.hak with the one from the archive. Now it works. I suppose it could be one of the other scripts within that file, but the problem is certainly (well maybe not certainly...) somewhere in prc_scripts.hak.

Thanks for everyone's help with this one! I can't believe there is this much support for a mod from such an old game. You guys are great!


March 21, 2014, 12:22:44 AM
Reply #27
  • Hero Member
  • *****
  • Posts: 1439
  • Karma: +27/-0
  • Gender: Male
    • View Profile

The file I sent you is a latest version - hopefully will be included in next PRC release. I have optimized some functions, and that's why it is smaller than original ncs.

I have compared both packs (exe and rar) in a diffing program - the files are exactly the same, just distributed differently. I am glad it works for you now, but I have no clue why my new script didn't fixed the problem (I am using it currently and my archivist can learn spells at all levels) or why replacing prc_scripts.hak did. Anyway - let me know if this happens again. Thanks



March 22, 2014, 01:15:29 PM
Reply #28

Hmmmm... Very strange. The only thing, other than switching that file, which I did differently was wait maybe three minutes on the character select screen. Maybe that was the key. But I did that with the onmodload.ncs file you posted as well.

Another thing I noticed that might be of interest is that before it was working properly all cloaks had no icons. They just showed up as white rectangles in the inventory. I didn't really care because all their stats were there and working properly, they could be bought and sold, and could be equipped. But now that the archivist is working, the cloaks have icons as well.

Two birds, one stone I suppose.

Thanks for all your hard work on this one. I am having a lot of fun with my archivist.