Skip to content

Latest commit

 

History

History
447 lines (373 loc) · 20.2 KB

libraries.md

File metadata and controls

447 lines (373 loc) · 20.2 KB

Core Utilities: Libraries

Libraries allow the builder to encapsulate many scripts into one, dramatically reducing the script count in the module. In a library, each script is a function bound to a unique name and/or number. When the library is called, the name is routed to the proper function.

Since each script defined by a library has a unique name to identify it, the builder can execute a library script without having to know the file it is located in. This makes it easy to create script systems to override behavior of another system; you don't have to edit the other system's code, you just implement your own function to override it.

util_i_libraries.nss holds functions for interacting with libraries. This script requires util_i_debug.nss, util_i_csvlists.nss, util_i_sqlite.nss, and util_i_nss.nss.

Contents

Anatomy of a Library

This is an example of a simple library:

#include "util_i_libraries"

void MyFunction()
{
    // ...
}

void MyOtherFunction()
{
    // ...
}

void OnLibraryLoad()
{
    RegisterLibraryScript("MyFunction");
    RegisterLibraryScript("MyOtherFunction");
}

This script contains custom functions (MyFunction() and MyOtherFunction()) as well as an OnLibraryLoad() function. OnLibraryLoad() is executed whenever the library is loaded by LoadLibrary(); it calls RegisterLibraryScript() to expose the names of the custom functions as library scripts. When a library script is called with RunLibraryScript(), the custom functions are called.

If you want to do something more complicated that can't be handled by a single function call, you can pass a unique number to RegisterLibraryScript() as its second parameter, which will cause RunLibraryScript() to call a special customizable dispatching function called OnLibraryScript(). This function takes the name and number of the desired function and executes the desired code. For example:

#include "util_i_libraries"

void OnLibraryLoad()
{
    RegisterLibraryScript("Give50GP",  1);
    RegisterLibraryScript("Give100GP", 2);
}

void OnLibraryScript(string sScript, int nEntry)
{
    switch (nEntry)
    {
        case 1: GiveGoldToCreature(OBJECT_SELF, 50); break;
        case 2: GiveGoldToCreature(OBJECT_SELF, 100); break;
    }
}

Note: A library does not need to have a main() function, because this will be automatically generated by the LoadLibrary() and RunLibraryScript() functions.

Using a Library

util_i_libraries.nss is needed to load or run library scripts.

To use a library, you must first load it. This will activate the library's OnLibraryLoad() function and register each desired function.

// Loads a single library
LoadLibrary("my_l_library");

// Loads a CSV list of library scripts
LoadLibraries("pw_l_plugin, dlg_l_example, prr_l_main");

// Loads all libraries matching a glob pattern
LoadLibrariesByPattern("*_l_*");

// Loads all libraries matching a prefix
LoadLibrariesByPrefix("pw_l_");

If a library implements a script that has already been implemented in another library, a warning will be issued and the newer script will take precedence.

Calling a library script is done using RunLibraryScript(). The name supplied should be the name bound to the function in the library's OnLibraryLoad(). If the name supplied is implemented by a library, the library will be JIT compiled and the desired function will be called with ExecuteScriptChunk(). Otherwise, the name will be assumed to match a normal script, which will be executed with ExecuteScript().

// Executes a single library script on OBJECT_SELF
RunLibraryScript("MyFunction");

// Executes a CSV list of library scripts, for which oPC will be OBJECT_SELF
object oPC = GetFirstPC();
RunLibraryScripts("MyFunction, MyOtherFunction", oPC);

Pre-Compiled Libraries

By default, libraries are run using ExecuteScriptChunk(), which JIT compiles the script and runs it each time the library script is called. If you wish to have your script pre-compiled, you can include the script util_i_library.nss in your file in place of util_i_libraries.nss. This script contains a main() function that will call either your OnLibraryLoad() or OnLibraryScript() function as appropriate; thus, if you use this method, you must provide an OnLibraryScript() dispatch function.

Note: util_i_library.nss uses the nwnsc default_function pragma to prevent compilation errors and will not compile with the toolset compiler. If this is not desired, you can either comment those lines out or implement the main() function yourself.

