Author Topic: Reading a CRE's shield slot and determining the item_type within?  (Read 3121 times)

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Hello.

Right now I'm using the following code to patch CRE files so they use BG1's sprites instead of BG2's:

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x28 current
  PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
    PATCH_IF (current = old) BEGIN
      WRITE_LONG 0x28 new
    END
  END
  BUT_ONLY

(there's an associative array before this block from which "old" and "new" are derived)

However CREs that come equipped with two weapons at the time of patching exhibit graphical glitches.

It is worth noting that my mod applies exclusively to the Enhanced Editions, where the game does not crash if BG1 sprites are made to dual-wield. If a CRE is patched to use a BG1 sprite first, then made to dual-wield thereafter, you have two possible scenarios:

  • If your main-hand weapon is equipped first, and your off-hand weapon is equipped thereafter, no weapon will show and your character will look like they're punching the enemy (even though mechanically they still get all the perks of dual-wielding whatever weapons you equipped.)
  • If your off-hand weapon is equipped first, and your main-hand weapon is equipped thereafter, only your main-hand weapon will show and your character will look like they're going single-weapon style (with your main-hand weapon visually performing all of the attacks.)

So here's my conundrum: I want to read inside a CRE's shield slot, and selectively patch only CREs whose shield slot is either empty or populated with a shield. I don't want to exclude CREs with pips in two-weapon fighting as that would exclude a lot of CREs that aren't dual-wielding (including all Rangers who get two pips in that fighting style from the get-go.)

The following code checks whether the shield slot is empty and patches CREs only if that returns true:

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber
  READ_SSHORT (itemslot + 0x04) shieldslot
   PATCH_IF (shieldslot = "-1") BEGIN
    READ_LONG 0x28 current
    PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
      PATCH_IF (current = old) BEGIN
        WRITE_LONG 0x28 new
      END
    END
    BUT_ONLY

What I do not know is how to add code to this block that checks for the item_type in the shield slot, and says "If it's empty or populated by a shield, go ahead and patch; otherwise do nothing."

Any tips?

Offline critto

  • Planewalker
  • *****
  • Posts: 4
You could probably check if file referenced in the shield slot is present in the game's files, then do a "COPY - %filename%.itm inlined/%filename%.itm" to read its attributes (item_type, namely) and make decision based on the field's value. Maybe define all this as a function for better code composition.

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
How would I go about checking for the file contained in the shield slot, though?

Will my designation "shieldslot" do? E.g.

Code: [Select]
PATCH_IF (FILE_EXISTS_IN_GAME ~%shieldslot%.itm~) BEGIN
(Probably not; just shooting in the dark here.)
« Last Edit: May 27, 2016, 11:17:52 AM by Andrea C. »

Offline critto

  • Planewalker
  • *****
  • Posts: 4
No, what you have in your %shieldslot% is either a "-1" or an offset into the actual table of items encoded into the creature's file. You need to use offset to items in order to retrieve the item name. See http://gibberlings3.net/iesdp/file_formats/ie_formats/cre_v1.htm for more info. You use the offset as a base and then go into the structure according to your shield slot value and the size of an item table record (14 bytes, I think).

To retrieve the shield slot, it should be something like that:

Code: [Select]
READ_ASCII ((LONG_AT 0x02bc) + %shieldslot% * 0x14) itemresname

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
I've prepared this chunk of code to handle CRE patching, but every CRE is getting patched regardless:

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber
  READ_SSHORT (itemslot + 0x04) shieldslot
  READ_ASCII ((LONG_AT 0x02bc) + %shieldslot% * 0x14) itemname
   COPY ~%itemname%.itm~ ~override~
    READ_SHORT 0x1c itemtype ELSE 0
     PATCH_IF (itemtype = 12) OR (shieldslot = "-1") BEGIN
      READ_LONG 0x28 current
      PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
        PATCH_IF (current = old) BEGIN
          WRITE_LONG 0x28 new
        END
      END
END
    BUT_ONLY

It's as though the itemtype section was being skipped entirely. Same happens with the following:

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber
  READ_SSHORT (itemslot + 0x04) shieldslot
  READ_ASCII ((LONG_AT 0x02bc) + %shieldslot% * 0x14) itemname
   COPY ~%itemname%.itm~ ~override~
    READ_SHORT 0x1c itemtype ELSE 0
     PATCH_IF (itemtype = 12) BEGIN
      READ_LONG 0x28 current
      PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
        PATCH_IF (current = old) BEGIN
          WRITE_LONG 0x28 new
        END
      END
END
ELSE BEGIN
PATCH_IF (shieldslot = "-1") BEGIN
      READ_LONG 0x28 current
      PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
        PATCH_IF (current = old) BEGIN
          WRITE_LONG 0x28 new
END
      END
    END
    END
    BUT_ONLY


What am I doing wrong?  ???
« Last Edit: June 19, 2016, 02:48:39 AM by Andrea C. »

Offline Wisp

  • Moderator
  • Planewalker
  • *****
  • Posts: 1176
You need to use INNER_ACTION. As posted, you code would be more appropriately formatted like this:
Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber
  READ_SSHORT (itemslot + 0x04) shieldslot
  READ_ASCII ((LONG_AT 0x02bc) + %shieldslot% * 0x14) itemname

COPY ~%itemname%.itm~ ~override~
  READ_SHORT 0x1c itemtype ELSE 0
  PATCH_IF (itemtype = 12) OR (shieldslot = "-1") BEGIN
    READ_LONG 0x28 current
    PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
      PATCH_IF (current = old) BEGIN
        WRITE_LONG 0x28 new
      END
    END
  END
BUT_ONLY

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Thank you, Wisp.

I had not considered INNER_ACTION because its tutorial in the WeiDU documentation suggests it's a very advanced feature, and I'm barely a beginner.

Indeed, because the example in the tutorial is very different from what I'm trying to do, I find its application to my case a bit confusing:

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber
  READ_SSHORT (itemslot + 0x04) shieldslot
  READ_ASCII ((LONG_AT 0x02bc) + %shieldslot% * 0x14) itemname

  INNER_ACTION BEGIN
   COPY ~%itemname%.itm~ ~override~
    READ_SHORT 0x1c itemtype ELSE 0
     PATCH_IF (itemtype = 12) OR (shieldslot = "-1") BEGIN
      READ_LONG 0x28 current
      PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
        PATCH_IF (current = old) BEGIN
          WRITE_LONG 0x28 new
        END
      END
    END
  END
  BUT_ONLY

Unsurprisingly, this also doesn't seem to work.

Should I be using INNER_PATCH instead of PATCH_IF?

The main source of confusion here is that I'm trying to combine an INNER_ACTION clause (i.e. check whether the item equipped is a shield) with a non-INNER_ACTION one (i.e. check whether the shield slot is populated at all.)

Perhaps INNER_ACTION should happen before I try to read the item in the slot?

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber
  READ_SSHORT (itemslot + 0x04) shieldslot

  INNER_ACTION BEGIN
  READ_ASCII ((LONG_AT 0x02bc) + %shieldslot% * 0x14) itemname
   COPY ~%itemname%.itm~ ~override~
    READ_SHORT 0x1c itemtype ELSE 0
  END
  PATCH_IF (itemtype = 12) OR (shieldslot = "-1") BEGIN
    READ_LONG 0x28 current
    PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
      PATCH_IF (current = old) BEGIN
        WRITE_LONG 0x28 new
       END
    END
  END
  BUT_ONLY


Offline subtledoctor

  • Planewalker
  • *****
  • Posts: 131
I'm on a phone so can't check this. But it addresses
1) the ordering of INNER_ACTION and the stuff that should or should not be inside it; and
2) I'm not sure you're using %shieldslot% correctly, so I tried to abstract away from it by just setting a variable in cases where you need your operation to run on the .cre

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber 
  READ_SSHORT (itemslot + 0x04) shieldslot
  PATCH_IF (shieldslot > 0) BEGIN
    READ_ASCII ((LONG_AT 0x02bc) + %shieldslot% * 0x14) itemname
    INNER_ACTION BEGIN
      COPY ~%itemname%.itm~ ~override~
        READ_SHORT 0x1c itemtype ELSE 0
        PATCH_IF (itemtype = 12) BEGIN
          OUTER_SET validslot = 1
        END
      BUT_ONLY
    END
  END
  ELSE BEGIN
    SET validslot = 1
  END
  PATCH_IF (validslot = 1) BEGIN
    READ_LONG 0x28 current
    PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
      PATCH_IF (old = current) BEGIN
        WRITE_LONG 0x28 new
       END
    END
  END
