Jump to content


How to detect uses of custom secondary types


No replies to this topic

#1 the bigg

    The Avatar of F/Ts

  • Modders
  • 2316 posts
  • Gender:Male
  • Location:Modena (Italy)

Posted 12 November 2010 - 02:05 PM

A common trick when creating advanced spell effects is to use Secondary Types (or Schools) to allow for advanced dispelling patterns and whatnot (the details of this are besides the scope of this tutorial).

The traditional method to do this was to pick a random number for your Secondary Type (or School). This has a compatibility drawback - if another mod picked your random number, the spells of the two mods would dispel each other (or other troubles).

The solution to this problem is to use the WeiDU command ADD_SECTYPE (or ADD_SCHOOL) to reserve a secondary type for your spells; this ensures that spells added by mods that use ADD_SECTYPE won't clash between them.

Converting a large mod to use ADD_SECTYPE might be non-trivial if lots of spells use custom secondary types - you have to check which effects in spells and items protect from, dispel, or reflect your custom secondary type. This tp2 snippet will scan all .eff, .spl, .itm and .cre files to see which files use which custom secondary types, and the offset where the sectype is used (note: the offset will be printed in decimal, not hexadecimal).

OUTER_SET school_threshold = 9
OUTER_SET sectype_threshold = 13

DEFINE_PATCH_MACRO ~check~ BEGIN
READ_LONG offset type
PATCH_IF (opcode = 202 || opcode = 204 || opcode = 220 || opcode = 223 || opcode = 227 || opcode = 229) && type > school_threshold THEN BEGIN
  PATCH_PRINT ~%SOURCE_FILE%: school %type% @ LONG %offset%~
END
PATCH_IF (opcode = 203 || opcode = 205 || opcode = 221 || opcode = 226 || opcode = 228 || opcode = 230)  && type > sectype_threshold THEN BEGIN
  PATCH_PRINT ~%SOURCE_FILE%: sectype %type% @ LONG %offset%~
END
END

COPY_EXISTING_REGEXP GLOB ~^.*\.spl$~ ~override~
  READ_SHORT 0x25 school
  READ_BYTE 0x27 sectype
  PATCH_IF school > school_threshold THEN BEGIN
	PATCH_PRINT ~%SOURCE_FILE% is school %school% @ SHORT 37~
  END
  PATCH_IF sectype > sectype_threshold THEN BEGIN
	PATCH_PRINT ~%SOURCE_FILE% is sectype %sectype% @ BYTE 39~
  END
BUT_ONLY

COPY_EXISTING_REGEXP GLOB ~^.*\.spl$~ ~override~
~^.*\.itm$~ ~override~
  READ_ASCII 0 signature (4)
  PATCH_IF ~%signature%~ STRING_EQUAL ~SPL ~ THEN BEGIN
	header_length = 0x28
  END ELSE PATCH_IF  ~%signature%~ STRING_EQUAL ~ITM ~ THEN BEGIN
	header_length = 0x38
  END ELSE BEGIN
	INNER_ACTION BEGIN
	  FAIL ~Malformed file signature "%signature%": %SOURCE_FILE%~
	END
  END
  READ_LONG 0x64 "headoffset"  ELSE 0
  READ_SHORT 0x68 "headcount"  ELSE 0
  READ_LONG 0x6a "effectsoffset"  ELSE 0
  WHILE ("%headcount%" > 0) BEGIN
	 READ_SHORT ("%headoffset%" + (("%headcount%" - 1 ) * header_length) + 0x1e) "count"  ELSE 0
	 READ_SHORT ("%headoffset%" + (("%headcount%" - 1 ) * header_length) + 0x20) "effectsindex"  ELSE 0
	 WHILE ("%count%" >0) BEGIN
		SET start = "%effectsoffset%" + (("%effectsindex%" + "%count%" - 1 )* 0x30)
		READ_SHORT start opcode
		SET offset = start + 8
		LPM ~check~
		SET "count" = ("%count%" -1)
	 END
	 SET "headcount" = "%headcount%" - 1
  END
	 READ_SHORT 0x70 "count"  ELSE 0
	 WHILE ("%count%" >0) BEGIN
		SET start = "%effectsoffset%" + (("%count%" - 1 )* 0x30)
		READ_SHORT start opcode
		SET offset = start + 8
		LPM ~check~
		SET "count" = ("%count%" -1)
	 END
BUT_ONLY

COPY_EXISTING_REGEXP GLOB ~^.*\.cre$~ ~override~
  READ_BYTE 0x33 "effv"  ELSE 0
  READ_LONG 0x02c4 "offseteffs"  ELSE 0
  READ_LONG 0x02c8 "counteffs"   ELSE 0
  PATCH_IF ("%effv%" = 0) BEGIN  //effects V1, like itms or spells
		WHILE ("%counteffs%" > 0) BEGIN
		  SET start = "%offseteffs%" + (("%counteffs%" - 1 ) * 0x30)
		  READ_SHORT start opcode
		  SET offset = start + 8
		  LPM ~check~
		  SET "counteffs" = "%counteffs%" - 1
		END
  END
  PATCH_IF ("%effv%" = 1) BEGIN  //effects V2
		WHILE ("%counteffs%" > 0) BEGIN
		SET start = "%offseteffs%" + (("%counteffs%" - 1 ) * 0x108) - 8
		READ_LONG start + 0x10  "opcode"  ELSE 0
		SET offset = start + 0x20
		LPM ~check~
		SET "counteffs" = "%counteffs%" - 1
		END
  END
BUT_ONLY

COPY_EXISTING_REGEXP GLOB ~^.*\.eff$~ ~override~
  READ_LONG 0x10  "opcode"  ELSE 0
  SET offset = 0x20
  LPM ~check~
BUT_ONLY

You still need to remember (or figure out) what your custom sectype(s) do, add an ADD_SECTYPE for each custom sectype to your tp2, and make sure that, when you copy/create/patch each file, you use the custom sectype instead of the old one you hardcoded in (most likely, by adding one or more WRITE_SOMETHING when copying your file to the override, but your situation might require more advanced patching).

I hope you'll find this code useful, and don't hesitate to provide comments or corrections.
Italian users: help test the Stivan NPC!

Author or Co-Author: WeiDU - Widescreen - Generalized Biffing - Refinements - TB#Tweaks - IWD2Tweaks - TB#Characters - Traify Tool - Some mods that I won't mention in public
Maintainer: Semi-Multi Clerics - Nalia Mod - Nvidia Fix
Code dumps: Detect custom secondary types - Stutter Investigator

If possible, send diffs, translations and other contributions using Git.




Reply to this topic



  


1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users