Aller au contenu

Photo

Store inventory restocking scripts (code included)


  • Veuillez vous connecter pour répondre
1 réponse à ce sujet

#1
rjshae

rjshae
  • Members
  • 4 491 messages
Listed below are my store inventory restocking scripts. Currently they are primarily useful for longer campaigns, but you can easily repurpose them for shorter restocking cycles. I have performed some testing, but I have not yet used it within a game setting. Please let me know if you spot any bugs or code improvements.

The first script below is a sample 'On Open Store' script. Each restocked store will need its own version of this script, with a suitably modified 'InitRestock' function. Items that are set to be restocked should have the 'Infinite' flag set to false in the Store blueprint. (Otherwise they will be ignored for restocking purposes.)

// restock_shop
/*
    This is the On Open Store script for a sample
    store. It performs restocking of inventory that
    are frequently expended by the PCs, plus valued
    items that need occasional replacement.
*/
// RJH 08jul11

#include "inc_restock_store"

// Store-specific restock tags
const string ARROW_P1_TAG = "nw_wammar009";
const string BOLT_P1_TAG = "nw_wammbo008";
const string CLUB_P1_TAG = "nw_wblmcl002";
const string COMP_LNGBOW_P1_TAG = "nw_wbwmln010";
const string HVY_SHIELD_P1_TAG = "nw_ashmlw002";
const string LGT_SHIELD_P1_TAG = "nw_ashmsw002";
const string QUARTRSTAFF_P1_TAG = "nw_wdbmq2002";

/*    The following calls will establish the items that
    will be restocked for this store.
    
    Restocking occurs in two-day, four-day, one-week
    and two-week intervals. The parameters passed to
    each Add*StockRate call are the tag of the item
    to be restocked, the number of the item to replace
    following each interval, and the maximum number
    of said items to allow. Hence:
    
        AddTwoDayStockRate( "NW_WAXHN001", 2, 5 );
    
    will set the restock to allow 2 copies of
    "NW_WAXHN001" (Handaxe) to be added to the store
    inventory every two days, up to a maximum of 5
    copies in the inventory.
*/
void InitRestock()
{
    AddTwoDayStockRate( ARROW_TAG, 1, 4 );
    AddTwoDayStockRate( BOLT_TAG, 1, 4 );
    AddTwoDayStockRate( LIGHT_CROSSBOW_TAG, 1, 1 );
    AddTwoDayStockRate( LIGHT_SHIELD_TAG, 1, 2 );
    AddTwoDayStockRate( SHORTBOW_TAG, 1, 1 );
    AddTwoDayStockRate( POT_BARKSKIN_TAG, 1, 3 );
    
    AddFourDayStockRate( HEAVY_SHIELD_TAG, 1, 1 );
    AddFourDayStockRate( HEAVY_CROSSBOW_TAG, 1, 1 );
    AddFourDayStockRate( LONGBOW_TAG, 1, 2 );
    AddFourDayStockRate( TOWER_SHIELD_TAG, 1, 1 );
    
    AddOneWeekStockRate( ARROW_P1_TAG, 1, 1 );
    AddOneWeekStockRate( BOLT_P1_TAG, 1, 1 );
    AddOneWeekStockRate( LGT_SHIELD_P1_TAG, 1, 1 );
    
    AddTwoWeekStockRate( CLUB_P1_TAG, 1, 1 );
    AddTwoWeekStockRate( COMP_LNGBOW_P1_TAG, 1, 1 );
    AddTwoWeekStockRate( HVY_SHIELD_P1_TAG, 1, 1 );
    AddTwoWeekStockRate( QUARTRSTAFF_P1_TAG, 1, 1 );
}

void main()
{
    int bInitRestock = GetLocalInt( OBJECT_SELF, RS_INIT_RESTOCK );
    if ( !bInitRestock ) {
        /* Initialize the items to be restocked */
        InitRestock();
        
        // Only need to initialize once
        SetLocalInt( OBJECT_SELF, RS_INIT_RESTOCK, TRUE );
    }
    
    // Restock the depleted inventory
    RestockStore();
}