BUT_ONLY
« Last Edit: July 06, 2016, 12:51:49 PM by subtledoctor »

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Hi subtledoctor,

thanks for the code.

Unfortunately, it's giving me an error message: http://i.imgur.com/cWNbrQ2.jpg

Any idea what might be causing it?


Offline Magus_BGforge

  • Planewalker
  • *****
  • Posts: 75
OUTER_SET is an action, you use it in patch context. Remove "OUTER_SET".

And by the way, if you've dived this deep, you're not a beginner.

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Re: Reading a CRE's shield slot and determining the item_type within?
« Reply #10 on: July 10, 2016, 02:38:22 PM »
OUTER_SET is an action, you use it in patch context. Remove "OUTER_SET".

If I remove OUTER_SET, won't I be one variable short?

My understanding of the code is that I need "validslot" to be defined as "itemtype=12" in the item that was read within the INNER_ACTION. Perhaps I should replace "PATCH_IF" with "ACTION_IF" before I OUTER_SET?
« Last Edit: July 10, 2016, 02:40:47 PM by Andrea C. »

Offline subtledoctor

  • Planewalker
  • *****
  • Posts: 131
Re: Reading a CRE's shield slot and determining the item_type within?
« Reply #11 on: July 11, 2016, 06:51:08 AM »
No, he means I was using OUTER_SET improperly. Just change it to SET.