Advanced Usage

For longer libraries, string comparison in a large if/else tree may be tedious and slow. Using nEntry to identify the script to run can help, and it enables more complicated routing. For example, in this long library, we use a switch/case statement to limit the number of comparisons we have to make. The hex formatting and bitwise operations make it a little easier to visually distinguish related scripts, but it's not really necessary:

void OnLibraryLoad()
{
    // Event functions
    RegisterLibraryScript("prr_OnComponentActivate",                 0x0100+0x01);
    RegisterLibraryScript("prr_OnClientEnter",                       0x0100+0x02);

    // Dialog utility functions
    RegisterLibraryScript("prr_SetDatabaseInt",                      0x0200+0x01);
    RegisterLibraryScript("prr_GetDatabaseInt",                      0x0200+0x02);

    RegisterLibraryScript("prr_SetHasMetNPC",                        0x0200+0x03);
    RegisterLibraryScript("prr_GetHasMetNPC",                        0x0200+0x04);

    RegisterLibraryScript("prr_GetReactionHate",                     0x0200+0x05);
    RegisterLibraryScript("prr_GetReactionNeutral",                  0x0200+0x06);
    RegisterLibraryScript("prr_GetReactionLike",                     0x0200+0x07);

    RegisterLibraryScript("prr_SkillCheckHigh",                      0x0200+0x08);
    RegisterLibraryScript("prr_SkillCheckMid",                       0x0200+0x09);
    RegisterLibraryScript("prr_SkillCheckLow",                       0x0200+0x0A);
    RegisterLibraryScript("prr_SkillCheckCustom",                    0x0200+0x0B);

    // Dialog reputation functions
    RegisterLibraryScript("prr_ReputationIncreaseHigh",              0x0300+0x01);
    RegisterLibraryScript("prr_ReputationIncreaseMid",               0x0300+0x02);
    RegisterLibraryScript("prr_ReputationIncreaseLow",               0x0300+0x03);
    RegisterLibraryScript("prr_ReputationDecreaseHigh",              0x0300+0x04);
    RegisterLibraryScript("prr_ReputationDecreaseMid",               0x0300+0x05);
    RegisterLibraryScript("prr_ReputationDecreaseLow",               0x0300+0x06);
    RegisterLibraryScript("prr_ReputationChangeCustom",              0x0300+0x07);

    RegisterLibraryScript("prr_ReputationIncreaseHigh_Party",        0x0300+0x08);
    RegisterLibraryScript("prr_ReputationIncreaseMid_Party",         0x0300+0x09);
    RegisterLibraryScript("prr_ReputationIncreaseLow_Party",         0x0300+0x0A);
    RegisterLibraryScript("prr_ReputationDecreaseHigh_Party",        0x0300+0x0B);
    RegisterLibraryScript("prr_ReputationDecreaseMid_Party",         0x0300+0x0C);
    RegisterLibraryScript("prr_ReputationDecreaseLow_Party",         0x0300+0x0D);
    RegisterLibraryScript("prr_ReputationChangeCustom_Party",        0x0300+0x0E);

    // Dialog faction reputation functions
    RegisterLibraryScript("prr_FactionReputationIncreaseHigh",       0x0400+0x01);
    RegisterLibraryScript("prr_FactionReputationIncreaseMid",        0x0400+0x02);
    RegisterLibraryScript("prr_FactionReputationIncreaseLow",        0x0400+0x03);
    RegisterLibraryScript("prr_FactionReputationDecreaseHigh",       0x0400+0x04);
    RegisterLibraryScript("prr_FactionReputationDecreaseMid",        0x0400+0x05);
    RegisterLibraryScript("prr_FactionReputationDecreaseLow",        0x0400+0x06);
    RegisterLibraryScript("prr_FactionReputationChangeCustom",       0x0400+0x07);

    RegisterLibraryScript("prr_FactionReputationIncreaseHigh_Party", 0x0400+0x08);
    RegisterLibraryScript("prr_FactionReputationIncreaseMid_Party",  0x0400+0x09);
    RegisterLibraryScript("prr_FactionReputationIncreaseLow_Party",  0x0400+0x0A);
    RegisterLibraryScript("prr_FactionReputationDecreaseHigh_Party", 0x0400+0x0B);
    RegisterLibraryScript("prr_FactionReputationDecreaseMid_Party",  0x0400+0x0C);
    RegisterLibraryScript("prr_FactionReputationDecreaseLow_Party",  0x0400+0x0D);
    RegisterLibraryScript("prr_FactionReputationChangeCustom_Party", 0x0400+0x0E);

    // General Utility functions
    RegisterLibraryScript("prr_CaptureChatText",                     0x0500+0x01);
}

