A course on SFO - chapter 2.5

lib_2daq: speed-optimized access to in-game 2da files

Back to contents

The lib_2da library, and its paradigm of loading a 2da file into an array, is very powerful and flexible; that flexibility, however, comes at some cost in speed. You won't notice this case-by-case, but if you are writing code that edits dozens or hundreds of 2da files, the time cost can add up. The lib_2daq library (the 'q' stands for 'quick') uses the simpler paradigm of editing a 2da file in place, and is optimized for speed, being only slightly slower than native WEIDU.

lib_2daq files operate only on in-game 2das, which are normally identified with the STR_VAR 'resref'.

2.5.1 Getting data from a 2da

The main function used to get data from a 2da is '2daq_extract', which reads a specified row, column, or entry from a 2da file (or from the current 2da file in patch context).

Here is an example that extracts the entire Paladin row from alignmnt.2da:

LAF 2daq_extract STR_VAR resref=alignmnt row=PALADIN RET_ARRAY array END

The 'array' array has entries like $array(L_G)=1.

This code extracts the entire Paladin column from weapprof.2da:

LAF 2daq_extract STR_VAR resref=weapprof column=PALADIN RET_ARRAY array END

If you specify both row and column, you get a unique value rather than an array: here's code to find the biography string of the Dark Moon monk from clastext.2da:

LAF 2daq_extract STR_VAR resref=clastext row=DARK_MOON column=BIOGRAPHY RET value END

You can specify a column other than the leftmost column to use as the source of row headers; in keeping with lib_2daq's focus on speed, you do so via the column's number rather than its header. Here's code to get the Dark Moon monk's clab file:

LAF 2daq_extract 
	INT_VAR rowname_column=1 
	STR_VAR 
		resref=kitlist 
		row=DARK_MOON 
		column=ABILITIES 
	RET clab=value 
END

By default, 2daq_extract operates in mixed case. You can force case by setting the STR_VAR 'case' to 'upper' or 'lower'.

lib_2daq also includes functions 2daq_has_row and 2daq_has_column. These are patch functions, and report whether the current 2da has a given row or column. For instance, the following checks if the Tempus kit is present in-game (it was missing on certain pre-2.6 games):

COPY_EXISTING - clastext.2da nowhere
	LPF 2daq_has_row STR_VAR row=OHTEMPUS RET value END

This code checks if there is a column for OHTEMPUS in weapprof:

COPY_EXISTING - weapprof.2da nowhere
	LPF 2daq_has_column STR_VAR column=OHTEMPUS RET value END

You can use rowname_column with 2daq_has_row, just as with 2daq_extract. This code checks for OHTEMPUS in kitlist.2da:

COPY_EXISTING - kitlist.2da nowhere
	LPF 2daq_has_row INT_VAR rowname_column=1 STR_VAR row=OHTEMPUS RET value END

2.5.2 Inserting data into a 2da

2daq_inject

This function, given a struct 'array', will inject all relevant entries in the struct into a .2da file (either the one specified by 'resref', or, in patch context, the current one). That is, if 'x' and 'y' are row and column labels in the 2da, and 'array_x_y' is set, the appropriate 2da entry will be changed to that value. For example, the following code alters the alignment table in a slightly weird way:

ACTION_DEFINE_ASSOCIATIVE_ARRAY aligns BEGIN
	PALADIN,L_G=>0
	PALADIN,N_G=>1
	PALADIN,C_G=>1
	FIGHTER,C_E=>0
END
LAF 2da_inject STR_VAR array=aligns resref=alignmnt END

Note that the struct entries do not need to be gathered into an array: the following code has the same effects.

OUTER_SET aligns_PALADIN_L_G=0
OUTER_SET aligns_PALADIN_N_G=1
OUTER_SET aligns_PALADIN_C_G=1
OUTER_SET aligns_FIGHTER_C_E=0
LAF 2da_inject STR_VAR array=aligns resref=alignmnt END

As usual, you can set the INT_VAR rowname_column to use a different column from the leftmost one.

2da_insert_row, 2da_insert_column

These patch functions insert a new row / column into the current 2da, filled with the default entry (controlled, recall, by the second line of the 2da file). For 2da_insert_row, this is always the last row, e.g. this code adds a new row to the bottom of the alignment table for a kit 'MYKIT' and populates it with zeroes:

COPY_EXISTING alignmnt.2da override
	LPF 2daq_insert_row STR_VAR row=MYKIT END

You can specify rowname_column if you want.

2daq_insert_column works almost identically: this adds a new column for MYKIT to weapprof.2da and fills it with zeroes:

COPY_EXISTING weapprof.2da override
LPF 2daq_insert_column STR_VAR column=MYKIT END

However, 2daq_insert_column takes an extra optional STR_VAR argument, insert_loc. By default it is 'last'; if set to 'before_last', it inserts the new column one column as the second-last column. (There is a rather obscure use case for this that I shan't bore you with.)

2daq_copy_row, 2daq_copy_column

These work similarly to 2daq_insert_row/column, except that instead of inserting a blank row/column, an existing one is duplicated. The existing row is identified by 'row' or 'column'; the new one by 'row_new' or 'column_new'. Here's code that copies PALADIN for MYKIT:

COPY_EXISTING alignmnt.2da override
	LPF 2daq_copy_row STR_VAR row=PALADIN row_new=MYKIT END

COPY_EXISTING weapprof.2da override
LPF 2daq_insert_column STR_VAR column=PALADIN column_new=MYKIT END

You can use the same other options as for inserting a row/column.