Jump to content


#GothemKnight

Member Since 09 Jun 2013
Offline Last Active Sep 22 2013 09:59 PM

Posts I've Made

In Topic: How to change colours

24 June 2013 - 07:07 PM

I'm writing this by request to show how to change NPC colours. Actually, it will work for any creatures that have palettable animations (like humans, elves and hobgoblins). It won't affect non-palettable animations (like orcs and lizardmen). Most typically, this shows how to make sure NPC avatars and paperdolls match mods that give them different portraits. It also demonstrates some basic WeiDU modding fundamentals, though there are other tutorials for that (particularly in the WeiDU documentation).

First, some basics. This is a typical header for a mod's .tp2 installer. This text file is called something like setup-mymod.tp2 or just mymod.tp2. It is executed by a copy of WeiDU.exe renamed to the .tp2 name (setup-mymod.exe).


BACKUP ~adjports/backup~ //Where the mod backs up files
AUTHOR ~www.shsforums.net/forum/584-portrait-mods/~ //Where to submit error reports
VERSION ~v1~
README ~adjports/adjports.html~
BACKUP and AUTHOR are mandatory. VERSION is optional but recommended. It prints the version number in the WeiDU.log file that keeps track of all mods installed. README will ask the user whether to view the readme.

There is also a subheader for each component. Some mods will have only one component; others will have many. In simple form, a component for changing an NPC's colours would look something like this:
BEGIN ~My Portrait Mod~
DESIGNATED 100

ACTION_FOR_EACH npc IN ~aerbod01~ ~aerie12~ ~cwsaer~ BEGIN
  ACTION_IF FILE_EXISTS_IN_GAME ~%npc%.cre~ BEGIN
    COPY_EXISTING ~%npc%.cre~ ~override~ //Aerie
	  WRITE_BYTE 0x2c 67 //Metal color (shiny gold, was 27 gray)
	  WRITE_BYTE 0x2d 47 //Minor color (pure dark red, was 41 dark brown)
	  WRITE_BYTE 0x2e 2 //Major color (dark gold, was 37 dark dirty yellow)
	  WRITE_BYTE 0x2f 12 //Skin color (light carnation pink)
	  WRITE_BYTE 0x30 16 //Leather color (silver gold, was 93 dark cement gray)
	  WRITE_BYTE 0x31 30 //Armor color (light iron gray)
	  WRITE_BYTE 0x32 115 //Hair color (sunkissed, was 3 light gold)
	BUT_ONLY_IF_IT_CHANGES
  END
END
BEGIN specifies the component name when installing; it is mandatory. DESIGNATED is optional but highly recommended - it hardcodes a component number as appears in the WeiDU.log. It also allows other mods to detect the component and automated programs (such as the BiG World Project) to install the component. If you don't use DESIGNATED, your component numbers will run consecutively from zero. Thus, they will change as you add and remove new components, which isn't a good thing for automation.

The main block is a loop consisting of ACTION_FOR_EACH, ACTION_IF FILE_EXISTS_IN_GAME and COPY_EXISTING. In layman's terms, this feeds a list of filenames to WeiDU (ACTION_FOR_EACH) and says: if each file exists in the game (ACTION_IF ...) then copy it to the override folder (COPY_EXISTING) and make some changes. As for those changes, we are making a series of one-byte changes to the file (WRITE_BYTE). We use an editor (either Near Infinity or DLTCEP) to look up the existing values if we want, the IESDP to find the offsets and the desired colour gradients and their numeric values. Once that is done, we simply plug all that into our patch.

BUT_ONLY_IF_IT_CHANGES (BUT_ONLY for short) says only to copy the file if it is modified by the patches. If, for example, it already has the colour values specified, it will simply be skipped. Two ENDs close out our ACTION loops. That is all you need to do to change an NPC's colours in its simplest form. Like other mods, this will not change any NPC who has already joined the party, as they are stored in the save game.

Here is a slightly more complex example. This component gives two choices for changing Aerie's garb, thus uses SUBCOMPONENT.
BEGIN ~Aerdrie's Garb~
SUBCOMPONENT ~Aerie as Priestess of Aerdrie/Baervan~
DESIGNATED 50
REQUIRE_PREDICATE ((NOT FILE_EXISTS_IN_GAME ~_plat06.itm~) AND ((FILE_EXISTS_IN_GAME ~aerie12.cre~) OR (FILE_EXISTS_IN_GAME ~cwsaer.cre~))) ~Aerie not available~
In this case, SUBCOMPONENT actually indicates the component name, and BEGIN indicates one of several choices the user has. Other choices will follow with their own BEGIN/SUBCOMPONENT sections.

REQUIRE_PREDICATE is a good way to ensure the NPC is present. If the conditions aren't fulfilled, the installer will skip the component and go to the next one. In this case, we want to skip the component if Tutu. Aerie is present but not joinable on Tutu (all BG2 files are). Therefore, we give it a Tutu item _plat06.itm and NOT FILE_EXISTS_IN_GAME for the condition. With the AND we also make sure at least version of Aerie is present (either aerie12 or cwsaer) and pair those with an OR. The whole condition looks like this in simpler terms: (NOT Tutu) AND (aerie12 OR cwsaer). This is followed by a text message (~Aerie not available~) that will display when the installer skips the component.
COPY ~adjports/bmp/t-aeri1l.bmp~ ~override~
	 ~adjports/bmp/t-aeri1m.bmp~ ~override~
	 ~adjports/bmp/t-aeri1s.bmp~ ~override~
The section above copies the portraits for this component. Most people don't do it this way - they overwrite the existing portraits. This demonstrates a way of avoiding that to keep the previous portraits available for other purposes.
ACTION_FOR_EACH tda IN ~aeriend1~ ~aeriend2~ ~aerifnd1~ BEGIN
  ACTION_IF FILE_EXISTS_IN_GAME ~%tda%.2da~ BEGIN
	COPY_EXISTING ~%tda%.2da~ ~override~ //ToB epilogue tables
	  SET_2DA_ENTRY 1 0 1 ~*T-AERI1L~
	BUT_ONLY
  END
END
That section replaces Aerie's Throne of Bhaal epilogue portrait in the relevant tables. It is only relevant for ToB NPCs, and then only if you don't overwrite the existing portrait.
COPY_EXISTING ~spin745.spl~ ~override~ //Aerie Portrait Spell
  PATCH_IF SOURCE_SIZE > 0x71 BEGIN
	READ_LONG 0x64 hf //Extended header offset
	READ_SHORT 0x68 hc //Extended header count
	READ_LONG 0x6a fb //Feature block offset
	FOR (i1 = 0; i1 < hc; i1 += 1) BEGIN //Cycle through extended headers
	  READ_SHORT (0x28 * i1 + hf + 0x1e) fc //Feature count
	  READ_SHORT (0x28 * i1 + hf + 0x20) fs //Feature offset
	  FOR (i2 = fs; i2 < (fs + fc); i2 += 1) BEGIN //Cycle through ability effects
		READ_SHORT (i2 * 0x30 + fs) opcode
		READ_LONG (i2 * 0x30 + fs + <img src='http://www.shsforums.net/public/style_emoticons/<#EMO_DIR#>/cool.png' class='bbc_emoticon' alt='8)' /> param2
		PATCH_IF (opcode = 107) AND (param2 = 0) BEGIN //If small portrait
		  WRITE_ASCII (i2 * 0x30 + fs + 0x14) ~t-aeri1s~ #8
		END
		PATCH_IF (opcode = 107) AND (param2 = 1) BEGIN //If large portrait
		  WRITE_ASCII (i2 * 0x30 + fs + 0x14) ~t-aeri1m~ #8
		END
	  END
	END
  END
BUT_ONLY
The above section is relevant only to Aerie, as there is a spell effect that morphs her back into avariel form after being an ogre. It specifies her small and medium portraits. I won't detail the code, but it basically loops through all effects attached to the spell and changes those where it finds portraits.
ACTION_FOR_EACH npc IN ~aerbod01~ ~aerie12~ ~cwsaer~ BEGIN
  ACTION_IF FILE_EXISTS_IN_GAME ~%npc%.cre~ BEGIN
	COPY_EXISTING ~%npc%.cre~ ~override~ //Aerie
	  PATCH_IF SOURCE_SIZE > 0x2d3 BEGIN //Ensures valid .cre file size
		WRITE_BYTE 0x2c 67 //Metal color (shiny gold, was 27 gray)
		WRITE_BYTE 0x2d 47 //Minor color (pure dark red, was 41 dark brown)
		WRITE_BYTE 0x2e 2 //Major color (dark gold, was 37 dark dirty yellow)
		WRITE_BYTE 0x2f 12 //Skin color (light carnation pink)
		WRITE_BYTE 0x30 16 //Leather color (silver gold, was 93 dark cement gray)
		WRITE_BYTE 0x31 30 //Armor color (light iron gray)
		WRITE_BYTE 0x32 115 //Hair color (sunkissed, was 3 light gold)
	  END
	BUT_ONLY_IF_IT_CHANGES
  END
END
Finally, the code above changes her colours as in the first example. For the second subcomponent, we'd copy and paste the same code above and just change the BEGIN name (e.g. to ~Baervan's Garb~), increase the DESIGNATED component number (e.g. to 60), change all the portraits we're copying (e.g. from t-aeri1 to t-aeri2) and finally, change the colours in the second set of code to match her second portrait, following the techique above (i.e. look them up in the IESDP).