void OnLibraryScript(string sScript, int nEntry)
{
    switch (nEntry & 0xff00)
    {
        case 0x0100:
            switch (nEntry & 0x00ff)
            {
                case 0x01: prr_OnComponentActivate();                  break;
                case 0x02: prr_OnClientEnter();                        break;
            }  break;

        case 0x0200:
            switch (nEntry & 0x00ff)
            {
                 case 0x01: prr_SetDatabaseInt();                      break;
                 case 0x02: prr_GetDatabaseInt();                      break;

                 case 0x03: prr_SetHasMetNPC();                        break;
                 case 0x04: prr_GetHasMetNPC();                        break;

                 case 0x05: prr_GetReactionHate();                     break;
                 case 0x06: prr_GetReactionNeutral();                  break;
                 case 0x07: prr_GetReactionLike();                     break;

                 case 0x08: prr_SkillCheckHigh();                      break;
                 case 0x09: prr_SkillCheckMid();                       break;
                 case 0x0A: prr_SkillCheckLow();                       break;
                 case 0x0B: prr_SkillCheckCustom();                    break;
             }   break;

        case 0x0300:
            switch (nEntry & 0x00ff)
            {
                case 0x01: prr_ReputationIncreaseHigh();               break;
                case 0x02: prr_ReputationIncreaseMid();                break;
                case 0x03: prr_ReputationIncreaseLow();                break;
                case 0x04: prr_ReputationDecreaseHigh();               break;
                case 0x05: prr_ReputationDecreaseMid();                break;
                case 0x06: prr_ReputationDecreaseLow();                break;
                case 0x07: prr_ReputationChangeCustom();               break;

                case 0x08: prr_ReputationIncreaseHigh_Party();         break;
                case 0x09: prr_ReputationIncreaseMid_Party();          break;
                case 0x0A: prr_ReputationIncreaseLow_Party();          break;
                case 0x0B: prr_ReputationDecreaseHigh_Party();         break;
                case 0x0C: prr_ReputationDecreaseMid_Party();          break;
                case 0x0D: prr_ReputationDecreaseLow_Party();          break;
                case 0x0E: prr_ReputationChangeCustom_Party();         break;
            }   break;

        case 0x0400:
            switch (nEntry & 0x00ff)
            {
                case 0x01: prr_FactionReputationIncreaseHigh();        break;
                case 0x02: prr_FactionReputationIncreaseMid();         break;
                case 0x03: prr_FactionReputationIncreaseLow();         break;
                case 0x04: prr_FactionReputationDecreaseHigh();        break;
                case 0x05: prr_FactionReputationDecreaseMid();         break;
                case 0x06: prr_FactionReputationDecreaseLow();         break;
                case 0x07: prr_FactionReputationChangeCustom();        break;

                case 0x08: prr_FactionReputationIncreaseHigh_Party();  break;
                case 0x09: prr_FactionReputationIncreaseMid_Party();   break;
                case 0x0A: prr_FactionReputationIncreaseLow_Party();   break;
                case 0x0B: prr_FactionReputationDecreaseHigh_Party();  break;
                case 0x0C: prr_FactionReputationDecreaseMid_Party();   break;
                case 0x0D: prr_FactionReputationDecreaseLow_Party();   break;
                case 0x0E: prr_FactionReputationChangeCustom_Party();  break;
            }   break;

        case 0x0500:
            switch (nEntry & 0x00ff)
            {
                case 0x01: prr_CaptureChatText();                      break;
            }   break;
    }
}

