Jump to content

Enhanced ALTER_EFFECT functions


Recommended Posts

Posted

For various reasons I've been doing some coding recently where it's been convenient not to rely on SCS's function library, but to use the core WEIDU functions instead. In particular, I've been using ADD_[SPELL/ITEM]_EFFECT and [ALTER/CLONE/DELETE]_EFFECT. They're very nice but I found myself needing slightly more functionality, so I worked up a slightly modified version. They're available on the SCS repo in stratagems/lib/alter_effect.tpa.

There are four sets of changes, which I'll detail below.

Posted

1. flag matching and setting

Effects have flag data at 0x24-0x27 (the saving throw field) and 0x2c-0x2f (the 'special' field). The WEIDU functions only allow you to match or write these fields as LONG entries. My version gives you access to most of the main flags. Specifically, it defines the following variables:

	save_vs_spell
	save_vs_breath
	save_vs_poison
	save_vs_wand
	save_vs_polymorph
	ignore_primary
	ignore_secondary
	bypass_mirror_image
	ignore_difficulty
	drain_hp_to_caster
	transfer_hp_to_target
	fist_damage_only
	drain_to_max_hp
	suppress_feedback
	save_for_half
	made_save
	does_not_wake
	

along with the associated match_ variables (e.g., match_ignore_secondary, match_save_vs_spell).

Posted

2. Subfields of parameter2.

Certain opcodes (notably opcode 12, 'damage') split the parameter2 field into two subfields: each is a SHORT, located respectively at 0x8 and 0xa. My version lets you write to and match on these separately, via the variables 

	parameter2a
	parameter2b
	match_parameter2a
	match_parameter2b
	

Posted

3. Negative values of parameter1 and parameter2

This is minor - but for implementation reasons, in the original WEIDU versions of the functions, parameter1 and parameter2 have to take nonnegative values (otherwise they are ignored). My version lets them take negative values between -9 and -1. (The main application is if a parameter represents a string and you want to set it to -1.)

Posted

4. Function matching and patching