ACTIONs are for new events - e.g. COPY is an ACTION. PATCH is for when you're in the middle of an operation. WRITE_SHORT is a PATCH because it happens *within* the COPY operation.

You can set variables either way, it's just that the command is different. If, in the middle of your code, you up and decide to set a variable, you can do it as an ACTION by using OUTER_SET. If, as here, it is inside a COMy operation and bound by a PATCH_IF conditional, then you (and I) should use SET.

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Re: Reading a CRE's shield slot and determining the item_type within?
« Reply #12 on: July 11, 2016, 01:48:38 PM »
Thank you, subtledoctor.

I applied the change and the mod installs just fine; however, CREs with weapons in their shield slots are still getting the BG1 sprite for some reason  ???

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Re: Reading a CRE's shield slot and determining the item_type within?
« Reply #13 on: June 11, 2017, 03:54:51 PM »
Hi everyone.

I'm back on this after admittedly a fairly long time.

I was wondering why exactly this code didn't work:

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber
  READ_SSHORT (itemslot + 0x04) shieldslot
  PATCH_IF (shieldslot > 0) BEGIN
    READ_ASCII ((LONG_AT 0x02bc) + %shieldslot% * 0x14) itemname
    INNER_ACTION BEGIN
      COPY ~%itemname%.itm~ ~override~
        READ_SHORT 0x1c itemtype ELSE 0
        PATCH_IF (itemtype = 12) BEGIN
          SET validslot = 1
        END
      BUT_ONLY
    END
  END
  ELSE BEGIN
    SET validslot = 1
  END
  PATCH_IF (validslot = 1) BEGIN
    READ_LONG 0x28 current
    PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
      PATCH_IF (old = current) BEGIN
        WRITE_LONG 0x28 new
       END
    END
  END
BUT_ONLY


So I looked at a CRE I knew was getting patched despite wielding a weapon in their off-hand: OHBJOKER.CRE (from The Black Pits II)

0x2b8 corresponds indeed to item slots.

To get to the shield slot, I'm adding 0x04 as per the "values and expressions" tutorial from the WeiDU documentation:

