Jump to content

Explaining Creature Patching by .tp2: An introduction


cmorgan

Recommended Posts

[Clipped out of a discussion of rebuilding creature files for Level 1 NPCs - cmorgan]

 

[The original question can be simplified to "how do you figure out what a .cre has, and change it to something else?"]

 

Nythrun Oct 3 2006, 07:27 AM

 

No matter how it's done there'll be some risks. :D I'm going to talk through a few of them step by step - please don't think I'm being condescending, there's bound to be someone reading who could benefit from this approach, even if you know every bit of it already (and you probably do).

 

Since Valygar has been such a good stoic about the teasing, we'll pick on Minsc for awhile (who needs our help, or a doctor, or something). If each Minsc creature is listed individually

COPY_EXISTING ~minsc7.cre~   ~override~
		  ~minsc8.cre~   ~override~
		  ~minsc9.cre~   ~override~
		  ~minsc10.cre~  ~override~
		  ~minsc12.cre~  ~override~
		  ~minsc14.cre~  ~override~

then we'll run into problems when FlimFandang0 releases his "Start BG2 at level three and cap out at level nine because high levels make no sense" mod, because his minsc3.cre isn't on the list until we add it there (along with an ALLOW_MISSING at the start of the .tp2). And just doing a COPY_EXISTING_REGEXP ~minsc[0-9]\.cre~ sort of thing causes problems when we accidently destroy the spellbooks of Marcie Ingles's New Super Clerics from her highly acclaimed "Mighty priests rough you up and take your lunch money" mod. So instead, let's steal borrow some code from CamDawg, where we copy every creature and only patch them if the name matches "Minsc" - and then incorporate an additional check to make sure the death variable matches. Checking IESDP, we find that a creature's full name is stored at 0x8 and is four bytes long, and that its tooltip name is stored at 0xc and is also four bytes long, and that its death variable is stored at 0x280 and is thirty-two bytes long. Now we can be maximally finicky about patching all Minsc all the time, but only Minsc.

COPY_EXISTING_REGEXP GLOB ~^.+\.cre$~   ~override~
 READ_LONG  0x0008 "ln"		ELSE 0  // we're reading the long name here
 READ_ASCII 0x0280 "dv"   (0x20) ELSE 0  // and reading the death variable here
 PATCH_IF   (("%ln%"=9501) OR ("%ln%"=9482))   BEGIN // these are the two "Minsc" strrefs in Dialog.tlk 
PATCH_IF ("%dv%" STRING_COMPARE_CASE "MINSC"=0) BEGIN // and here's the death variable check
END // PATCH_IF death variable check
 END // PATCH_IF long name match
BUT_ONLY_IF_IT_CHANGES // do not litter the backup directory please

Unfortunately, this code isn't doing anything useful yet, so let's start small and decide how to patch the known spells area of the creature for now.

 

REMOVE_KNOWN_SPELL is definately an option, and it's easy to write

REMOVE_KNOWN_SPELL ~sppr101~
			   ~sppr103~
			   ~sppr104~

where we list by hand everything in the ranger spellbook. But what if some other mod made Minsc a Blade, and we have to get rid of arcane spells and some innates? What if The Darkest Day is installed and a dozen new ranger spells have been added? There are a lot of possible variations on what spells Minsc could know, and any variation that isn't accounted for that a user has installed before a rekit mod is a potential game crasher. So the surgical removal method could work, but it's unwieldy. Perhaps the tabula rasa is a better choice, especially as there's no need to worry about mod conflicts at the moment and we can handle everything with READing, WRITEing, and basic arithmetic...

 

Referring back to the IEDSP, we find 0x02a0 is listed as "Known Spells Offset". Because the list of known spells in a .cre doesn't always start in the same place, there has to be a marker somewhere in the file that lets the game know where to seek this information. Now that we can find the marker, we can tell WeiDU to go find the list for us with a

READ_LONG 0x2a0 "known_spells_offset"

. Of course, this only states where the list begins, and doesn't say where the list ends - and there's no "Known Spells End Here" offset. This matters because we only want to erase the spells known and not a bunch of the rest of the file too. Which means we'll have to figure out a way to make WeiDU find the file size for us. Fortunately that's easy :)

The length of the Spells Known array can either be always the same, or vary based on something. Checking IEDSP again, there's no listing for a fixed table size. What there is, however, is helpful description about the Spells Known array - specifically, that each spell that's known has eight bytes devoted to its resource reference, two devoted to its spell level, and two devoted to its type. Because that's all there is to a Spells Known array, we know that the array has to be (twelve bytes multiplied by number of spells known) in size. The number of spells know isn't something we've found yet - but fortunately IEDSP lists 0x02a4 as "known spells count", exactly what we need.

READ_LONG	0x02a0  "known_spells_offset"
READ_LONG	0x02a4  "known_spells_count"
DELETE_BYTES "%known_spells_offset%"   (12*"%known_spells_count%")

And we're done, right?

 

Not quite; not only is there a Known Spells Offset, there are offsets to several other parts of the file too. Because we've just removed a chunk of the landscape, these maps may not be accurate anymore. Whoops. This, too, however is easy to fix :) We'll just have to READ all of these other offsets (there are five) and adjust them if they need adjusting. If these offsets are less than the value of Known Spells array offset, it means they refer to an earlier part of the .cre than the part we just excised, and if these offsets are greater than the value of the Known Spells array offset, then something in between has changed and we need to patch them. Hmm, if...patch - sounds like PATCH_IF to me :)

READ_LONG	0x02a0	kso
READ_LONG	0x02a4	ksc
READ_LONG	0x02a8	smo
READ_LONG	0x02b0	mso
READ_LONG	0x02b8	iso
READ_LONG	0x02bc	ilo
READ_LONG	0x02c4	elo
DELETE_BYTES kso	   (0x0c*ksc)
PATCH_IF	 (smo>kso) BEGIN
 WRITE_LONG 0x02a8	(smo-(0x0c*ksc))
END
PATCH_IF	 (mso>kso) BEGIN
 WRITE_LONG 0x02b0	(mso-(0x0c*ksc))
END
PATCH_IF	 (iso>kso) BEGIN
 WRITE_LONG 0x02b8	(iso-(0x0c*ksc))
END
PATCH_IF	 (ilo>kso) BEGIN
 WRITE_LONG 0x02bc	(ilo-(0x0c*ksc))
END
PATCH_IF	 (elo>kso) BEGIN
 WRITE_LONG 0x02c4	(elo-(0x0c*ksc))
END

I like short variable names (ksc rather than known_spells_offset) because the long ones confuse me - you don't have to use them. I like having "12" written as "0x0c" because changing number base in the same line looks ugly to me - you don't have to do that either. And I like having everything in neat columns because I have obsessive compulsive disorder because I make fewer mistakes that way - you definately don't have to do that.

 

Naturally, there are trade-offs involved in this method too - Minsc has lost his punch-drunk rage (which we'll probably want to add back with an ADD_KNOWN_SPELL later). And if you have Nythrun's mod that grants Minsc the "Summon Cricetinae Horde" innate (which allows Minsc to summon a legion of hampsters to eat Goodberrys and stand around looking fuzzy) then that's gone too, and you can do a file check to restore it if you're feeling silly. Much like the REMOVE_KNOWN_SPELL option we mentioned way back, there's going to be some manual touching up needed to ensure total compatablity. The difference here is, we don't have to add any specifics and the ones we do include are there only because we want them.

 

And now we're done :)

 

cmorgan Oct 5 2006, 11:07 AM

 

If you guys can give me a day or so, I am still absorbing Nythrun's cool tutorial post, and I may have a simple way of combining this with the patching stuff reducing the .cre load to just one new .cre per NPC per class, as per Macready/Nythrun -- just gotta think and poke through IESDP more.

 

Quick question:

 PATCH_IF   (("%ln%"=9501) OR ("%ln%"=9482))   BEGIN // these are the two "Minsc" strrefs in Dialog.tlk

 

OK, this gets more tricky -- are those strrefs stable over different versions, say Tutu/EasyTutu_SOA, EasyTutu_TOB, BGT, BG2, etc? Would it be strong enough a ckeck to only use DV (which means special treatment for BGT, as they have some versions of Imoen, for example, which use imoen2, etc).

 

Given that, how about we create a base set of "incomplete" .cres for each NPC, copied to override based on version, patch them with the existing offsets a-la Macready, then use the tph patching to assign a kit fleshing out the cre (using TRUECLASS as the default FORCED_SUBCOMPONENT)

 

Trying to figure out how to avoid creating all of the .cres Nythrun mentioned, plus the duplicate set for Tutu, plus the duplicate set for BGT all at once.

 

erik Oct 5 2006, 02:53 PM

 

Use READ_STRREF on the name strref you've already read out of the .cre and compare the string to "Minsc". That's foolproof, once you get it working. Can't remember where I saw the working example, though. Easy enough to sketch a method, though:

 

(insert copy-all-cre-files-regexp here)
 READ_STRREF name "your_variable_here"
 PATCH_IF "%your_variable_here%" STRING_EQUAL "Minsc"

 

et voila! (untested, but you get the idea)

 

-erik

Link to comment

Archived

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

×
×
  • Create New...