Jump to content

Photo

Changing Class/Race/Kit Restrictions with WeiDU


  • This topic is locked This topic is locked
No replies to this topic

#1 CamDawg

CamDawg

    ALL GLORY TO THE HYPNOTOAD

  • Gibberling Poobah
  • 9656 posts
  • Gender:Not Telling

Posted 02 February 2004 - 10:12 PM

Well, since it's now part of the WeiDU documentation, I guess it would be funny if this wasn't at least mirrored here.  :D


This was a topic that I struggled with while trying to convert some kits with unusual item restrictions. Thanks to WeiDU's bitwise operators, this is a process that can be 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 byte 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 (BYTE_AT 0x23) BAND (BYTE_AT 0x24) is 0b00010001 whereas (BYTE_AT 0x23) BOR (BYTE_AT 0x24) is 0b10110101.

Note: the symbols & and | can be used as shortcuts for BAND and BOR, respectively. These should not be confused with the symbols && and ||, which are shortcuts for AND and OR, respectively.

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~ // copy all item files
  PATCH_IF (SOURCE_SIZE > 0x71) BEGIN
    READ_SHORT 0x1c type // read the bytes containing item type
    READ_BYTE  0x1e usability1 // read the byte containing bard usability flag
    READ_BYTE  0x20 usability3 // read the byte containing the mage usability flag
    PATCH_IF (type == 67 || type == 2) BEGIN // if it is a robe or armor
      PATCH_IF ((usability3 BAND 0b00000100) == 0b00000000) BEGIN // if it is usable by mages
        WRITE_BYTE 0x1e (usability1 BAND 0b10111111)
      END
    END
  END
  BUT_ONLY_IF_IT_CHANGES
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 bit 2 in byte 0x20 (IESDP). By checking (usability3 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 (usability1 BAND 0b10111111) into the bard usability byte, 0x1e. By using 1 in bits 0-5 and 7, this ensures that the original values of those flags are 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~ // copy all item files
  PATCH_IF (SOURCE_SIZE > 0x71) BEGIN
    READ_BYTE 0x21 usability4 // read the byte containing race usability flags we're interested in
    READ_BYTE 0x31 proficiency // read the byte containing weapon proficiency type
    PATCH_IF (proficiency == 93 || proficiency == 99) BEGIN // if it is a two-handed sword or halberd
      WRITE_BYTE 0x21 (usability4 BOR 0b00010101) // make unusable by dwarves, halflings, and gnomes
    END
  END
  BUT_ONLY_IF_IT_CHANGES
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, proficiency 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 (usability4 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.


With the addition of the WeiDU variable THIS, many bitwise patches can be written with less code. Whenever a WRITE_BYTE, WRITE_SHORT, or WRITE_LONG command is performed, WeiDU will first execute a READ_BYTE, READ_SHORT, or READ_LONG and store the previous value in a variable named THIS. Using THIS allows us to avoid issuing our own READ_BYTE commands ahead of time.

Here are the same patches from above, with one READ_BYTE command in each replaced by using THIS.
COPY_EXISTING_REGEXP GLOB ~^.+\.itm$~ ~override~ // copy all item files
  PATCH_IF (SOURCE_SIZE > 0x71) BEGIN
    READ_SHORT 0x1c type // read the bytes containing item type
    READ_BYTE  0x20 usability3 // read the byte containing the mage usability flag
    PATCH_IF (type == 67 || type == 2) BEGIN // if it is a robe or armor
      PATCH_IF ((usability3 BAND 0b00000100) == 0b00000000) BEGIN // if it is usable by mages
        WRITE_BYTE 0x1e (THIS BAND 0b10111111)
      END
    END
  END
  BUT_ONLY_IF_IT_CHANGES
COPY_EXISTING_REGEXP GLOB ~^.+\.itm$~ ~override~ // copy all item files
  PATCH_IF (SOURCE_SIZE > 0x71) BEGIN
    READ_BYTE 0x31 proficiency // read the byte containing weapon proficiency type
    PATCH_IF (proficiency == 93 || proficiency == 99) BEGIN // if it is a two-handed sword or halberd
      WRITE_BYTE 0x21 (THIS BOR 0b00010101) // make unusable by dwarves, halflings, and gnomes
    END
  END
  BUT_ONLY_IF_IT_CHANGES
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.

Edited by Mike1072, 14 March 2015 - 11:15 PM.
removed gibberish caused by encoding issues, modernized, and added notes about THIS

Why is this Hypnotoad video so popu... ALL GLORY TO THE HYPNOTOAD.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users