Quote
Next, we’re going to read in the shield slot to make sure that it’s empty. We do this by taking the "itemslot" variable and adding 0x04 to it which we know is always going to be the shield slot. (You can verify this by looking at a creature file in NI. If you look at the offset 0x2b8 it will have the offset for item slots. If you add 0x04 to that number, it should equal the offset for the shield slot.)

However, 0x2b8 + 0x04 equals 0x2bc according to Google; a far cry from the 0x1fa0 that I can see in NI as the shield slot. How do I get to 0x04? On a related note, why do I need to read the item offset and go through all this trouble instead of reading 0x1fa0 directly? I mean, obviously that won't work or it would be all over WeiDU's documentation; I'm trying to understand the reason why it doesn't work and the reason why 0x2b8 + 0x04 should get me that slot in particular.

The value at 0x2b8 is 0x1f9c.

0x1f9c + 0x04 = 0x1fa0 which is exactly the offset for shield. This offset's value for OHBJOKER.CRE is "1", indicating that the shield slot is populated.

The code that follows is less clear:

Code: [Select]
READ_ASCII ((LONG_AT 0x02bc) + %shieldslot% * 0x14) itemname
I reckon from the WeiDU documentation that items are always 0x14, but that doesn't tell me why I need to multiply (item offset + shield slot) by it. This results in

(0x1efc + 0x1fa0) = 0x3E9C * 0x14 = 0x4E430

whereas the shield slot item's offset is 0x1f10.

What am I missing?

Also, should this:

Code: [Select]
PATCH_IF (shieldslot > 0) BEGIN
be this:

Code: [Select]
PATCH_IF (shieldslot > "0") BEGIN
instead?

Does it even make a difference?
« Last Edit: June 12, 2017, 01:26:00 PM by Andrea C. »

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Re: Reading a CRE's shield slot and determining the item_type within?
« Reply #14 on: June 12, 2017, 01:43:11 PM »
So I've experimented a little bit and the following code:

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber
  READ_SSHORT (itemslot + 0x04) shieldslot
  PATCH_IF (shieldslot > "0") BEGIN
    READ_ASCII ((LONG_AT 0x2bc) + %shieldslot% * 0x14) itemname
    INNER_ACTION BEGIN
      COPY ~%itemname%.itm~ ~override~
        READ_SHORT 0x1c itemtype
        PATCH_IF (itemtype = 12) BEGIN
          SET validslot = 1
        END
      BUT_ONLY
    END
  END
  ELSE BEGIN
    SET validslot = 1
  END
  PATCH_IF (%validslot% = 1) BEGIN
    READ_LONG 0x28 current
    PATCH_PHP_EACH cd_animation_map AS old => new BEGIN
      PATCH_IF (old = current) BEGIN
        WRITE_LONG 0x28 new
       END
    END
  END
BUT_ONLY

Appears to be working until I get this error message:

