Random dynamic NPCs - a new method for EE

Hitherto, creating random dynamic NPC appearances (for wandering citizens etc) has been buggy, especially if variable heads and clothing are required.

Even with workarounds, missing or misaligned body parts couldn’t easily be eliminated entirely.

Now, with EE, an entirely different approach is possible, which seems to be much more robust, albeit very slightly slower to spawn (tested for a random town population of 20).

The trick is to start with a basic character template, load it into a Json, then tweak all the fields including head and clothing before spawning the NPC.

// sTemplate is the resref of the creature template to be edited
// nHead, nHair, nSkin, sTalk and nGender are randomly-determined values
// sDress is the clothing resref chosen at random
// PORTRAIT_BLANK = 515 removes the need to select a portrait matching the head
// oWaypoint is a waypoint object at which the NPC is to spawn  
      json   jTemplate = TemplateToJson(sTemplate, RESTYPE_UTC);

      jTemplate = GffReplaceByte  (jTemplate, "Appearance_Head", nHead);
      jTemplate = GffReplaceByte  (jTemplate, "Color_Hair",      nHair);
      jTemplate = GffReplaceByte  (jTemplate, "Color_Skin",      nSkin);
      jTemplate = GffReplaceByte  (jTemplate, "Color_Tattoo1",   nSkin);
      jTemplate = GffReplaceByte  (jTemplate, "Color_Tattoo2",   nSkin);
      jTemplate = GffReplaceResRef(jTemplate, "Conversation",    sTalk);
      jTemplate = GffReplaceByte  (jTemplate, "Gender",          nGender);
      jTemplate = GffReplaceResRef(jTemplate, "Equip_ItemList/value/0/EquippedRes", sDress);
      jTemplate = GffReplaceWord  (jTemplate, "PortraitId", PORTRAIT_BLANK);

      JsonToObject(jTemplate, GetLocation(oWaypoint));

This simple example assumes that the original creature template has one (and only one) item equipped. Note the syntax “value/0” when refering to a GFF array element.

It’s just one illustration of how we can now edit templates in game, changing any fields, not just the ones that can be modified by explicit NWScript functions.

3 Likes

An alternative is to use GffGetList to process arrays within a GFF.

Here’s an example, examining the item properties of a UTI.

Not sure that it’s much clearer - notice that the keyword “value” still has to be inserted - apparently, there’s no direct helper function for lists.

// Determine whether clothing is rich, poor or neutal

int zDressStatus(string sDress)
{
  int    CLOTHING_NEUTRAL = 0;
  int    nProperty;
  json   jItem            = TemplateToJson(sDress, RESTYPE_UTI);
  json   jProperties      = GffGetList(jItem, "PropertiesList");
  int    n     = -1;
  json   jProperty        = JsonArrayGet(jProperties, ++n);

  while (JsonGetType(jProperty) != JSON_TYPE_NULL)
    {
      nProperty = JsonGetInt(JsonObjectGet(JsonObjectGet(jProperty, "PropertyName"), "value"));

      if ((nProperty == ITEM_PROPERTY_CLOTHING_POOR) || (nProperty == ITEM_PROPERTY_CLOTHING_RICH)) return nProperty;

      jProperty = JsonArrayGet(jProperties, ++n);
    }

  return CLOTHING_NEUTRAL;
}

What I’ve found out so far is that at least the GffGetXYZ() functions are rather slow because they have some overhead (type checking) which is propably not required if you know what you’re doing. I guess it’s the same with all the other functions from the nw_inc_gff file.

1 Like