Animatronic store is a script that causes a NPC to approach a PC, say something, then open a store whenever the PC uses (clicks on) a special placeable. The contents of the store are randomly generated and replenished over time.


The elements a module needs in order to make use of this script are

  • a conversation called "basic_merchant",
  • an NPC to initiate the dialog,
  • a placeable for PCs to click, and
  • a merchant object to be opened.

To make the script work, some guidelines must be followed. The NPC must have the word "seller" in its tag, and the placeable should have one of six keywords in its tag ("CLASS", "ARCANE", "DIVINE", "AMMO", "POTION", or "KIT"). In addition, the placeable must be useable with no inventory, and probably should be flagged as plot. The dialog does not need to be assigned to the NPC in the Toolset, but should end with a line like "Do you want to see these?", for which the second script below is the "Actions Taken" script. Finally, the merchant object needs to be placed near the placeable.

How it works[]

The script stocks the nearest store using the standard (non-expansion) treasure system included in NWN, based on the keyword in the placeable's tag. This treasure system often factors a character's class and level into which items are created; for this script, it is the NPC's class and level that affect the items. Next, the NPC moves to the PC and starts the "basic_merchant" conversation, which ends with the newly-stocked store being opened.

Script 1[]

This script is to be the container's OnUsed event handler.

#include "nw_o2_coninclude"

// Generate treasure routine modified from guess where
void GenerateStock(int nNumberItems, object oSeller, object oStore)
    // There are 6 basic types of stores.
    // The tag should include one of the 6 words below.
    int nType = 0; // defaults to class (personal items)
    if ( FindSubString( GetTag(oStore), "CLASS") >= 0 )
        nType = 0;
    else if ( FindSubString(GetTag(oStore), "ARCANE") >= 0 )
    else if ( FindSubString(GetTag(oStore), "DIVINE") >= 0 )
    else if ( FindSubString(GetTag(oStore), "AMMO") >= 0 )
    else if ( FindSubString(GetTag(oStore), "POTION") >= 0 )
    else if ( FindSubString(GetTag(oStore), "KIT") >= 0 )

    int i = 0;
    for ( i = 1; i <= nNumberItems; i++)
            case 0:  CreateTable2Item(oStore, oSeller, 0);  break;
            case 1:  CreateArcaneScroll(oStore, oSeller);   break;
            case 2:  CreateDivineScroll(oStore, oSeller);   break;
            case 3:  CreateAmmo(oStore, oSeller);           break;
            case 4:  CreatePotion(oStore, oSeller);         break;
            case 5:  CreateKit(oStore, oSeller);            break;

void main()
    object oPC = GetLastUsedBy();
    if ( !GetIsObjectValid(oPC) )
        oPC = GetLastOpenedBy();

    object oShelf = OBJECT_SELF;
    object oStore = GetNearestObject(OBJECT_TYPE_STORE, oShelf, 1);

    int nNth = 1;
    object oSeller = GetNearestObject(OBJECT_TYPE_CREATURE, oShelf, nNth);
    while ( GetIsObjectValid(oSeller) )
        if ( !GetIsPC(oSeller) )
        oSeller = GetNearestObject(OBJECT_TYPE_CREATURE, oShelf, nNth);

    // Store the merchant object.
    SetLocalObject(oPC, "Store_at", oStore);

    // Restock the store. (Simulates trade and over-ordering.)
    // Add lots if store has never been used before.
    if ( GetLocalInt(oStore, "LastStocked") == 0 )
        GenerateStock(d10(1)+10, oSeller, oStore);
    // Get what day it is.
    ///Adjustment needed: In "(GetCalendarYear()-##)", the "##" should be the module's starting year.
    int nTime = (GetCalendarYear()-65)*365 + GetCalendarMonth()*31 + GetCalendarDay();
    // restock fastest if low inventory
    nNth = 0;
    object oItem = GetFirstItemInInventory(oStore);
    while ( GetIsObjectValid(oItem) )
        oItem = GetNextItemInInventory(oStore);
    nTime = nTime + 5 / nNth;
    // Add some stock every week or so.
    if ( GetLocalInt(oStore, "LastStocked") < nTime-10 )
        GenerateStock(d4(1), oSeller, oStore);
    // Add some more stock every few days.
    if ( GetLocalInt(oStore, "LastStocked") < nTime- 5)
        GenerateStock(d4(1), oSeller, oStore);
        SetLocalInt(oStore, "LastStocked", nTime);
    // END Restocking.

    // Salesman comes over.
    AssignCommand(oSeller, ActionMoveToObject(oShelf, FALSE, 5.0));
    AssignCommand(oSeller, ActionStartConversation(oPC, "basic_merchant", TRUE));

Script 2[]

This script is an "Actions Taken" script for the last line in the dialog named "basic_merchant".

void main()
    object oPC = GetPCSpeaker();
    object oStore = GetLocalObject(oPC, "Store_at");

    if ( GetDistanceToObject(oStore) < 10.0 )
        OpenStore(oStore, oPC);


  • It is possible to merge the two scripts, eliminating the "basic_merchat" conversation. (Instead of a full-fledged conversation, the NPC would be made to speak a one-liner.)
  • The original version of this script is by WyrmSoul, and was posted in the BioWare fourms here.