JSON is a new feature of NWN:EE that allows you to load templates to JSON or generate JSON directly from text.

These can be used to check a GFF object or alter data before creating things.

JSON is also used for the Nui system that can generate GUI elements.

Helper Include for GFF editing

Beamdog created an include file to help with JSON manipulation although you can replicate this yourself using the base game nwscript functions; nw_inc_gff.nss is the file.

JSON for Nui

See separate page on Nui.

Helper Pages on this wiki

There will be pages made for the fields list of each main GFF object available in the game to JSONfy:

  • RESTYPE_CAF (and RESTYPE_ARE, RESTYPE_GIT, RESTYPE_GIC)

  • RESTYPE_UTC - Creature JSON

  • RESTYPE_UTI

  • RESTYPE_UTT

  • RESTYPE_UTP

  • RESTYPE_UTD

  • RESTYPE_UTW

  • RESTYPE_UTE

  • RESTYPE_UTM

Use these to find what fields to replace.

It's probably a better idea to use TemplateToJSON and edit it, not generate the entire object in nwscript unless you want to spend a lot of time and energy writing valid JSON structures.

You can save JSON text to SQL as well to load it back later. For instance you could have an editable / upgradable henchman which is stored when a player logs out into SQL and back again.

Checking JSON of GFF objects

A simple way to generate JSON from a template and check the text returned is to write it to server log nwclientLog1.txt

void main()
{
    json jChicken = TemplateToJson("nw_chicken", RESTYPE_UTC);

    string sChicken = JsonDump(jChicken, 2);

    WriteTimestampedLogEntry(sChicken);
}

This generates this output:

{
  "Appearance_Type": {
    "type": "word",
    "value": 31
  },
  "BodyBag": {
    "type": "byte",
    "value": 0
  },
  "CRAdjust": {
    "type": "int",
    "value": 0
  },
  "Cha": {
    "type": "byte",
    "value": 6
  },
  "ChallengeRating": {
    "type": "float",
    "value": 0.25
  },
  "ClassList": {
    "type": "list",
    "value": [
      {
        "Class": {
          "type": "int",
          "value": 12
        },
        "ClassLevel": {
          "type": "short",
          "value": 1
        },
        "__struct_id": 2
      }
    ]
  },
  "Comment": {
    "type": "cexostring",
    "value": ""
  },
  "Con": {
    "type": "byte",
    "value": 8
  },
  "Conversation": {
    "type": "resref",
    "value": ""
  },
  "CurrentHitPoints": {
    "type": "short",
    "value": 5
  },
  "Deity": {
    "type": "cexostring",
    "value": ""
  },
  "Description": {
    "type": "cexolocstring",
    "value": {
      "id": 12415
    }
  },
  "Dex": {
    "type": "byte",
    "value": 7
  },
  "Disarmable": {
    "type": "byte",
    "value": 0
  },
  "Equip_ItemList": {
    "type": "list",
    "value": []
  },
  "FactionID": {
    "type": "word",
    "value": 2
  },
  "FeatList": {
    "type": "list",
    "value": [
      {
        "Feat": {
          "type": "word",
          "value": 289
        },
        "__struct_id": 1
      }
    ]
  },
  "FirstName": {
    "type": "cexolocstring",
    "value": {
      "id": 12416
    }
  },
  "Gender": {
    "type": "byte",
    "value": 1
  },
  "GoodEvil": {
    "type": "byte",
    "value": 50
  },
  "HitPoints": {
    "type": "short",
    "value": 5
  },
  "Int": {
    "type": "byte",
    "value": 3
  },
  "Interruptable": {
    "type": "byte",
    "value": 1
  },
  "IsPC": {
    "type": "byte",
    "value": 0
  },
  "LastName": {
    "type": "cexolocstring",
    "value": {}
  },
  "LawfulChaotic": {
    "type": "byte",
    "value": 50
  },
  "MaxHitPoints": {
    "type": "short",
    "value": 6
  },
  "NaturalAC": {
    "type": "byte",
    "value": 0
  },
  "NoPermDeath": {
    "type": "byte",
    "value": 0
  },
  "PaletteID": {
    "type": "byte",
    "value": 6
  },
  "PerceptionRange": {
    "type": "byte",
    "value": 11
  },
  "Phenotype": {
    "type": "int",
    "value": 0
  },
  "Plot": {
    "type": "byte",
    "value": 0
  },
  "PortraitId": {
    "type": "word",
    "value": 168
  },
  "Race": {
    "type": "byte",
    "value": 8
  },
  "ScriptAttacked": {
    "type": "resref",
    "value": "nw_c2_default5"
  },
  "ScriptDamaged": {
    "type": "resref",
    "value": "nw_c2_default6"
  },
  "ScriptDeath": {
    "type": "resref",
    "value": "nw_c2_default7"
  },
  "ScriptDialogue": {
    "type": "resref",
    "value": "nw_c2_default4"
  },
  "ScriptDisturbed": {
    "type": "resref",
    "value": "nw_c2_default8"
  },
  "ScriptEndRound": {
    "type": "resref",
    "value": "nw_c2_default3"
  },
  "ScriptHeartbeat": {
    "type": "resref",
    "value": "nw_c2_default1"
  },
  "ScriptOnBlocked": {
    "type": "resref",
    "value": "nw_c2_defaulte"
  },
  "ScriptOnNotice": {
    "type": "resref",
    "value": "nw_c2_default2"
  },
  "ScriptRested": {
    "type": "resref",
    "value": "nw_c2_defaulta"
  },
  "ScriptSpawn": {
    "type": "resref",
    "value": "nw_c2_herbivore"
  },
  "ScriptSpellAt": {
    "type": "resref",
    "value": "nw_c2_defaultb"
  },
  "ScriptUserDefine": {
    "type": "resref",
    "value": "nw_c2_defaultd"
  },
  "SkillList": {
    "type": "list",
    "value": [
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 2
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 2
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      },
      {
        "Rank": {
          "type": "byte",
          "value": 0
        },
        "__struct_id": 0
      }
    ]
  },
  "SoundSetFile": {
    "type": "word",
    "value": 21
  },
  "SpecAbilityList": {
    "type": "list",
    "value": []
  },
  "Str": {
    "type": "byte",
    "value": 3
  },
  "Subrace": {
    "type": "cexostring",
    "value": ""
  },
  "Tag": {
    "type": "cexostring",
    "value": "NW_CHICKEN"
  },
  "Tail": {
    "type": "byte",
    "value": 0
  },
  "TemplateList": {
    "type": "list",
    "value": []
  },
  "TemplateResRef": {
    "type": "resref",
    "value": "nw_chicken"
  },
  "WalkRate": {
    "type": "int",
    "value": 7
  },
  "Wings": {
    "type": "byte",
    "value": 0
  },
  "Wis": {
    "type": "byte",
    "value": 8
  },
  "__data_type": "UTC ",
  "fortbonus": {
    "type": "short",
    "value": 0
  },
  "refbonus": {
    "type": "short",
    "value": 0
  },
  "willbonus": {
    "type": "short",
    "value": 0
  }
}

nim-tools

Another alternative to look through GFF information is to use offline tools, this can be done with the nim-tools that output similar JSON to the game:

https://github.com/niv/neverwinter.nim

These can be run on a GFF file (eg chicken.utc as above) for similar output.

Note however there are two kinds of GFF - the one on the template file and the in-game struct, which you can get with ObjectToJson. These can differ quite significantly sometimes due to how the game generates fields (saved in a save game) with the active state of some parts of the creature.

nim-tools Differences to TemplateToJson

The JSON for some fields may be different, for instance nim-tools nwn_gff:

  "LocalizedName": {
    "id": 181,
    "type": "cexolocstring",
    "value": {
      "0": "Adorned Full Plate"
    }
  },

Similar block from the game:

 "LocalizedName": {
  "type": "cexolocstring",
  "value": {
   "0": "The Name",
   "id": 123
  }
 },

Due to this you might want to use the Wiki pages to find the in-game compatible key-value fields.

Finding Values Example

A simple example to find a usually inaccessible field. This doesn't use nw_inc_gff.nss so explains step by step the process of parsing the array of information.

const int SOUNDSET_INVALID = -1;

// Retrieves the line in soundset.2da
// Returns SOUNDSET_INVALID if they have no soundset assigned or on error.
// * oCreature - Creature to check
int GetCreatureSoundset(object oCreature)
{
    if(GetObjectType(oCreature) != OBJECT_TYPE_CREATURE) return SOUNDSET_INVALID;

    // Convert to JSON
    json jGet = ObjectToJson(oCreature);

    // Retrieve the key
    jGet = JsonObjectGet(jGet, "SoundSetFile");

    if(JsonGetType(jGet) == JSON_TYPE_NULL) return SOUNDSET_INVALID;

    // Retrieve the value
    jGet = JsonObjectGet(jGet, "value");

    if(JsonGetType(jGet) != JSON_TYPE_INTEGER) return SOUNDSET_INVALID;

    return JsonGetInt(jGet);
}

It can be simplified with the include:

#include "nw_inc_gff"

const int SOUNDSET_INVALID = -1;

// Retrieves the SOUNDSET_* constant, a line in soundset.2da
// Returns SOUNDSET_INVALID if they have no soundset assigned or on error.
// * oCreature - Creature to check
int GetCreatureSoundset(object oCreature)
{
    if(GetObjectType(oCreature) != OBJECT_TYPE_CREATURE) return SOUNDSET_INVALID;

    // Return the soundset variable
    return JsonGetInt(GffGetWord(ObjectToJson(oCreature), "SoundSetFile"));
}

void main()
{
    SpeakString("My soundset: " + IntToString(GetCreatureSoundset(OBJECT_SELF)));
}

Altering Values Example

A simple example for how to edit some JSON on a field and creating that object, in this case a placeable having it's appearance field changed so when it's spawned in it is different:

#include "nw_inc_gff"
void main()
{
    // Generate JSON from resref template
    json jPlaceable = TemplateToJson("plc_armoire", RESTYPE_UTP);
 
    // Change to appearance 390
    jPlaceable = GffReplaceDword(jPlaceable, "Appearance", 33);
 
    // Spawn
    JsonToObject(jPlaceable, GetLocation(OBJECT_SELF));
}
  • No labels