The include file below does all the donkey work for the restocking scripts. As a convenience, the top part of the file lists the tags of items that seem likely to be selected for restocking.

// inc_restock_store
/*
    This file includes function calls that can be used to
    perform restocking of a store. They should be called
    from the 'On Open Store' script in a store blueprint.
    The Add*StockRate routines perform the initialization
    of the restocking rate information. Each visit
    thereafter, RestockStore should be called to update
    the store inventory.
    
    Note that restock scripts are best used for items
    that are frequently expended by the party or those
    items that are sought by multiple party members and
    have a non-trivial price tag, such as longswords or
    bows. Low cost items such as clubs, daggers, torches
    and clothing, are best handled with the 'Infinite'
    setting in the store dialog.
*/
// RJH 07jul11

// Common armor and shield restock tags
const string CHAIN_SHIRT_TAG = "p_hhm_ch01";
const string CHAINMAIL_TAG = "nw_aarcl004";
const string HEAVY_SHIELD_TAG = "nw_ashlw001";
const string LEATHER_ARMOR_TAG = "nw_aarc001";
const string LIGHT_SHIELD_TAG = "nw_ashsw001";
const string SCALE_MAIL_TAG = "nw_aarcl003";
const string STUDDED_LEATHER_TAG = "nw_aarcl002";
const string TOWER_SHIELD_TAG = "nw_ashto001";

// Common potion restock tags
const string POT_ANTIDOTE = "nw_it_mpotion006";
const string POT_BARKSKIN_TAG = "nw_it_mpotion005";
const string POT_BLESS_TAG = "nw_it_mpotion009";
const string POT_CURE_LIGHT_WOUNDS_TAG = "nw_it_mpotion001";
const string HOLY_WATER_TAG = "x1_wmgrenade005";

// Common weapon restock tags
const string ARROW_TAG = "nw_wamar001";
const string BATTLEAXE_TAG = "nw_waxbt001";
const string BOLT_TAG = "nw_wambo001";
const string BULLET_TAG = "nw_wambu001";
const string DART_TAG = "nw_wthdt001";
const string HEAVY_CROSSBOW_TAG = "nw_wbwxh001";
const string LIGHT_CROSSBOW_TAG = "nw_wbwxl001";
const string LONGBOW_TAG = "nw_wbwmln006";
const string SHORTBOW_TAG = "nw_wbwsh001";
const string LONGSWORD_TAG = "nw_wswls001";
const string SHURIKEN_TAG = "nw_wthsh001";
const string THROWING_AXE_TAG = "nw_wthax001";

// Common miscellaneous restock tags
const string ACID_FLASK_TAG = "x1_wmgrenade001";
const string CHOKING_POWDER_TAG = "x1_wmgrenade004";
const string HEALERS_KIT_1_TAG = "nw_it_medkit001";
const string HEALERS_KIT_3_TAG = "nw_it_medkit002";
const string TANGLEFOOT_BAG_TAG = "x1_wmgrenade006";
const string THIEVES_TOOLS_1_TAG = "nw_it_picks001";
const string THIEVES_TOOLS_3_TAG = "nw_it_picks002";
const string THUNDERSTONE_TAG = "x1_xmgrenade007";

// Restock date tracking variables
const string RS_INIT_RESTOCK = "rs_init_restock";
const string RS_PRIOR_VISIT = "rs_prior_visit";
const string RS_TWODAY_DATE = "rs_twoday_date";
const string RS_FOURDAY_DATE = "rs_fourday_date";
const string RS_ONEWEEK_DATE = "rs_oneweek_date";
const string RS_TWOWEEK_DATE = "rs_twoweek_date";

// Inventory tracking variables
const string RS_NO_RESTOCK = "rs_no_restock_items";
const string RS_INVENTORY_TYPES = "rs_inventory_types";