(http://i.imgur.com/OW6wt5O.png)

AESOLD.CRE appears to be the first .CRE with something in the shield slot (specifically, SHLD15.ITM.)

Any idea why it would fail to copy it over?

Offline CamDawg

  • Infidel
  • Planewalker
  • *****
  • Posts: 859
  • Dreaming of a red Xmas
    • The Gibberlings Three
Re: Reading a CRE's shield slot and determining the item_type within?
« Reply #15 on: June 12, 2017, 07:44:29 PM »
You'll want COPY_EXISTING, not COPY, on that INNER_ACTION. Also, you should safeguard your code with a sanity check to make sure the item exists:

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber
  READ_SSHORT (itemslot + 0x04) shieldslot
  PATCH_IF (shieldslot > "0") BEGIN
    READ_ASCII ((LONG_AT 0x2bc) + %shieldslot% * 0x14) itemname
    INNER_ACTION BEGIN
      COPY_EXISTING ~%itemname%.itm~ ~override~
        READ_SHORT 0x1c itemtype
        PATCH_IF (itemtype = 12) BEGIN
          SET validslot = 1
        END
      BUT_ONLY IF_EXISTS
    END
  END
The Gibberlings Three - Home of IE Mods

The BG2 Fixpack - All the fixes of Baldurdash, plus a few hundred more. Now available, with more fixes being added in every release.

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Re: Reading a CRE's shield slot and determining the item_type within?
« Reply #16 on: June 13, 2017, 01:27:01 PM »
Thank you, CamDawg.

The code now installs, but it still patches all CREs regardless of whether their shield slot is occupied by a weapon.

I've injected the following:

Code: [Select]
PRINT ~itemtype is %itemtype% and validslot is %validslot%~
for good measure and "validslot = 1" is systematically set  regardless of the value of itemtype.

In theory I would expect there to be no validslot set at all if itemtype isn't 12. Should I inject additional code like this to ensure that validslot isn't 1 when itemtype isn't 12?

Code: [Select]
INNER_ACTION BEGIN
      COPY_EXISTING ~%itemname%.itm~ ~override~
        READ_SHORT 0x1c itemtype
        PATCH_IF (itemtype = 12) BEGIN
          SET validslot = 1 ELSE validslot = 0
        END
      BUT_ONLY IF_EXISTS
    END
« Last Edit: June 13, 2017, 01:30:14 PM by Andrea C. »

Offline CamDawg

  • Infidel
  • Planewalker
  • *****
  • Posts: 859
  • Dreaming of a red Xmas
    • The Gibberlings Three
Re: Reading a CRE's shield slot and determining the item_type within?
« Reply #17 on: June 13, 2017, 03:06:43 PM »
Yeah, remember that variables persist--the previous code had no mechanism to drop validslot back to 0, so once it was set to 1 it stayed there. The problem with setting it to zero in the INNER_ACTION is that the INNER_ACTION itself is conditional, e.g. it could get blocked by a null shield reference or bad file name, meaning it could be at 1 from the previous creature and not set back to 0. This is what you're looking for:

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  SET validslot = 0
  READ_LONG 0x2bc itemsoffset
  READ_LONG 0x2b8 itemslot
  READ_LONG 0x2c0 itemnumber
  READ_SSHORT (itemslot + 0x04) shieldslot
  PATCH_IF (shieldslot > "0") BEGIN
    READ_ASCII ((LONG_AT 0x2bc) + %shieldslot% * 0x14) itemname
    INNER_ACTION BEGIN
      COPY_EXISTING ~%itemname%.itm~ ~override~
        READ_SHORT 0x1c itemtype
        PATCH_IF (itemtype = 12) BEGIN
          SET validslot = 1
        END
      BUT_ONLY IF_EXISTS
    END
  END
Putting that validslot = 0 at the top of the patch resets it for every creature checked.
« Last Edit: June 13, 2017, 03:08:00 PM by CamDawg »
The Gibberlings Three - Home of IE Mods

The BG2 Fixpack - All the fixes of Baldurdash, plus a few hundred more. Now available, with more fixes being added in every release.

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Re: Reading a CRE's shield slot and determining the item_type within?
« Reply #18 on: June 13, 2017, 03:57:14 PM »
Eureka! It works! Thank you so much, CamDawg! :)

Offline Wisp

  • Moderator
  • Planewalker
  • *****
  • Posts: 1176
Re: Reading a CRE's shield slot and determining the item_type within?
« Reply #19 on: June 14, 2017, 11:12:09 AM »
In WeiDU 241 you'll be able to do
Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
  PATCH_WITH_SCOPE BEGIN
    // insert code here
  END
BUT_ONLY
and no variables will be carried over between files patched.
« Last Edit: June 14, 2017, 11:17:26 AM by Wisp »

 

With Quick-Reply you can write a post when viewing a topic without loading a new page. You can still use bulletin board code and smileys as you would in a normal post.

Warning: this topic has not been posted in for at least 120 days.
Unless you're sure you want to reply, please consider starting a new topic.

Name: Email:
Verification:
Type the letters shown in the picture
Listen to the letters / Request another image
Type the letters shown in the picture:
What color is grass?:
What is the seventh word in this sentence?:
What is five minus two (use the full word)?: