Well, I've read some side comments on other topics that lead me to believe that WeiDU does not necessarily execute code in the order it was written. I'd like to evaluate what can be done safely. Let's start with a bit of example code (FYI: it corrects the Wizard saving throw table, which was supposed to get a final boost at level 21):
The WHILE loop does the real work, and it's framed with a couple of R_T to protect the header formating. The whole snippet did alright for me, but when I sent it to a friend, he complained that the R_T part didn't work and the header formating was screwed up (he's at v153, while I'm still at v148 (being a Mac head, I didn't dare to compile a new version yet)). Any comments/suggestions?
Then there's these two snippets. Both yield the same result (changing the color of the Dragon Helm to something easier on the eyes) and work alright for me:
The first one, above, closely follows Idobek's wonderful tutorial. Yet, there's some redundancy in there, namely four "%#fx%" - 1. So, I slightly changed it, into this:
Here I've moved the SET "#fx" = ("%#fx%" - 1) right to the beginning of the loop, so that I need to do the calculation only once. It seems to be working for me, yet the question remains: how safe is this? Can I be certain that the SET is always executed before the subsequent READ commands? Any comments/suggestions?
Cheers, Armin
Apologies I never followed up on this. It's good to see you here.
The problem with SET_2DA_ENTRY is that every execution will re-format the entire table (the columns are all made equal to the widest entry in the array). So if you try to do both the 2DA V1.0 and first column header with SET_2DA_ENTRY, only one will work (as the last action will mess with any previous formatting actions). The following works flawlessly:
COPY_EXISTING ~SAVEWIZ.2DA~ ~override~
SET_2DA_ENTRY 0 0 2 ~2DA_V1.0~
SET_2DA_ENTRY 0 1 2 ~~
SET_2DA_ENTRY 0 0 40 ~2DA 1~
SET ~column~ = 21
WHILE (~%column%~ < 41)
BEGIN
SET_2DA_ENTRY 1 ~%column%~ 41 ~8 ~
SET_2DA_ENTRY 2 ~%column%~ 41 ~3~
SET_2DA_ENTRY 3 ~%column%~ 41 ~5~
SET_2DA_ENTRY 4 ~%column%~ 41 ~7~
SET_2DA_ENTRY 5 ~%column%~ 41 ~4~
SET ~column~ = ~%column%~ + 1
END
SET_2DA_ENTRY 0 0 41 ~~
REPLACE_TEXTUALLY ~2DA_V1.0~ ~2DA V1.0~
The perfect solution would be if
WeiDU didn't treat the 2DA header and magic number as valid entries when it goes to modify the file. Since it does, we can only use SET_2DA_ENTRY once for formatting (in the example above, I delete the V1.0 "column," essentially making the 2DA header one column). Additionally,
WeiDU will treat any text-whitespace-text as two columns, and the auto-resizing of the columns makes it unnecessary to worry about width (meaning that all actions can be done by changing the first column to "2DA column1Header," or even "1 column1Header"). When using SET_2DA_ENTRY to remove the header,
WeiDU will do a final re-formatting, ensuring that all the headers are in the right position.
The problem I was experiencing previously was most likely because I didn't take into account the fact that each SET_2DA_ENTRY action re-formats the table. I also ran into a strange problem when I tried to separate the formatting commands (done with REPLACE_TEXTUALLY) from the rest of the action block (I put them all at the end of the component). It almost seemed as if
WeiDU was doing the REPLACE_TEXTUALLY actions before the blocks with SET_2DA_ENTRY (even though they were the last actions in the component). All of the formatting was incorrect (the column 1 header would actually appear at column 2 with whitespace for columns 0 and 1). I never bothered to fully research why this was happening (the code above works perfectly, so I just went with it).
I never tried your formatting actions, but I suspect they would have worked perfectly here (just a style decision- I tried to find a different way because I didn't like the idea of placing "dummy" text just to delete it later).
On the subject of variables (I had never though of using a loop for these either, the original action list was quite lengthy before Scar presented me with this solution), note that SET_2DA_ENTRY apparently performs variable substitution for the row, column and required column count parameters, but they cannot be used for the actual value (so SET_2DA_ENTRY 0 0 1 ~%variable%~ wouldn't work, it just sets the 2DA entry to %variable%). Additionally, unless it's keyed to ignore other files, this can potentially be used for any tabular file (IDS files come to mind). Also, SET_2DA_ENTRY can be used to add columns. Fixing XPLIST.2DA previously required you to REPLACE_TEXTUALLY the entire line, but now it can be done as such:
COPY_EXISTING ~XPLIST.2DA~ ~override~
SET_2DA_ENTRY 0 0 2 ~2DA_V1.0~
SET_2DA_ENTRY 0 1 2 ~~
SET_2DA_ENTRY 0 0 41 ~2DA COMMENT~
SET_2DA_ENTRY 67 38 39 ~-1 -1 -1 -1~
SET_2DA_ENTRY 0 0 42 ~~
REPLACE_TEXTUALLY ~2DA_V1.0~ ~2DA V1.0~
Since
WeiDU will reformat the table with the last SET_2DA_ENTRY action, no concern needs to be given to the spacing (any whitespace is treated as the start of the new colum for that row). This is, IMO, much cleaner than the previous method.
On the subject of variables, I've found that the READ actions are always executed first, regardless of their position in the block. For instance, in the following:
COPY_EXISTING ~nothing~ ~override~
READ_LONG 0x00 ~someOffset~
WRITE_LONG 0x00 ~%someOffset%~ + 0x14
DO_SOMETHING
READ_LONG 0x00 ~newOffset~
the value of newOffset will be identical to someOffset, even though you have a WRITE_LONG action that should change the value before the range is re-read. This made it tough for me to update things like spell indices, as the method I was using required
WeiDU to read the updated bytes. The solution here was to move these particular actions to a separate block, so that the correct values will be read. In most cases, you won't want to re-read a byte more than once- you'll instead want to use SET to either replace the old variable or create a new one (so, if you changed the offset in the example above, you'd want to do the following):
COPY_EXISTING ~something~ ~override~
READ_LONG 0x00 ~someOffset~
WRITE_LONG 0x00 ~%someOffset%~ + 0x14
SET ~someOffset~ = ~%someOffset%~ + 0x14
This will insure that the value of someOffset is correct when used again in the block. As far as I can tell, SET is performed sequentially with the rest of the actions (wherever it appears in the file). So you should never have to worry about the value if you explicitly use SET, as long as it occurs before the action that requires the variable to have the new value.