Jump to content

WeiDU CRE patching - how to add BG2 proficiencies?


Recommended Posts

For the next revision of Rogue Rebalancing, I'm trying to patch as many creatures as possible instead of directly overwriting the .CRE files. Thanks to my namesake's excellent IE Lister plugin, I've managed to get all the base values which I wanted to change and patching that in worked perfectly. However, I have absolutely no idea how to add new BG2 proficiencies to creatures which currently have none assigned to them. I've tried studying some of the fixpack code (i.e. the Anomen proficiency fix) but I don't understand how it works. :cry:


For now, I'm simply using the BG1 proficiencies (i.e. small sword and such) since they are very easy to modify and actually work properly even in BG2. However, for proficiencies which did not exist in BG1 (like Two Weapon Style) this workaround obviously won't cut it. So, I'd be much obliged if someone someone could explain to me (preferably in a "step-by-step for dummies" fashion :D) what I need to do in order to assign 3 points in TWS to a character who previously had no BG2 proficiency points at all. It would be best if you could demonstrate the necessary code on ARLED.CRE since that's the character which I'm modifying ATM. ;)

Link to comment

You need to add effects, it is not too easy with weidu.

Someone could make a diff tool which creates a series of weidu commands to update one file to another.


You could use dltcep (or ni) to add the necessary effects and by running the tool you would get the weidu commands.

Link to comment

change the parts in bold.

READ_LONG 0x2b8 sloto

READ_LONG 0x2bc itemo

READ_LONG 0x2c4 effecto

READ_LONG 0x2c8 effectn

SET beffectn = effectn


SET effectgain = 1 // substitute with actual number of effects to add.


// repeat & edit the following three lines for every proficiency you want to add.

INSERT_FILE effecto ~path/to/mod/generic_weapprof.eff~ // substitute with path to file.

WRITE_LONG effecto + 0x14 3 // number of stars

WRITE_LONG effecto + 0x18 101 // the proficiency code, as per stats.ids.


WRITE_LONG 0x2c8 beffectn + effectgain


SET sloto = sloto } (effectgain * 0x108)

WRITE_LONG 0x2b8 sloto


SET itemo = itemo + (effectgain * 0x108)

WRITE_LONG 0x2bc itemo


I'd attach path/to/mod/generic_weapprof.eff if I were able. You can export it from a working .cre using an hexadecimal editor, or here's an uuencoded version:

begin-base64 644 path/to/modgeneric_weapprof.eff

To decode the UUENCODEd version via GUI, try http://www.seekfordsolutions.com/Products/.../EncoderWizard/. I cannot guarantee for it.


EDIT: CD's solution down here is better (no dependancy on external files), but I like simple tp2s and external logic :cry:

Link to comment

Between Fixpack and Tweaks, you can probably find every type of patching known to man. Here's a template you can use for inserting new proficiencies:


COPY_EXISTING ~foo.cre~ ~override~
 SET "new_fx" = 1 // adjust as needed
 READ_BYTE   0x33 "fx_type"
 READ_LONG  0x2b0 "known_off"
 READ_LONG  0x2b8 "slot_off"
 READ_LONG  0x2bc "itm_off"
 READ_LONG  0x2c4 "fx_off"
 READ_LONG  0x2c8 "fx_num"
 WRITE_LONG 0x2c8 ("%fx_num%" + "%new_fx%")
 SET "offset" = ((0x30 + (0xd8 * "%fx_type%")) * "%new_fx%")
 PATCH_IF ("%known_off%" >= "%fx_off%") BEGIN
WRITE_LONG 0x2b0 ("%known_off%" + "%offset%")
 PATCH_IF ("%slot_off%" >= "%fx_off%") BEGIN
WRITE_LONG 0x2b8 ("%slot_off%" + "%offset%")
 PATCH_IF ("%itm_off%" >= "%fx_off%") BEGIN
WRITE_LONG 0x2bc ("%itm_off%" + "%offset%")
 // repeat this block for every new prof
 INSERT_BYTES   ("%fx_off%"							  ) (0x30 + (0xd8 * "%fx_type%"))
