Initial PC inventory

Hopefully a trivial question…

When a player starts a module for the first time, how is their initial inventory set? Like, you tend to start with a few healing potions, a torch, maybe some armor, maybe a weapon… Is that hard-coded into the game, or is there a script that populates it? Relatedly, is OnModuleLoad called whenever the player starts a new game, or is it also called whenever a saved game is loaded? If OnModuleLoad is invoked on loading a saved game, is there an event that triggers only when a module is started for the first time?

I imagine, if OnModuleLoad is called every time, then you need to use variables to flag whether a PC is spawning for the first time ever.

They come in with what they have. If creating a new character, they get what the package 2da for their class says they get (and that can be literally anything, minor headache mp).

On module load does fire for when the game is loaded from save, and variables are a good thing to deal with whether you want to know if it’s the first time their character is in module.

Makes sense. I guess this still means that you could empty their inventory when they spawn in, if you wanted to do away with that headache. I mean, doesn’t Hordes of the Underdark do that?

The following empties the inventory on new Characters. It replaces the script “On client enter”:

// Script: mod_def_enter

void main()
{
  object oPC = GetEnteringObject();

  object o;
  int x = GetXP (oPC);
  if (x == 0)
    {
      o = GetFirstItemInInventory(oPC);
      while (GetIsObjectValid (o))
        {
          DestroyObject (o);
          o = GetNextItemInInventory(oPC);
        }
      GiveXPToCreature (oPC, 1);
    }
  ExecuteScript("x3_mod_def_enter", OBJECT_SELF);
}

The script of HotU is a bit different, the items are not deleted but moved into a container instead.

It’s also useful to tell area and trigger entry scripts that we are loading a saved game, so that they don’t fire unnecessarily. The “Reload” switch should be deleted at the end of area entry (trigger scripts fire first). OnClientEnter needs something like this:

  if (GetLocalInt(oPC, "ModuleEnteredBefore"))
    {
      SetLocalInt(oPC, "Reload", TRUE); 
      return;
    }

  SetLocalInt(oPC, "ModuleEnteredBefore", TRUE);

Here’s a script that moves everything to a container, including equipped items and gold, but excluding the skin and (optionally) clothing.

I find that a subsequent delay of about 2 seconds is helpful before equipping any new gear.

This was written before script parameters were available, hence the use of local variables on the PC before the script is called.

// Strip all items to a nominated container
// Works for any object that has an inventory
// Option to leave armour equipped

#include "bh__lib"

void main()
{
    object oPC        = GetFirstPC();
    object oCreature  = OBJECT_SELF;
    object oChest     = GetLocalObject(oCreature, "bh_chest");
    int    bNotArmour = GetLocalInt(oCreature, "bh_NotArmour");
    object oEquip;
    int i;

    DeleteLocalInt(oCreature, "bh_NotArmour");

    // Strip equipped items

    if (GetObjectType(oCreature) == OBJECT_TYPE_CREATURE)
      {
        for (i=0; i<NUM_INVENTORY_SLOTS; i++)
          {
            oEquip = GetItemInSlot(i, oCreature);

            if(GetIsObjectValid(oEquip))
              if (i != INVENTORY_SLOT_CARMOUR)    // Don't remove PC skin
                if ((i != INVENTORY_SLOT_CHEST) || !bNotArmour)
                  {
                    CopyItem(oEquip, oChest, TRUE);
                    SetPlotFlag(oEquip, FALSE);
                    DestroyObject(oEquip);
                  }
         }
     }

   // Strip inventory

   // If an inventory item is a container with contents, CopyItem doesn't work,
   // so we use CreateItemOnObject instead; destroying the container item moves the
   // contents to regular inventory, from where they are copied to the target container
   // normally.

    object oItem = GetFirstItemInInventory(oCreature);
    while(GetIsObjectValid(oItem))
        {
          if (GetTag(oItem) != "x3_it_pchide")   // Don't remove PC skin
            {
              if (GetHasInventory(oItem))
                CreateItemOnObject(GetResRef(oItem), oChest, 1, GetTag(oItem));
              else
                CopyItem(oItem, oChest, TRUE);
              SetPlotFlag(oItem, FALSE);
              DestroyObject(oItem);
            }
          oItem = GetNextItemInInventory(oCreature);
        }

    // Copy gold

    if (oCreature == oPC)
      {
        int nAmount = GetGold(oPC);

        while (nAmount > 50000)
          {
            CreateItemOnObject("NW_IT_GOLD001", oChest, 50000);
            AssignCommand(oPC,TakeGoldFromCreature(50000,oPC,TRUE));
            nAmount -= 50000;
          }

        if(nAmount > 0)
            {
              CreateItemOnObject("NW_IT_GOLD001", oChest, nAmount);
              AssignCommand(oPC,TakeGoldFromCreature(nAmount,oPC,TRUE));
            }
      }
}

Thanks a bunch. Your code reminded me that I need to set the player’s starting gold, but is there a reason you have a loop to take gold in 50k increments? Like does TakeGoldFromCreature get dodgy if you use an unboundedly high parameter?

Gold has a maximum stacksize of 50000 in objects that are not PCs.