Information on 2da files, which are essentially 2 dimensional array tables of information.
Note a lot of information on specific 2da files are on different pages, such as Spells and Abilities for spells.2da.
2da Format
The 2da format is relatively simple:
- The top line must be 2DA V2.0
- There may be one or more blank lines before the first line that contains characters, which is considered the header line
- The first column header must be blank, and that column is ignored (is usually a human readable number)
- The first column number IDs can be wrong - the game just reads each new line as a new entry (incremented from 0)
- The second and onwards columns must have the header match the in-game reference loaded, the order of columns doesn't matter. Label columns are generally ignored.
- The first column header must be blank, and that column is ignored (is usually a human readable number)
- Further lines are data rows:
- Column data - but not the header name - can be in double quotes - "text here" - to include spaces. Whether or not the game understands that one being in quotes very much depends
- Column data is separated by one or more whitespace characters (tabs, spaces)
- Blank data is classified with 4 stars: ****
- You can quote the stars ("****") or have a blank quoted field which, of course, is blank ("") but any other combination of stars (less, more, or in single quotes) will be returns literally.
- Column data - but not the header name - can be in double quotes - "text here" - to include spaces. Whether or not the game understands that one being in quotes very much depends
This all comes together to mean some very odd looking 2das can be completely valid.
You can find notes on reading 2DA files in scripts at the Lexicon page for Get2DAString. Main thing to note is on error (no column or row matching etc.) and on blank entries it returns an empty string.
Missing Columns versus Blank Columns (****)
There is different behaviour for certain columns where the column is missing entirely (eg; using a old 1.69 version of a 2da) versus the column being present but blanked (****). A notable example is the additional columns in racialtypes.2da which defaults to the 1.69 implementations if the columns are not present to maintain comatibility, and where **** can actually be counted as "Empty value" instead of "Missing value" meaning different behaviour.
Order of Columns
The game generally won't care what order columns are in except for two exceptions;
- swearfilter.2da cannot be rearranged
- The first column of the spell preference 2da files
- itemprops.2da appears to be hardcoded due to the PropColumn in baseitems.2da (although not sure if you can add more columns to it).
Adding Columns
Most game read 2da files (if not all) can have custom columns added but these are only useful for Get2DAString (and it's a much better idea to create a custom 2DA file for additional metadata).
In one instance in NWN:EE this has a valid use case the engine recognises: Additional "Spellbook" columns in spells.2da, which are now referenced by column name in classes.2da.
2DA Line Limits
There are a limit to the amount of lines the engine will properly use for 2da files. This is restricted due to network traffic (even in SP there is a "server" running) since the game was made for dialup so Bioware coded in a lot of practical limits to lower packet sizes (presumably...)
| 2da file | Line Limit (assuming 0 start) | Notes |
|---|---|---|
| baseitems.2da | 255 | Hardcoded due to network limitations; items may not function as intended. |
| classes.2da | 254 | Hardcoded due to network limitations plus "255" number is reserved as "CLASS_TYPE_INVALID" |
| feats.2da | 32,768 | uint16 used in network however not recommended to go over 32K even if 65,536 is absolute maximum. There are likely problems with subdial feats if you do this. |
| skills.2da | 32 | Hardcoded due to network limitations to 255 but GUI issues make it a 32 skill limit. May only affect the "Untrained" column however? https://github.com/Beamdog/nwn-issues/issues/171 |
| spells.2da | 4096 | Possibly the limit - needs testing, but is to do with 12 bits available for the "spell radial" entry being sent over the network. May have been removed at some point but needs fully testing. |
| iprp_damagecost.2da | 128 | Going to line 129 works for the server end (damage is done correctly) but the client won't show it correctly because it looks up 2da line "-1". Line 159 was tested which had the string "-97 Magical Damage" since 159 - 128 = 31, and 128 - 31 = 97. |
Unused 2da files
Bioware left in some 2da files from early development (before game release). These files are not loaded by the game, toolset or Get2DAString at all and can be ignored. They do get confusing however.
- areaeffects.2da - See persistent.2da instead
- caarmorclass.2da - remnants of armour system?
- catype.2da - remnants of creature parts system?
- combatmodes.2da - Unused references to (a limited set) of combat action modes
- doortype.2da - See doortypes.2da instead
- effectanim.2da - See visualeffects.2da instead
- metamagic.2da - Appears to be unused by the game making all of these hardcoded
- nwconfig.2da - Unused as of NWN:EE since it removes the "settings" nwconfig program.
- nwconfig2.2da - Was this ever used?
- resistancecost.2da - Blank
- soundcatfilters.2da - Replaced probably by rrf_wav.2da and would be toolset specific anyway
- soundtypes.2da - Sounds are not tied to a 2da, but are done by resource name
- treasurescale.2da - I don't think this is used anywhere of note. It'd be used in the toolset I'd guess but there isn't a treasure generation system there.
- vfx_fire_forget.2da - See visualeffects.2da instead
Designer 2da files
These are strictly used by Get2DAString and not used by the game inherently. However the systems they relate to are things like crafting, which is called by default game scripts and conversations the PCs can activate.
They are all prefixed des_ - and being only used by Get2DAString they are serverside only.
There is one special case here: x3restrict.2da is used in the default game spells to restrict polymorph spells when casting on a horse. Added for horse support.
Toolset only 2da files
These are only used by the toolset, not directly by the game:
- environment.2da - Used to load some initial lighting settings in the Area Properties.
- itmwizXXX.2da - Item Wizard 2da files, they relates to what properties are valid for each item type (eg; Melee) and are generated by the wizards.
- repute.2da - Used to define the default reputations of Player, Hostile, Commoner and Merchant in a new module
- rrf_nss.2da - Filters for scripts in the toolset
- rrf_wav.2da - Filters for sounds in the toolset
- waypoint.2da - "waypoint" types, of course in game are never rendered
Client and Serverside 2das
The game as a whole is made up of the client and the server. A singleplayer game essentially runs both at once for the local user - of course other players may still join if it is created as a network game. Therefore it is important to understand which is which, since some 2da files you don't need to include at all in hakpack or nwsync resource lists, and can be left in the .mod or override folder on a PW server perfectly fine.
The client ones tend to be based around levelling up, appeareances, visuals (icons, vfx, textures, etc.) and sounds (the latter 3 servers don't even load of course). Servers need to know about all the resources loaded by the game in regards to objects, spells, etc.
It is recommended of course for the server to have copies of everything (if only for Get2DAString lookups) while you can lessen the amount put in client hakpacks or on nwsync if sending certain ones to the client.
Note: If a file is not listed here (or above as a toolset only, or unused one) it might still be used!
Client and Server
| 2da load reference | 2da Name | Notes |
|---|---|---|
| m_paIPRPCostTables // all of them | All cost tables in iprp_costtable.2da column "Name" | Example: iprp_bonuscost.2da |
| m_pAppearanceTable | appearance.2da | |
| m_pGenderTable | gender.2da | |
| m_pSurfaceMaterialTable | surfacemat.2da | |
| m_pVisualEffectTable | visualeffects.2da | |
| m_pPersistentVisualEffectTable | vfx_persistent.2da | |
| m_pCreatureSpeedTable | creaturespeed.2da | |
| m_pDoorTypesTable | doortypes.2da | |
| m_pGenericDoorsTable | genericdoors.2da | |
| m_pPlaceablesTable | placeables.2da | |
| m_pAreaTransitionTable | loadscreens.2da | |
| m_pIPRPSpells | iprp_spells.2da | |
| m_pIPRPLightTable | Not sure - probably iprp_lightcost.2da ? | |
| m_pIPRPColor | iprp_color.2da | |
| m_pIPRPMelee | iprp_meleecost.2da | |
| m_pItemPropDefTable | itempropdef.2da | |
| m_pItemPropsTable | itemprops.2da | |
| m_pCreatureSizeTable | creaturesize.2da | |
| m_pArmorTable | armor.2da | |
| m_pEncumbranceTable | encumbrance.2da | |
| m_pPortraitTable | portraits.2da | |
| m_pPartsChest | parts_chest.2da | |
| m_pSpellSchoolTable | spellschools.2da | |
| m_pTrapTable | traps.2da | |
| m_pLightColorTable | lightcolor.2da | |
| m_pIPRPDamageTable | iprp_damagecost.2da | |
| m_pPartsRobe | parts_robe.2da | |
| m_pItemValue | itemvalue.2da | |
| m_pPackages | packages.2da | Presumably also all the relevant class package 2das |
| chargenclothes.2da | Clothing items must be part of the base game, or included in the hak as UTI resources | |
feat.2da masterfeats,2da | Likely needed for both (chargen needs feat.2da, and server obviously needs it for backend stuff) | |
| namefilter.2da | No bad words, naughty! Probably char gen only? | |
| swearfilter.2da | No swearing for you! | |
| prioritygroups.2da | Sound related but may be just client side (along with some others) usually never edited by hakpacks anyway. |
Client Only
While many of these are only loaded by the game client, the toolset would still make regular use of them, for instance, to be able to have a blueprint reference a particular wing model on a suitable NPC.
It is always recommended to have these available on the server if only for Get2DAString lookups.
| 2da load reference | 2da Name | Notes |
|---|---|---|
| m_pPartTable | Not sure? | Model appearance related |
| m_pArmorTypesTable | armourtypes.2da | Sounds loading (cloth, leather, plate, chain) |
| m_pTileColorTable | tilecolor.2da | Texture loading |
| m_pReplaceTextureTable | replacetexture.2da | Texture loading |
| m_pTailModelTable | tailmodel.2da | Model appearance related |
| m_pWingModelTable | wingmodel.2da | Model appearance related |
| m_pCloakModelTable | cloakmodel.2da | Model appearance related Note: CopyItemAndModify() may use this server-side in some instances (non-PLT based ones which there are none in the base game). |
| m_pCursorTable | cursors.2da | For the cursors for certain default game actions. |
| m_pActionsTable | actions.2da | For GUI icons list (player action list top left) |
| m_pAmbientMusicTable | ambientmusic.2da | Server tells client which line to load, which refers to this for the sound files |
| m_pAmbientSoundTable | ambientsound.2da | Server tells client which line to load, which refers to this for the sound files |
| m_pFootstepSoundsTable | footstepsounds.2da | Sounds loading |
| m_pAppearanceSoundsTable | appearancesndset.2da | Sounds loading |
| m_pWeaponSoundsTable | weaponsounds.2da | Sounds loading |
| m_pDefaultACSoundsTable | defaultacsounds.2da | Sounds loading |
| m_pAmmunitionTypesTable | ammunitiontypes.2da | Sounds loading |
| m_pKeymapTable | keymap.2da | Client GUI default keys/shortcuts |
| m_pPlaceableSoundsTable | placeableobjsnds.2da | Sounds loading |
| m_pDamageLevelTable | damagelevels.2da | GUI showing how damaged something is (Formats the text over creatures heads) |
| m_pEffectIconsTable | effecticons.2da | GUI effect icons (top right, inspect panel on items/characters, character sheet) |
| m_pLoadHintsTable | loadhints.2da | Loading string text loading |
| m_pSwearFilterTable | swearfilter.2da | It's funny how this is here. I presume it alters incoming messages not outgoing ones? Needs testing. Servers can implement changes to PC chat to implement a filter using OnPlayerChat event. |
| m_pPhenoTypeTable | phenotype.2da | Model appearance related |
| m_pIPRPVisualFxTable | iprp_visualfx.2da | Visual effects |
| m_pWeatherTypesTable | weathertypes.2da | Client is just told which line to load |
Server Only
| 2da load reference | 2da Name | Notes |
|---|---|---|
| Any custom 2da | eg: des_crft_aparts.2da | Loaded with Get2DAString so entirely script based access |
| m_pStateScriptsTable | statescripts.2da | Runs specific scripts |
| m_pPoisonTable | poison.2da | Applies effects / timings of effect changes |
| m_pDiseaseTable | disease.2da | Applies effects / scripts / timing of effect changes |
| m_pRepAdjustmentsTable | repadjust.2da | Engine changes to "reputation" ie; if they're hostile or not. |
| m_pFractionalCRTable | fractionalcr.2da | Works out how much a goblin with 1/4CR is "worth" in the engine XP system/"difficulty" tags (eg; "Easy" enemy when inspected) |
| m_pEncounterDifficultyTable | encdifficulty.2da | The "value" column is loaded when referenced from the encounters GFF |
| m_pCategoryTable | categories.2da | Used by the functions for talents. Some is hardcoded (eg; potion lines). So yeah. |
| m_pExcitedDurationTable | excitedduration.2da | Presumably amount of time in milliseconds (so 10 seconds) the thing is considered in combat after the events listed |
| m_pRestDurationTable | restduration.2da | Amount of milliseconds to sit on the ground for |
| m_pPartsBelt | parts_belt.2da | |
| m_pPartsBicep | parts_bicep.2da | |
| m_pPartsFoot | parts_foot.2da | |
| m_pPartsForearm | parts_forearm.2da | |
| m_pPartsHand | parts_hand.2da | |
| m_pPartsLegs | parts_legs.2da | |
| m_pPartsNeck | parts_neck.2da | |
| m_pPartsPelvis | parts_pelvis.2da | |
| m_pPartsShin | parts_shin.2da | |
| m_pPartsShoulder | parts_shoulder.2da | |
| m_pXpBase | exptable.2da | Levelling up chart |
| m_pAssociatesAnimalCompanionTable | hen_companion.2da | |
| m_pAssociatesFamiliarTable | hen_familiar.2da | |
| m_pSkillVsItemCostTable | skillvsitemcost.2da | |
| m_pRangesTable | ranges.2da | Note the "45.0M" range for streaming in usable placeables is hardcoded, and a maximum of visibility regardless of values here of any creature object. |
| m_pPolymorphTable | polymorph.2da | |
| m_pDamageHitVisualTable | damagehitvisual.2da | Sends a relevant visualeffect.2da line number to the client |
| m_pBodyBagTable | bodybag.2da | References a placeables.2da line |
| m_pIPRPFeatsTable | iprp_feats.2da | |
| m_pIPRPMonsterDamageTable | iprp_monsterdam.2da | |
| m_pOnHitTable | iprp_onhit.2da | Note this pretty much is hardcoded |
| m_pOnHitSpellTable | iprp_onhitspell.2da | |
| m_pOnHitDurationTable | iprp_onhitdur.2da | |
| m_pIPRPACModTypeTable | iprp_acmodtype.2da | |
| m_pIPRPWeightIncTable | iprp_weightinc.2da | |
| m_pIPRPArcSpell | iprp_arcspell.2da | |
| m_pIPRPBonusCostTable | iprp_bonuscost.2da | |
| m_pIPRPSRCostTable | iprp_srcost.2da |
NWNX Reference
Source of what is referenced by the game engine (not complete - some 2da files seem to be loaded from elsewhere such as swear filters). It relates to this file: https://github.com/nwnxee/unified/blob/master/NWNXLib/API/API/CTwoDimArrays.hpp