WRITE_SHORT  ("%fx_off%"		+ (0x08 * "%fx_type%")) 233 // opcode: prof
WRITE_LONG   ("%fx_off%" + 0x04 + (0x10 * "%fx_type%"))   1 // number of stars
WRITE_LONG   ("%fx_off%" + 0x08 + (0x10 * "%fx_type%"))  90 // proficiency
WRITE_LONG   ("%fx_off%" + 0x0c + (0x10 * "%fx_type%"))   9 // instant/permanent
WRITE_LONG   ("%fx_off%" + 0x12 + (0x18 * "%fx_type%")) 100 // probability


If you just want to modify existing ones, this can be useful. Changing the PATCH_IF conditions is the fun bit.


COPY_EXISTING ~bar.cre~  ~override~
 READ_BYTE  0x33 "fx_type" ELSE 2
 READ_LONG 0x2c4 "fx_off" ELSE 0
 READ_LONG 0x2c8 "fx_num" ELSE 0
 FOR (index = 0; index < fx_num; index = index + 1) BEGIN
READ_SHORT  ("%fx_off%"		+ (0x08 * "%fx_type%") + ("%index%" * (0x30 + (0xd8 * "%fx_type%")))) "opcode"
READ_LONG   ("%fx_off%" + 0x04 + (0x10 * "%fx_type%") + ("%index%" * (0x30 + (0xd8 * "%fx_type%")))) "stars"
READ_LONG   ("%fx_off%" + 0x08 + (0x10 * "%fx_type%") + ("%index%" * (0x30 + (0xd8 * "%fx_type%")))) "prof"
PATCH_IF (("%opcode%" = 233) AND ("%prof%" = 90%") AND ("%stars%" = 1)) BEGIN // if one star in longswords
  WRITE_LONG   ("%fx_off%" + 0x04 + (0x10 * "%fx_type%") + ("%index%" * (0x30 + (0xd8 * "%fx_type%")))) 2 // then make it two

Link to comment

Here is the way I do it for addition of one effect, using ARLED.CRE as an example. Re-iteratively, you might want to use something more simpler than repeating the code over and over.



//Declare constants
SET FX_BLOCK_SIZE = 0x108 //the size of an effect block
SET FX_MODIFY_PROFICIENCIES = 233 //modify proficiencies effect code
SET STATS_PROFICIENCYTWOHANDEDSWORD = 93 //stats code for THS proficiency

//Read in all block offsets
READ_LONG 0x2A0 known_spl_off
READ_LONG 0x2A8 mem_info_off
READ_LONG 0x2B0 mem_spl_off
READ_LONG 0x2B8 item_slot_off
READ_LONG 0x2BC item_off
READ_LONG 0x2C4 fx_off

//Read in all block numbeers
READ_LONG 0x2A4 known_spl_num
READ_LONG 0x2AC mem_info_num
READ_LONG 0x2B4 mem_spl_num
READ_LONG 0x2C0 item_num
READ_LONG 0x2C8 fx_num

//Add an effect block

//Populate the effect block with particulars
WRITE_LONG (%fx_off% + 0x8) %FX_MODIFY_PROFICIENCIES% //Effect type
WRITE_LONG (%fx_off% + 0x14) 3 //Parameter 1 [#Stars]
WRITE_LONG (%fx_off% + 0x18) %STATS_PROFICIENCYTWOHANDEDSWORD% //Parameter 2 [STATS.IDS proficiency code]
WRITE_SHORT (%fx_off% + 0x24) 100 //Probability 1

//Based on IESDP EFF struct for identification of fields, you can adjust any of these 
/*WRITE_LONG (%fx_off% + 0x8) type
WRITE_LONG (%fx_off% + 0xC) target
WRITE_LONG (%fx_off% + 0x10) power
WRITE_LONG (%fx_off% + 0x14) param1
WRITE_LONG (%fx_off% + 0x18) param2
WRITE_LONG (%fx_off% + 0x1C) timing mode
WRITE_LONG (%fx_off% + 0x20) duration
WRITE_SHORT (%fx_off% + 0x24) prob1
WRITE_SHORT (%fx_off% + 0x26) prob2
WRITE_ASCII (%fx_off% + 0x28) resource
WRITE_LONG (%fx_off% + 0x30) #dice
WRITE_LONG (%fx_off% + 0x34) dicesize
WRITE_LONG (%fx_off% + 0x38) saving type
WRITE_LONG (%fx_off% + 0x3C) save bonus
WRITE_LONG (%fx_off% + 0x40) variable?

WRITE_LONG (%fx_off% + 0x44) school
WRITE_LONG (%fx_off% + 0x48) unknown
WRITE_LONG (%fx_off% + 0x4C) lowestaffectedlvl
WRITE_LONG (%fx_off% + 0x50) highestaffectedlevel
WRITE_LONG (%fx_off% + 0x54) resistancetype
WRITE_BYTE (%fx_off% + 0x58) param3
WRITE_BYTE (%fx_off% + 0x59) param4
WRITE_SHORT (%fx_off% + 0x5A) unk
WRITE_LONG (%fx_off% + 0x5C) unk
WRITE_LONG (%fx_off% + 0x60) unk
WRITE_LONG (%fx_off% + 0x64) unk
WRITE_ASCII (%fx_off% + 0x68) vvc resource
WRITE_ASCII (%fx_off% + 0x70) res2
WRITE_LONG (%fx_off% + 0x78) caster xpos
WRITE_LONG (%fx_off% + 0x7C) caster ypos
WRITE_LONG (%fx_off% + 0x80) target xpos
WRITE_LONG (%fx_off% + 0x84) target ypos
WRITE_LONG (%fx_off% + 0x88) unk
WRITE_ASCII (%fx_off% + 0x8C) effect source
WRITE_LONG (%fx_off% + 0x94) unk
WRITE_LONG (%fx_off% + 0x98) unk
WRITE_LONG (%fx_off% + 0x9C) unk
WRITE_ASCII (%fx_off% + 0xA0) variable (32)
WRITE_SHORT (%fx_off% + 0xC0) caster level
WRITE_SHORT (%fx_off% + 0xC2) unk
WRITE_LONG (%fx_off% + 0xC4) unk
WRITE_LONG (%fx_off% + 0xC8) secondary type
WRITE_LONG (%fx_off% + 0xCC) unk
WRITE_LONG (%fx_off% + 0xD0) unk
WRITE_LONG (%fx_off% + 0xD4) unk

WRITE_LONG (%fx_off% + 0xD8) unk
WRITE_LONG (%fx_off% + 0xDC) unk
WRITE_LONG (%fx_off% + 0xE0) unk
WRITE_LONG (%fx_off% + 0xE4) unk

WRITE_LONG (%fx_off% + 0xE8) unk
WRITE_LONG (%fx_off% + 0xEC) unk
WRITE_LONG (%fx_off% + 0xF0) unk
WRITE_LONG (%fx_off% + 0xF4) unk

WRITE_LONG (%fx_off% + 0xF8) unk
WRITE_LONG (%fx_off% + 0xFC) unk
WRITE_LONG (%fx_off% + 0x100) unk
WRITE_LONG (%fx_off% + 0x104) unk*/

//Set the effect flag - if effect flag is 0 and CRE has an effect, game crashes
WRITE_BYTE 0x33 1 //Effect flag

//Increment the number of effects
SET fx_num += 1
WRITE_LONG 0x2C8 %fx_num%

//Check and update block offsets of other data structures
PATCH_IF (%known_spl_off% > %fx_off%) ||
	 (%known_spl_off% = %fx_off% && %known_spl_num% != 0) BEGIN
SET %known_spl_off% = %known_spl_off% + %FX_BLOCK_SIZE%
WRITE_LONG 0x2A0 %known_spl_off%
PATCH_IF (%mem_info_off% > %fx_off%) ||
	 (%mem_info_off% = %fx_off% && %mem_info_num% != 0) BEGIN
SET %mem_info_off% = %mem_info_off% + %FX_BLOCK_SIZE%
WRITE_LONG 0x2A8 %mem_info_off%
PATCH_IF (%mem_spl_off% > %fx_off%) ||
	 (%mem_spl_off% = %fx_off% && %mem_spl_num% != 0) BEGIN
SET %mem_spl_off% = %mem_spl_off% + %FX_BLOCK_SIZE%
WRITE_LONG 0x2B0 %mem_spl_off%
PATCH_IF (%item_slot_off% > %fx_off%) || (%item_slot_off% = %fx_off%) BEGIN
SET %item_slot_off% = %item_slot_off% + %FX_BLOCK_SIZE%
WRITE_LONG 0x2B8 %item_slot_off%
PATCH_IF (%item_off% > %fx_off%) ||
	 (%item_off% = %fx_off% && %item_num% != 0) BEGIN
SET %item_off% = %item_off% + %FX_BLOCK_SIZE%
WRITE_LONG 0x2BC %item_off%

Link to comment

Thanks guys! :cry: I've got two more questions:


1) I assume that this code (PATCH_IF...) checks whether the creature already has any proficiency points in the category which I intend to change, is that correct? For example, I wouldn't want to add my 3 points over someone else's 2 points and thus create an invalid value of 5 stars in TWS.


2) Do BG1 proficiency points stack or merely overlap with BG2 proficiencies? I'm asking this because many creatures already have a few points in certain BG1 categories (i.e. small sword). So, I was wondering if I add 2 points in short sword (BG2 proficiency) to a creature who already has 1 point in small sword (BG1 proficiency) would both stack and thus grant the creature 3 points in short swords (i.e. rank of mastery) or would the proficiencies merely overlap with the highest value always taking precedence?

Link to comment

Proficiencies don't stack; a creature with two stars in two-weapon fighting, three stars in two-weapon fighting, and one star in two-weapon fighting has three stars in two-weapon fighting. BG1 proficiencies also overlap, with the engine selecting the best value.


If you just want to avoid the (small) chance of leaving redundant cruft in the .cres that you're patching, you could try something like:

COPY_EXISTING ~arled.cre~ ~override~
READ_LONG  0x02c8  "ec"
READ_LONG  0x02c4  "eo"
FOR ("i1" = "eo"; "i1" < ("eo" + (0x0108 * "ec")); "i1" += 0x0108) BEGIN
  READ_LONG	("i1" + 0x08) "op"
  READ_LONG	("i1" + 0x14) "p1"
  READ_LONG	("i1" + 0x08) "p2"
  PATCH_IF		  (("op" = 0xe9) AND ("p1 != 0x03) AND ("p2" = 0x72)) THEN BEGIN
	SET "p1" = 0x03
	WRITE_LONG ("i1" + 0x14) "p1"
	SET "i1" = ("eo" + (0x0108 * "ec"))
  END ELSE PATCH_IF (("op" = 0xe9) AND ("p1  = 0x03) AND ("p2" = 0x72)) THEN BEGIN
	SET "i1" = ("eo" + (0x0108 * "ec"))
PATCH_IF !(("op" = 0xe9) AND ("p1  = 0x03) AND ("p2" = 0x72)) THEN BEGIN
  WRITE_LONG 0x02c8 ("ec" + 0x01)
  INSERT_BYTES ("eo" + 0x00) 0x0108
  WRITE_LONG   ("eo" + 0x08) 0xe9
  WRITE_LONG   ("eo" + 0x0c) 0x01
  WRITE_LONG   ("eo" + 0x14) 0x03
  WRITE_LONG   ("eo" + 0x18) 0x72
  WRITE_BYTE   ("eo" + 0x1c) 0x09
  WRITE_SHORT  ("eo" + 0x24) 0x64
  FOR ("i1" = ("eo" + 0x78); "i1" < ("eo" + 0x84); "i1" += 0x04) BEGIN
	WRITE_LONG "i1" (` 0x00)
  PATCH_FOR_EACH "o1" IN 0x02a0 0x02a8 0x02b0 0x02b8 0x02bc BEGIN
	READ_LONG  "o1" "o2"
	WRITE_LONG "o1" ("o2" >= "eo" ? ("o2" + 0x0108) : "o2")

Link to comment

Thanks for the clarification, Nythrun. :cry:


A few more questions, how do I patch a .STO file so that it accepts fenced goods and so that thieves are prohibited from stealing from it? Each of these should be within a separate code block, if that is possible, since I'd like to turn several regular merchants into fences and prohibit stealing from some other merchants who are already marked as fences.


EDIT - ideally, I'd like to do this in two passes. First, I'd designate a few regular stores as fences, and I assume this can be done en mass for several .STO files ar once. After that is done, I'd like to copy all .STO files in the game, somehow check which ones have the fence flag, and then only mark those as "impossible to steal from". I'm guessing that BUT_ONLY_IF_IT_CHANGES can be used to accomplish this but I'm not sure how to do the base patching i.e. which WeiDU command to use. Could BAND or BXOR be the one I'm looking for, per chance?

Link to comment

You'll be wanting BOR to add set a flag, and BAND to unset a flag.


Erm, I'll explain this better (at all!) when I get a bit more time, if someone more sensible hasn't already done so.

COPY_EXISTING ~StoreThatShouldFence1.sto~ ~override~
		  ~StoreThatShouldFence2.sto~ ~override~
READ_LONG  0x10 "flags"
WRITE_LONG 0x10 ("flags" | 0x1000)

COPY_EXISTING_REGEXP GLOB ~^.+\.sto$~ ~override~
READ_LONG 0x08 "type"
PATCH_IF (("type" = 0x00) OR ("type" = 0x01) OR ("type" = 0x02)) THEN BEGIN
  READ_LONG  0x10 "flags"
  WRITE_LONG 0x10 (("flags" & 0x1000) != 0x00 ? ("flags" & (` 0x8)) : "flags")

Link to comment

Heh, I've figured it out in the meanwhile by checking some of the fixpack code for adding and removing area flags. :cry: Also, in the end, I've decided against patching mod introduced fences, so I'm not going to patch all stores after all. This is my current code:


COPY_EXISTING ~ARLED.STO~ ~override~	 // Arledrian's store (fence on the top floor of Gaelan Bayle's house)
		  ~BMTHIEF.STO~ ~override~   // Marina's store (Black Market Thief on the main floor of the Shadow Thief guild)
		  ~SLSHOP01.STO~ ~override~  // halfling male thief merchant in the Slums
		  ~SLSHOP02.STO~ ~override~  // half-orc female thief merchant in the Slums
		  ~ROGER.STO~ ~override~	 // Roger the fence (human male thief merchant in the sewers)
		  ~BSHOP01.STO~ ~override~   // Cutpurse (halfling male thief merchant in The Bridge District)
		  ~DMARK.STO~ ~override~	 // Fovem (human male thief merchant in the Docks District, appears at night only)
		  ~GORCH.STO~ ~override~	 // Gorch (human male merchant on the main floor of MaeVar's Guild)
		  ~SHTHSTOR.STO~ ~override~  // Rattell (Shadow Thief merchant on the main floor the thief stronghold)
		  ~AMSMUG01.STO~ ~override~  // Carras' regular store (Amkathran smuggler in ToB)
		  ~AMSMUG02.STO~ ~override~  // Carras' discount store (Amkathran smuggler in ToB)
 READ_BYTE 0x10 "flags"
 WRITE_LONG 0x10 ("%flags%" BOR 0b1000000000000)  // allow buying stolen goods
 WRITE_BYTE 0x10 ("%flags%" BAND 0b11110111) // disallow stealing from fences


As far as I can see, there seems to be no harm done in flagging a store already marked as fence once again as such (i.e. ROGER.STO) as long as I use BUT_ONLY_IF_IT_CHANGES. Is that correct?

Link to comment

It's harmless, yes :cry:


BUT_ONLY will stop WeiDU from making a backup of the file and writing a new copy to the override if that would not do anything. It's more of a clutter reducer than it is harm prevention.


You're alternating between treating the flags as a dword and as a byte, though, so you'll be wanting READ_LONG for the flags, in case something else in the second byte is set. It'll probably work as written, but something like

READ_LONG  0x10 "flags"
 SET "flags" |= 0x1000   // allow buying stolen goods
 SET "flags" &= (` 0x08) // disallow stealing from fences
 WRITE_LONG 0x10 "flags"

is safer.

Link to comment


This topic is now archived and is closed to further replies.

  • Create New...