// Prefixes used for creating unique local variable names
const string RS_ID_PREFIX = "rs_id_";
const string RS_NO_PREFIX = "rs_no_";
const string RS_PER_PREFIX = "rs_per_";
const string RS_TAG_PREFIX = "rs_tag_";
const string RS_CNT_PREFIX = "rs_cnt_";
const string RS_MAX_PREFIX = "rs_max_";


// Prototypes

// Initialization routines
void AddStockRate( int nPeriod, string sTag, int nCopies, int nMax );
void AddTwoDayStockRate( string sTag, int nCopies, int nMax );
void AddFourDayStockRate( string sTag, int nCopies, int nMax );
void AddOneWeekStockRate( string sTag, int nCopies, int nMax );
void AddTwoWeekStockRate( string sTag, int nCopies, int nMax );

// Calls that perform the inventory tracking and restocking
void UpdateInventory();
void RestockItem( string sTag, int nCopies, int nMax );
void RestockItems( int nPeriod, int nCount );
void RestockStore();


/*    Add the item and its restocking parameters to the list
    of items to be restocked. The nPeriod determines the
    restocking interval, sTag is the tag of the item to be
    restocked, nCopies is the number of copies to restock
    after nPeriod, and nMax is the maximum inventory.
*/
void AddStockRate( int nPeriod, string sTag, int nCopies, int nMax )
{
    string sTagLC = GetStringLowerCase( sTag );
    int nNumRestock = GetLocalInt( OBJECT_SELF, RS_NO_RESTOCK ) + 1;
    string sCount = IntToString( nNumRestock );
    SetLocalInt( OBJECT_SELF, RS_PER_PREFIX + sCount, nPeriod );
    SetLocalString( OBJECT_SELF, RS_TAG_PREFIX + sCount, sTagLC );
    SetLocalInt( OBJECT_SELF, RS_CNT_PREFIX + sCount, nCopies );
    SetLocalInt( OBJECT_SELF, RS_MAX_PREFIX + sCount, nMax );
    SetLocalInt( OBJECT_SELF, RS_NO_RESTOCK, nNumRestock );
}


// Convenience call for AddStockRate with a two-day period
void AddTwoDayStockRate( string sTag, int nCopies, int nMax )
{
    AddStockRate( 2, sTag, nCopies, nMax );
}


// Convenience call for AddStockRate with a four-day period
void AddFourDayStockRate( string sTag, int nCopies, int nMax )
{
    AddStockRate( 4, sTag, nCopies, nMax );
}

// Convenience call for AddStockRate with a one-week period
void AddOneWeekStockRate( string sTag, int nCopies, int nMax )
{
    AddStockRate( 7, sTag, nCopies, nMax );
}

// Convenience call for AddStockRate with a two-week period
void AddTwoWeekStockRate( string sTag, int nCopies, int nMax )
{
    AddStockRate( 14, sTag, nCopies, nMax );
}

/*    Clear the old inventory records, then cycle through the
    inventory, saving information about each unique item type
    as a pair of local variables on the store:
    
    1) Stock count variable:
        Name = RS_NO_PREFIX + item tag
        Content = (int) item stock count
    2) Stock index variable:
        Name = RS_ID_PREFIX + incremental index (1, 2, ...)
        Content = (string) item tag
    
    Variable 1 is used for quick lookup of an item count when
    the item tag is known. Variable 2 is used for fast cleanup
    of Variable 1 when the inventory is being re-initialized.
*/
void UpdateInventory()
{
    /* Incrementally clear the previous inventory by using
       the stock ID variable to lookup the name of the stock
       count variable, then initialize both.
    */
    int i;
    int nItemTypes = GetLocalInt( OBJECT_SELF, RS_INVENTORY_TYPES );
    for ( i = 1; i <= nItemTypes; i++ ) {
        string sStockID = RS_ID_PREFIX + IntToString( i );
        string sTag = GetLocalString( OBJECT_SELF, sStockID );
        SetLocalString( OBJECT_SELF, sStockID, "" );
        string sStockCount = RS_NO_PREFIX + sTag;
        SetLocalInt( OBJECT_SELF, sStockCount, 0 );
    }
    
    /* Cycle through the store inventory. If an item does not
       have a stock count, set the stock ID variable to the
       item tag and set the stock count variable to 1. Otherwise,
       increment the stock count by 1.
    */
    nItemTypes = 0;
    object oInventory = GetFirstItemInInventory( OBJECT_SELF );
    while( oInventory != OBJECT_INVALID ) {
        string sTag = GetStringLowerCase( GetTag( oInventory ) );
        string sStockCount = RS_NO_PREFIX + sTag;
        int nStockCount = GetLocalInt( OBJECT_SELF, sStockCount );
        if ( nStockCount > 0 ) {
            // Increment the count
            SetLocalInt( OBJECT_SELF, sStockCount, nStockCount + 1 );
        } else {
            // Check if the infinite flag is set
            if ( GetInfiniteFlag( oInventory ) ) {
                // Set to a large number
                SetLocalInt( OBJECT_SELF, sStockCount, 10000 );
            } else {
                // Initialize the count
                SetLocalInt( OBJECT_SELF, sStockCount, 1 );
            }
            
            // Store the tag
            nItemTypes++;
            string sStockID = RS_ID_PREFIX + IntToString( nItemTypes );
            SetLocalString( OBJECT_SELF, sStockID, sTag );
        }
        oInventory = GetNextItemInInventory( OBJECT_SELF );
    }
    
    // Save a count of the inventory
    SetLocalInt( OBJECT_SELF, RS_INVENTORY_TYPES, nItemTypes );
}

/*    This function will add up to nCopies copies of the item with
    the tag sTag to the current inventory, for a maximum limit
    of nMax copies.
*/
void RestockItem( string sTag, int nCopies, int nMax )
{
    string sStockCount = RS_NO_PREFIX + sTag;
    int nStockCount = GetLocalInt( OBJECT_SELF, sStockCount );
    
    // Check if the inventory is at the maximum
    if ( nStockCount >= nMax ) {
        return;
    }
    
    // Determine the amount to add
    int nAdd = nCopies;
    if (  nCopies > nMax - nStockCount ) {
        nAdd = nMax - nStockCount;
    }
    
    // Add the copies
    int i;
    for ( i = 0; i < nAdd; i++ ) {
        CreateItemOnObject( sTag, OBJECT_SELF );
    }
}


/*    The list of items to be restocked is searched for items that
    are restocked on a time interval of nPeriod days. Each time
    a matching item is found, that item is restocked with a call
    to RestockItem. The nCount value determines the number of
    restock intervals that have passed.
*/
void RestockItems( int nPeriod, int nCount )
{
    int i;
    
    // Run through the list of items to restock
    int nNumRestock = GetLocalInt( OBJECT_SELF, RS_NO_RESTOCK );
    for ( i = 1; i <= nNumRestock; i++ ) {
        string sCount = IntToString( i );
        int nRate = GetLocalInt( OBJECT_SELF, RS_PER_PREFIX + sCount );
        if ( nRate == nPeriod ) {
            /*    The restock rate matches the selected period,
                so obtain the stocking information.
            */
            string sTag = GetLocalString( OBJECT_SELF, RS_TAG_PREFIX + sCount );
            int nCopies = GetLocalInt( OBJECT_SELF, RS_CNT_PREFIX + sCount );
            int nMax = GetLocalInt( OBJECT_SELF, RS_MAX_PREFIX + sCount );
            
            // Restock the item
            RestockItem( sTag, nCount * nCopies, nMax );
        }
    }
}