Here's the full code for a slightly simpler example. It provides two different examples for Branwen (based on portraits with either brown or purple robes).

There's no ToB Branwen nor morphing spell for her, so we just need to copy the relevant portraits and assign them and the appropriate colours on her .cre files. We do, however, have to check via REQUIRE_PREDICATE that Branwen is present, so we look for Tutu (_branwe5.cre), BGT (branwe5.cre) and at least 4 different mod versions. Apparently, Branwen is a popular gal - you could in theory have a party with you and 5 other Branwens! Ah, the joys of redundant mods, er, I mean user choices.
BEGIN ~Brown Robe~
SUBCOMPONENT ~Branwen as Priestess of Tempus~
DESIGNATED 100
REQUIRE_PREDICATE ((FILE_EXISTS_IN_GAME ~_branwe5.cre~) OR (FILE_EXISTS_IN_GAME ~branwe5.cre~) OR (FILE_EXISTS_IN_GAME ~cb3513bw.cre~) OR (FILE_EXISTS_IN_GAME ~dl#bwn.cre~) OR (FILE_EXISTS_IN_GAME ~ttbran.cre~) OR (FILE_EXISTS_IN_GAME ~wlbran0.cre~)) ~Branwen not available~

COPY ~adjports/bmp/t-bran1l.bmp~ ~override~
	 ~adjports/bmp/t-bran1m.bmp~ ~override~
	 ~adjports/bmp/t-bran1s.bmp~ ~override~

ACTION_FOR_EACH npc IN ~_branwe~ ~_branwe5~ ~branwe~ ~branwe5~ ~cb3513bw~ ~dl#bwn~ ~ttbran~ ~wlbran0~ BEGIN
  ACTION_IF FILE_EXISTS_IN_GAME ~%npc%.cre~ BEGIN
	COPY_EXISTING ~%npc%.cre~ ~override~ //Branwen
	  PATCH_IF SOURCE_SIZE > 0x2d3 BEGINWeiDU documentation
		WRITE_BYTE 0x2c 67 //Metal color (shiny gold, was 27 gray)
		WRITE_BYTE 0x2d 47 //Minor color (pure dark red, was 41 dark brown)
		WRITE_BYTE 0x2e 2 //Major color (dark gold, was 37 dark dirty yellow)
		WRITE_BYTE 0x2f 12 //Skin color (light carnation pink)
		WRITE_BYTE 0x30 16 //Leather color (silver gold, was 93 dark cement gray)
		WRITE_BYTE 0x31 30 //Armor color (light iron gray)
		WRITE_BYTE 0x32 115 //Hair color (sunkissed, was 3 light gold)
		WRITE_ASCII 0x34 ~t-bran1s~ #8 //Small portrait
		WRITE_ASCII 0x3c ~t-bran1m~ #8 //Large portrait
	  END
	BUT_ONLY_IF_IT_CHANGES
  END
END

BEGIN ~Purple Robe~
SUBCOMPONENT ~Branwen as Priestess of Tempus~
DESIGNATED 105
REQUIRE_PREDICATE ((FILE_EXISTS_IN_GAME ~_plat06.itm~) OR (FILE_EXISTS_IN_GAME ~plat06.itm~)) ~Branwen not found~

COPY ~adjports/bmp/t-bran2l.bmp~ ~override~
	 ~adjports/bmp/t-bran2m.bmp~ ~override~
	 ~adjports/bmp/t-bran2s.bmp~ ~override~

ACTION_FOR_EACH npc IN ~_branwe~ ~_branwe5~ ~branwe~ ~branwe5~ ~cb3513bw~ ~dl#bwn~ ~ttbran~ ~wlbran0~ BEGIN
  ACTION_IF FILE_EXISTS_IN_GAME ~%npc%.cre~ BEGIN
	COPY_EXISTING ~%npc%.cre~ ~override~ //Branwen
	  PATCH_IF SOURCE_SIZE > 0x2d3 BEGIN
		WRITE_BYTE 0x2c 67 //Metal color (shiny gold, was 27 gray)
		WRITE_BYTE 0x2d 47 //Minor color (pure dark red, was 41 dark brown)
		WRITE_BYTE 0x2e 60 //Major color (dark purple, was 37 dark dirty yellow)
		WRITE_BYTE 0x2f 12 //Skin color (light carnation pink)
		WRITE_BYTE 0x30 16 //Leather color (silver gold, was 93 dark cement gray)
		WRITE_BYTE 0x31 104 //Armor color (dark chrome purple)
		WRITE_BYTE 0x32 115 //Hair color (sunkissed, was 3 light gold)
		WRITE_ASCII 0x34 ~t-bran2s~ #8 //Small portrait
		WRITE_ASCII 0x3c ~t-bran2m~ #8 //Large portrait
	  END
	BUT_ONLY_IF_IT_CHANGES
  END
END
And that's it for a simple component with two different portrait options.

Folks have also asked me how to change the colours of items. Say you've changed the hue of an NPC's special armour in your portrait and you want the item to follow suit. I won't get into how to change the icons. BAM Batcher and your favourite graphics program can do that easily enough, and there are probably other tutorials for that. This changes the colours on the paperdoll and animation or avatar when equipping the item.
BEGIN ~Ankheg Plate matches its icon~
DESIGNATED 25

ACTION_FOR_EACH ank IN ~_plat06~ ~plat06~ BEGIN
  ACTION_IF FILE_EXISTS_IN_GAME ~%ank%.itm~ BEGIN
	COPY_EXISTING ~%ank%.itm~ ~override~ //Ankheg Plate
	  PATCH_IF SOURCE_SIZE > 0x71 BEGIN //Ensures valid .itm file size
		READ_LONG 0x6a eq_off //Equipped effects offset
		READ_SHORT 0x70 eq_cnt //Equipped effects count
		FOR (i1 = 0; i1 < eq_cnt; i1 += 1) BEGIN
		  READ_SHORT (i2 * 0x30 + eq_off) opcode
		  READ_LONG (i2 * 0x30 + eq_off + <img src='http://www.shsforums.net/public/style_emoticons/<#EMO_DIR#>/cool.png' class='bbc_emoticon' alt='8)' /> param2
		  PATCH_IF (opcode = 7) AND (param2 = 5) BEGIN //If armor color
			WRITE_SHORT (i2 * 0x30 + eq_off + 4) 7 //Dark metallic green
		  END
		  PATCH_IF (opcode = 7) AND (param2 = 4) BEGIN //If strap color
			WRITE_SHORT (i2 * 0x30 + eq_off + 4) 1 //Light pure gold
		  END
		  PATCH_IF (opcode = 7) AND (param2 = 0) BEGIN //If belt color
			WRITE_SHORT (i2 * 0x30 + eq_off + 4) 36 //Light dirty yellow
		  END
		END
	  END
	BUT_ONLY
  END
END
Fairly straightforward example for the Ankheg Plate. Following the ACTION... logic described above, the code loops through each version of the item, looks for equipped effects matching certain conditions (opcode 7 for colour effects) and sets them to the relevant colours when it finds the right effects (for armour, strap and belt in this case).

What about the creature that cannot be palleted ?

like dragons or wyverns?