nwn

Database integration

Using the Bioware Data base is easier than most might think. If you understand how to "Set" and "Get" Local variables then you are more than half way there.

For example, to set a Local variable on a PC, you might use something like this: SetLocalInt( oPC, "VARIABLE_NAME", 1).

Storing this variable in the Database is just as simple. The only difference is you must provide a Campaign (or database) name. I.e. SetCampaignInt( "DATABASE_NAME", "VARIABLE_NAME", 1, oPC).

The ability to assign variables to specific PCs allows this single data base to hold a lot of information about each PC, without the need to create separate DBs for each PC.

Stat Storage Script

Take the script, for example, for when the PC joins the game. Tt will record each stat of each PC – all in one DB.

// Place in OnClientEnter
// Will store each ability score as:
// ABILITY_0 = STRENGTH
// ABILITY_1 = DEXTERITY
// ABILITY_2 = CONSTITUTION
// ABILITY_3 = INTELLIGENCE
// ABILITY_4 = WISDOM
// ABILITY_5 = CHARISMA
void main()
{
    object oPC = GetEnteringObject();
    if(!GetIsPC(oPC)) return;
    int i,iAb;
    for(i=0; i<6; i++)
        {
        iAb = GetAbilityScore(oPC,i);
        SetCampaignInt("STATS","ABILITY_"+IntToString(i),iAb,oPC);
        }
}

First Player Abilities

Since each score is assigned to the entering PC, the next PC won’t over-write the previously stored values. When the first PC enters, the database basically will look like this;

ABILITY_0 ___ JassperJassper Krane ___ 17 
ABILITY_1 ___ JassperJassper Krane ___ 29 
ABILITY_2 ___ JassperJassper Krane ___ 16 
ABILITY_3 ___ JassperJassper Krane ___ 16 
ABILITY_4 ___ JassperJassper Krane ___ 10 
ABILITY_5 ___ JassperJassper Krane ___ 14

Note the "JassperJassper Krane" -- this is the Player ID that is set when the variable is assigned to a PC object. This uses the player's Login name plus the character name.

Second Player Abilities

So what happens to the database when a second PC logs in? It will look something like this:

ABILITY_0 ___ JassperJassper Krane ___ 17 
ABILITY_1 ___ JassperJassper Krane ___ 29 
ABILITY_2 ___ JassperJassper Krane ___ 16 
ABILITY_3 ___ JassperJassper Krane ___ 16 
ABILITY_4 ___ JassperJassper Krane ___ 10 
ABILITY_5 ___ JassperJassper Krane ___ 14 
ABILITY_0 ___ JassperJade Krane ___ 17 
ABILITY_1 ___ JassperJade Krane ___ 30 
ABILITY_2 ___ JassperJade Krane ___ 17 
ABILITY_3 ___ JassperJade Krane ___ 17 
ABILITY_4 ___ JassperJade Krane ___ 11 
ABILITY_5 ___ JassperJade Krane ___ 15

Notice that the original data is still there, along with the new Player stats; it is all within the same database. When a PC logs in for the second time (and that PC's stats have already been recorded), values will be over-written with the character's current values. In other words, any changes are be recorded.

Check Ability Value

Now if you needed to check an ability value, lets say INTELLIGENCE, use the GetCampaignInt() function, which works a lot like the GetLocalInt() function. We simply need to specify what database to draw from, like this: GetCampaignInt( "STATS", "ABILITY_3", oPC). If oPC is the "JassperJade Krane" player object, then the result returned would be 17.

Obviously, the example above has no real value as it would be much less CPU intensive to simply call GetAbilityScore() whenever an ability score is needed rather than store them in a DB and retrieve them later, but it is important to know what assigning the variables to Player objects do.

With Quests

Now lets create something somewhat useful.

Here is a Scenario: Let's say I have 5 quests and each quest has different "levels" of completion. I want to record what level of completion each PC is on for each quest, and restore those values when the characters enter the module.

During Game play I will set local variables on the PC and the in the database; by doing so, I don’t need to access the DB each time I need to check the status of a quest. I will most likely check the value many times more often during a conversation, and only need to set the values once.

Status Change (Conversation)

For example, a PC starts some of the quest via some conversations. In one of the ActionTaken scripts I would set the values with something like this:

void main()
{
    object oPC = GetPCSpeaker();
    SetLocalInt(oPC,"QUEST_1",3);
    SetCampaignInt("QUEST_STATUS","QUEST_1",3,oPC);
}

This will set the completion level of quest 1 to 3 on my PC and in the DB.

Checking Status (Conversation)

Later, in a different conversation I can simply check what level the PC is on in a quest by a Starting Conditional script something like this;

int StartingConditional()
{
    if(GetLocalInt(GetPCSpeaker(),"QUEST_1") == 3)
        {
        return TRUE;
        }
    return FALSE;
}

Reapplication after Server Reset

So let's say I played long enough to get to the 3rd part of quest 1, didn’t start quest 2, got to the 2nd part of quest 3, didn’t start quest 4, and completed quest 5. I want to restore these numbers when that PC logs back in after a server reset so My OnClientEnter script would look something like this:

void main()
{
    object oPC = GetEnteringObject();
    if(!GetIsPC(oPC))return;
    int i,iQuest;
    for(i=1; i<6; i++)
        {
        iQuest = GetCampaignInt("QUEST_STATUS","QUEST_"+IntToString(i),oPC);
        SetLocalInt(oPC,"QUEST_"+IntToString(i),iQuest);
        }
}

This will retrieve the variables from the DB and set them back onto my PC. Now all the quest states are restored on the PC and all my convo scripts can still check the quest status using GetLocalInt(). What happens when a PC logs in that doesn’t have any records in the Database? Nothing really, all the QUEST_* variables will be set to 0 on that PC, which is what they are by default.

Notes

Message me if this info was helpful or if you need further clarification. If so I will continue with other DB functions.