This one is very much for advanced users only! (If you're not happy defining your own functions, ignore this bit of the post.) It introduces two new STR variables:

	function (only for CLONE_EFFECT/ALTER_EFFECT)
	match_function (only for CLONE_EFFECT/ALTER_EFFECT/DELETE_EFFECT)
	

Each should be set to the name of a PATCH_FUNCTION that expects to execute on a length-0x30 file with the structure of an EFFv1. The match_function should have one return value, 'value', which can be equal to either 0 or 1. If match_function is set, the function is executed on each EFF block, and then the CLONE/ALTER/DELETE is performed on each block for which the function returns value=1. When it is executed, if function is set then that function is executed on the cloned or altered block.

This is easiest to illustrate through examples. Firstly, suppose you want to delete all damage-resistance effects (that is, all opcodes in the ranges 27-31 and 84-89). Of course you could do it with 11 DELETE_EFFECTs, but that's tedious to type and inefficient to execute if you're applying it to many items (which, ok, is unlikely in this case, but there are more realistic cases). You can do it like this. First, define a function that returns 1 if the opcode is in the desired range:

	DEFINE_PATCH_FUNCTION is_damage_resistance
	RET value
	BEGIN
	READ_BYTE 0x0 opcode
	PATCH_MATCH "%opcode%" WITH
	27 28 29 30 31 84 85 86 87 88 89 BEGIN
	SET value=1
	END
	DEFAULT
	SET value=0
	END
	END
	

Then do this patch on the item or spell you're working on

	LPF DELETE_EFFECT STR_VAR match_function=is_damage_resistance END
	

Secondly, suppose you instead want to halve the efficacy of all damage-resistance powers. Define another function:

	DEFINE_PATCH_FUNCTION halve_parameter2
	BEGIN
	WRITE_LONG 0x4  (LONG_AT 0x4) / 2
	END
	

Then just apply the patch

	LPF ALTER_EFFECT STR_VAR match_function=is_damage_resistance function=halve_parameter2 END
	
Posted

4a. More advanced function matching/patching

Instead of a function name, you can set function or match_function to a string

	function_name(args)
	

Function_name should still be the name of an appropriate patch function, but if that function has a STR_VAR 'arguments', 'args' will be fed into that variable. In my applications, I defined functions 'opcode_is' and 'opcode_is_not', each of which took as argument a space-separated string of integers and returned 1 if the opcode was /wasn't in the string.

Posted (edited)
On 5/11/2019 at 12:09 PM, DavidW said:

3. Negative values of parameter1 and parameter2

(The main application is if a parameter represents a string and you want to set it to -1.)

Moreover, there may be issues with signed integers (i.e., all those values for which BIT31 - 0x80000000 is set to 1). For instance:

LPF ALTER_EFFECT
INT_VAR
  match_opcode = 324 // Immunity from spell and message
  parameter1 = 0x80110FEF // the function won't write this value...
  parameter2 = 138 // STATE bit_eq specified value
END

 

Edited by Luke
Posted

Yes, but this fix won’t handle that case, because I didn’t need it. I wasn’t really aiming to do a systematic improvement of the toolset, just to share something I’d done for my own use.

Posted

Thanks, @DavidW for this awesome addition. Editing params via functions works great.

 

I've noticed that you didn't include ADD_ITEM_EQEFFECT in your altered macros file. Are you planning to add it in future?

Posted

To be truthful, I was just adding things that I found I needed, and didn't find the need to do ADD_ITEM_EQEFFECT / ADD_SPELL_CFEFFECT. It would be easy enough to do; equally, is the use case that obvious? Those sort of effects very rarely have saving throws or special parameters, and very occasional edge cases can always be worked around using a subsequent ALTER_EFFECT.

Posted

If you add missing stuff  to ADD_ITEM_EQEFFECT / ADD_SPELL_CFEFFECT I don't see a reason why not include your variants of all these functions officially in weidu (alongside ADD_SPELL_HEADER, ADD_ITEM_HEADER from this topic). I doubt Wisp will be against it considering your variants are well documented in this topic, works as expected and greatly expands the inital functionality (the functions support for ALTER effects is fantastic addition).

btw. yes, I need parameter2a / b distinction for ADD_ITEM_EQEFFECT in my mod, but of course I can modify the code myself to add this one feature. I just think your  functions should be internalized officialy in weidu.

Posted

Well, this is very cool.  I've found a reason to modify rather than set values of certain effect variables.  Well, I'm only doing this for savebonus, but I could see it being useful for duration, dicesize, dicenumber, or in certain cases, param1 or 2 (e.g. opcode 12).  

That is, right now I'm doing a thing where characters get a bonus based on dexterity to save vs. certain spells, such as fireball.  Basically, I'm cloning the spell to cast different versions of itself based on the targets dexterity (i.e. giving bonuses for exceptional dex).  But, certain spells--and certain mods and potential mods--already impose save bonuses or penalties for certain spells, and I want the code to be as unintrusive--as unintrusive as a modification like this could be--as possible, so I just modified alter_spell accordingly (i.e. as a new zombie function).  

If you're already building a more expansive ALTER_SPELL, maybe you can add a few subfunctions that, if ticked, would add/subtract the inputted value rather than set it to that value for a given field.  That is, for example, there could be a set_savebonus which could default to 0.  If the value of set_savebonus is 0, then savebonus acts as normal (i.e. it sets the save bonus).  But, if set_savebonus is non-zero, then save bonus = the previous value +/- savebonus.  Similar for other fields

I'd offer my code, but it's not this sophisticated (I just hijacked savebonus).  So, instead, I'm offering the suggestion.    

Posted

The way I code, you'd just do it functionally. Define a function that reads savebonus, adds something, writes it back. Then run ALTER_EFFECT with that function as a 'function' argument (and with any more match requirements you want).

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...