If maintaining the function reference (nEntry) numbers proves challenging due to the size of the library, you can use variable incrementation to assign and access function reference numbers. This technique will require less time to build a library, however, order is exceptionally important.

Warning If a function is no longer registered by RegisterLibraryScript in OnLibraryLoad(), it must be removed from OnLibraryScript() or this technique may route the request to an undesired function. Additionally, the functions routed in OnLibraryScript() must be inserted in the same order they are registered in OnLibraryLoad().

The following is the same library definition as above, but uses variable incrementation to define the function entry numbers. The various subsections are defined by modifying the value of, in this case, n, but any variable may be used.

Note Dividing functions into sections is a technique, not a requirement.

This example uses an increment of 100 numbers between sections. If another increment is used, that number should be reflected in the value of n and the case values in OnLibraryScript().

void OnLibraryLoad()
{
    // Event functions
    int n = 100;
    RegisterLibraryScript("prr_OnComponentActivate",                 n++);
    RegisterLibraryScript("prr_OnClientEnter",                       n++);

    // Dialog utility functions
    n = 200;
    RegisterLibraryScript("prr_SetDatabaseInt",                      n++);
    RegisterLibraryScript("prr_GetDatabaseInt",                      n++);

    RegisterLibraryScript("prr_SetHasMetNPC",                        n++);
    RegisterLibraryScript("prr_GetHasMetNPC",                        n++);

    RegisterLibraryScript("prr_GetReactionHate",                     n++);
    RegisterLibraryScript("prr_GetReactionNeutral",                  n++);
    RegisterLibraryScript("prr_GetReactionLike",                     n++);

    RegisterLibraryScript("prr_SkillCheckHigh",                      n++);
    RegisterLibraryScript("prr_SkillCheckMid",                       n++);
    RegisterLibraryScript("prr_SkillCheckLow",                       n++);
    RegisterLibraryScript("prr_SkillCheckCustom",                    n++);

    // Dialog reputation functions
    n = 300;
    RegisterLibraryScript("prr_ReputationIncreaseHigh",              n++);
    RegisterLibraryScript("prr_ReputationIncreaseMid",               n++);
    RegisterLibraryScript("prr_ReputationIncreaseLow",               n++);
    RegisterLibraryScript("prr_ReputationDecreaseHigh",              n++);
    RegisterLibraryScript("prr_ReputationDecreaseMid",               n++);
    RegisterLibraryScript("prr_ReputationDecreaseLow",               n++);
    RegisterLibraryScript("prr_ReputationChangeCustom",              n++);

    RegisterLibraryScript("prr_ReputationIncreaseHigh_Party",        n++);
    RegisterLibraryScript("prr_ReputationIncreaseMid_Party",         n++);
    RegisterLibraryScript("prr_ReputationIncreaseLow_Party",         n++);
    RegisterLibraryScript("prr_ReputationDecreaseHigh_Party",        n++);
    RegisterLibraryScript("prr_ReputationDecreaseMid_Party",         n++);
    RegisterLibraryScript("prr_ReputationDecreaseLow_Party",         n++);
    RegisterLibraryScript("prr_ReputationChangeCustom_Party",        n++);

    // Dialog faction reputation functions
    n = 400;
    RegisterLibraryScript("prr_FactionReputationIncreaseHigh",       n++);
    RegisterLibraryScript("prr_FactionReputationIncreaseMid",        n++);
    RegisterLibraryScript("prr_FactionReputationIncreaseLow",        n++);
    RegisterLibraryScript("prr_FactionReputationDecreaseHigh",       n++);
    RegisterLibraryScript("prr_FactionReputationDecreaseMid",        n++);
    RegisterLibraryScript("prr_FactionReputationDecreaseLow",        n++);
    RegisterLibraryScript("prr_FactionReputationChangeCustom",       n++);

    RegisterLibraryScript("prr_FactionReputationIncreaseHigh_Party", n++);
    RegisterLibraryScript("prr_FactionReputationIncreaseMid_Party",  n++);
    RegisterLibraryScript("prr_FactionReputationIncreaseLow_Party",  n++);
    RegisterLibraryScript("prr_FactionReputationDecreaseHigh_Party", n++);
    RegisterLibraryScript("prr_FactionReputationDecreaseMid_Party",  n++);
    RegisterLibraryScript("prr_FactionReputationDecreaseLow_Party",  n++);
    RegisterLibraryScript("prr_FactionReputationChangeCustom_Party", n++);

    // General Utility functions
    n = 500;
    RegisterLibraryScript("prr_CaptureChatText",                     n++);
}