/*    This is the main routine for performing restocking of a store.
    The first time this is called, it stores the starting dates
    for the restocking. Thereafter, the current date is compared
    to the date of the last restocking. If two days have passed,
    then the two-day restocking is performed. Likewise for
    four-day, one-week and two-week restocking intervals.
    
    To expedite the process, on the first used restocking interval,
    an inventory is taken that puts a lookup list of the stock
    information in local variables on the store object. That
    inventory is reused for the other restocking intervals.
*/
void RestockStore()
{
    int bHaveInventory = FALSE;
    int i, nCount;
    
    // Get a numerical date
    int nDay = GetCalendarDay();
    int nMonth = GetCalendarMonth();
    int nYear = GetCalendarYear();
    int nDate = nDay + (28 * (nMonth + (12 * nYear)));
    
    // Check for a previous visit
    int bPriorVisit = GetLocalInt( OBJECT_SELF, RS_PRIOR_VISIT );
    if ( !bPriorVisit ) {
        /* Initialize the restock trackers in such a manner
           that four restock cycles will be performed. This
           will ensure that the store is well stocked.
        */
        SetLocalInt( OBJECT_SELF, RS_TWODAY_DATE, nDate - 8 );
        SetLocalInt( OBJECT_SELF, RS_FOURDAY_DATE, nDate - 16 );
        SetLocalInt( OBJECT_SELF, RS_ONEWEEK_DATE, nDate - 28 );
        SetLocalInt( OBJECT_SELF, RS_TWOWEEK_DATE, nDate - 56 );
        SetLocalInt( OBJECT_SELF, RS_PRIOR_VISIT, TRUE );
    }
    
    // Check for a two day restock
    int nTwoDay = GetLocalInt( OBJECT_SELF, RS_TWODAY_DATE );
    if ( ( nDate - nTwoDay ) >= 2 ) {
        if ( bHaveInventory == FALSE ) {
            // Build an updated inventory
            UpdateInventory();
            bHaveInventory = TRUE;
        }
        
        // 2+ days have passed since the last 2-day restock
        RestockItems( 2, ( nDate - nTwoDay ) / 2 );
        SetLocalInt( OBJECT_SELF, RS_TWODAY_DATE, nDate );
    }
    
    // Check for a four day restock
    int nFourDay = GetLocalInt( OBJECT_SELF, RS_FOURDAY_DATE );
    if ( ( nDate - nFourDay ) >= 4 ) {
        if ( bHaveInventory == FALSE ) {
            // Build an updated inventory
            UpdateInventory();
            bHaveInventory = TRUE;
        }
        
        // 4+ days have passed since the last 4-day restock
        RestockItems( 4, ( nDate - nFourDay ) / 4 );
        SetLocalInt( OBJECT_SELF, RS_FOURDAY_DATE, nDate );
    }
    
    // Check for a one week restock
    int nOneWeek = GetLocalInt( OBJECT_SELF, RS_ONEWEEK_DATE );
    if ( ( nDate - nOneWeek ) >= 7 ) {
        if ( bHaveInventory == FALSE ) {
            // Build an updated inventory
            UpdateInventory();
            bHaveInventory = TRUE;
        }
        
        // 1+ week has passed since the last 1-week restock
        RestockItems( 7, ( nDate - nOneWeek ) / 7 );
        SetLocalInt( OBJECT_SELF, RS_ONEWEEK_DATE, nDate );
    }
    
    // Check for a two week restock
    int nTwoWeek = GetLocalInt( OBJECT_SELF, RS_TWOWEEK_DATE );
    if ( ( nDate - nTwoWeek ) >= 14 ) {
        if ( bHaveInventory == FALSE ) {
            // Build an updated inventory
            UpdateInventory();
//            bHaveInventory = TRUE;
        }
        
        // 2+ weeks have passed since the last 2-week restock
        RestockItems( 14, ( nDate - nOneWeek ) / 14 );
        SetLocalInt( OBJECT_SELF, RS_TWOWEEK_DATE, nDate );
    }
}

Modifié par rjshae, 12 juillet 2011 - 03:00 .


#2
rjshae

rjshae
  • Members
  • 4 491 messages
After further testing, I did find there was one refinement that I needed to make. In the
RestockItem
routine, I made the
CreateItemOnObject
call return an object and then ran it through
SetItemStackSize
with an nSize of 99 (but only when restocking objects of type arrow, bullet, dart or throwing axe). This will cause munitions such as arrows to be stocked at their maximum stack size rather than at only '1'.

I think it should be simple to enhance these routines with some code that provides random stock fluctuation over time, such as trading out some of the high end items for others of similar value. For the future though...