Jump to content


Changing class/race/kit restrictions with WeiDU


  • Please log in to reply
No replies to this topic

#1 -CamDawg-

-CamDawg-
  • Guest

Posted 02 April 2004 - 02:41 AM

This was a topic that I struggled with while trying to convert some kits with unusual item restrictions. Thanks to WeiDU?s new bitwise operators, this is a process that can done dynamically and non-destructively and affect all items of a particular type, even if added or altered by another mod (assuming yours is installed after others of course ). The basic idea is to construct a function in WeiDU that:

1) Searches through all item files in the game
2) Reads selected data from an item (type, usability, etc)
3) Alters specific data on an item without changing anything else

This tutorial is basically an expansion on Japheth's excellent READ/BYTE/LONG/SHORT tutorial and I suggest you take a look at that before going further.

My initial problem came about when trying to make specific items usable or unusable by a particular class/race/kit. If you look at the item file structure at IESDP, you see that all of the unusability flags of an item are controlled by the individual bits of bytes 0x1E, 0x1F, 0x20 and 0x21 for class/race alignment restrictions and 0x29, 0x2B, 0x2D and 0x2F for the individual kit restrictions. (Near Infinity combines and displays the four bytes of class/race restrictions as a single chunk of data.)

A quick aside about notation before I proceed. Numbers in binary (the strings of bits) are preceded with 0b and hexadecimal numbers (typically the bytes) are preceded with 0x. Eight bits make a byte and bits read right-to-left. So if the second bit is 1 and the rest 0, then the bit would be written as 0b00000010.

If you were trying to alter the usability of a specific class or race, simply using a WRITE_BYTE command on any of these particular bytes would result in changing the usability of an item by all the classes in the particular byte. The new bitwise operators provide an easier solution.

First we need to look at what the new operators BAND and BOR do with bits. They are both ways of combining two bytes, based on different rules. Both BAND and BOR compare the individual bits (bit 0 vs bit 0, bit 1 vs bit 1, etc. all the way through bit 7 vs bit 7) of two bytes. For each individual bit, the following tables are used to determine the value:

0 BAND 0 = 0
0 BAND 1 = 0
1 BAND 0 = 0
1 BAND 1 = 1

0 BOR 0 = 0
0 BOR 1 = 1
1 BOR 0 = 1
1 BOR 1 = 1

So if byte 0x23 is 0b00110101 and byte 0x24 is 0b10010001, then 0x23 BAND 0x24 is 0b00010001 whereas 0x23 BOR 0x24 is 0b10110101.

Back to relating this to unusability in items. For an item to be flagged as unusable, the corresponding bit must be set to 1. A 0 means a particular item is usable by the class/race. Let's look at the example of changing mage robes usable by bards. This would go into your TP2 file.

COPY_EXISTING_REGEXP GLOB ~.*\.itm~ ~override~ //copies all item files
  READ_BYTE    "0x1E" "bard" //reads the byte containing bard usability flag
  READ_BYTE    "0x20" "mage" //reads the byte containing the mage usability flag
  READ_SHORT    "0x1C" "type" //reads the byte containing item type
  WRITE_BYTE    "0x1E" ("%bard%" BAND "0b10111111")  // makes usable by bards
  IF_EVAL (("%mage%" BAND "0b00000100") = "0b00000000") // if it is usable by mages
  IF_EVAL
        ("%type%" = "67") OR  // and if it is a robe
        ("%type%" = "2")        // or armor


This is the same basic idea in Japheth's BYTE tutorial , except now we're utilizing the bitwise operators in the writing and evaluation commands. The mage usability flag is the bit 2 in byte 0x20 (IESDP). By checking "%mage%" BAND 0b00000100, the values of any of the 7 other bits (0-1,3-7) are set to 0, whereas bit 2 is equal to 0 if and only if it is 0 to begin with. Therefore the statement will only be true if the item is usable by mages (bit 2 = 0). If that is true and the item is a robe or armor, then we write ("%bard%" BAND "0b10111111") into the bard usability byte, 0x1E. By using 1 in bits 0-5 and 7, this ensures that the original value of the flag is preserved, and using 0 in bit 6 ensures that bit 6 is set to 0 regardless of its previous value--making it usable by bards whether it was before or not.

Just as an aside, you could read the entire usability block with a READ_LONG at 0x1E. However, it becomes a pain because then you need to start writing out all 32 bits when doing the bitwise operations.

Let's try another example. Let's try making large weapons such as two-handed swords and halberds unusable by short folks--gnomes, halflings, and dwarves.

COPY_EXISTING_REGEXP GLOB ~.*\.itm~ ~override~ //copies all item files
  READ_BYTE    "0x21" "race" //reads the byte containing race usability flags we're interested in
  READ_BYTE    "0x31" "prof" //reads the byte containing item type
  WRITE_BYTE    "0x21" ("%race%" BOR "0b00010101")  // makes unusable by dwarves, halflings, and gnomes
  IF_EVAL
        ("%prof%" = "93") OR  // two-handed sword
        ("%prof%" = "99")        // or halberd


In this case, rather than reading the 'type' byte of the item, I've opted for the 'proficiency' byte. Many two-handed swords in BG2 are classed as long swords for some reason so in this case proficeiency is a better indicator if it is a two-handed sword IMHO.

The unusability flags for dwarves, halflings and gnomes are all in byte 0x2F at bits 0, 2, and 4 respectively (thanks again IESDP). By writing "%race%" BOR "0b00010101" to byte 0x2F we're preserving the values of bits 1,3, and 5-7 while setting the 0, 2, and 4 bits to 1 (unusable) regardless of their previous values.

Thanks especially to Smoketest for helping me with this. Thanks also to Wes Weimer for his excellent tool, Japheth for the tutorial that inspired this and the help given, and the IESDP team.

edit: The COPY_EXISTING_REGEXP function now has the GLOB flag to ensure proper replacements.