void OnLibraryScript(string sScript, int nEntry)
{
    int n = nEntry / 100 * 100;
    switch (n)
    {
        case 100:
        {
            if      (nEntry == n++) prr_OnComponentActivate();
            else if (nEntry == n++) prr_OnClientEnter();
        } break;

        case 200:
        {
            if      (nEntry == n++) prr_SetDatabaseInt();
            else if (nEntry == n++) prr_GetDatabaseInt();

            else if (nEntry == n++) prr_SetHasMetNPC();
            else if (nEntry == n++) prr_GetDatabaseInt();

            else if (nEntry == n++) prr_GetReactionHate();
            else if (nEntry == n++) prr_GetReactionNeutral();
            else if (nEntry == n++) prr_GetReactionLike();

            else if (nEntry == n++) prr_SkillCheckHigh();
            else if (nEntry == n++) prr_SkillCheckMid();
            else if (nEntry == n++) prr_SkillCheckLow();
            else if (nEntry == n++) prr_SkillCheckCustom();
        } break;

        case 300:
        {
            if      (nEntry == n++) prr_ReputationIncreaseHigh();
            else if (nEntry == n++) prr_ReputationIncreaseMid();
            else if (nEntry == n++) prr_ReputationIncreaseLow();
            else if (nEntry == n++) prr_ReputationDecreaseHigh();
            else if (nEntry == n++) prr_ReputationDecreaseMid();
            else if (nEntry == n++) prr_ReputationDecreaseLow();
            else if (nEntry == n++) prr_ReputationChangeCustom();

            else if (nEntry == n++) prr_ReputationIncreaseHigh_Party();
            else if (nEntry == n++) prr_ReputationIncreaseMid_Party();
            else if (nEntry == n++) prr_ReputationIncreaseLow_Party();
            else if (nEntry == n++) prr_ReputationDecreaseHigh_Party();
            else if (nEntry == n++) prr_ReputationDecreaseMid_Party();
            else if (nEntry == n++) prr_ReputationDecreaseLow_Party();
            else if (nEntry == n++) prr_ReputationChangeCustom_Party();
        } break;

        case 400:
        {
            if      (nEntry == n++) prr_FactionReputationIncreaseHigh();
            else if (nEntry == n++) prr_FactionReputationIncreaseMid();
            else if (nEntry == n++) prr_FactionReputationIncreaseLow();
            else if (nEntry == n++) prr_FactionReputationDecreaseHigh();
            else if (nEntry == n++) prr_FactionReputationDecreaseMid();
            else if (nEntry == n++) prr_FactionReputationDecreaseLow();
            else if (nEntry == n++) prr_FactionReputationChangeCustom();

            else if (nEntry == n++) prr_FactionReputationIncreaseHigh_Party();
            else if (nEntry == n++) prr_FactionReputationIncreaseMid_Party();
            else if (nEntry == n++) prr_FactionReputationIncreaseLow_Party();
            else if (nEntry == n++) prr_FactionReputationDecreaseHigh_Party();
            else if (nEntry == n++) prr_FactionReputationDecreaseMid_Party();
            else if (nEntry == n++) prr_FactionReputationDecreaseLow_Party();
            else if (nEntry == n++) prr_FactionReputationChangeCustom_Party();
        } break;

        case 500:
        {
            if      (nEntry == n++) prr_CaptureChatText();
        } break;

        default: CriticalError("Library function " + sScript + " not found");
    }
}