DavidW Posted May 11, 2019 Posted May 11, 2019 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. Quote
DavidW Posted May 11, 2019 Author Posted May 11, 2019 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). Quote
DavidW Posted May 11, 2019 Author Posted May 11, 2019 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 Quote
DavidW Posted May 11, 2019 Author Posted May 11, 2019 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.) Quote
DavidW Posted May 11, 2019 Author Posted May 11, 2019 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 Quote
DavidW Posted May 11, 2019 Author Posted May 11, 2019 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. Quote
Luke Posted May 15, 2019 Posted May 15, 2019 (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 May 15, 2019 by Luke Quote
DavidW Posted May 15, 2019 Author Posted May 15, 2019 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. Quote
Jarno Mikkola Posted May 15, 2019 Posted May 15, 2019 On 5/11/2019 at 1:01 PM, DavidW said: They're available on the SCS repo in stratagems/lib/alter_effect.tpa. Fixed that for yöu. Quote
K4thos Posted May 29, 2019 Posted May 29, 2019 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? Quote
DavidW Posted June 1, 2019 Author Posted June 1, 2019 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. Quote
K4thos Posted June 1, 2019 Posted June 1, 2019 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. Quote
Luke Posted June 3, 2019 Posted June 3, 2019 On 6/1/2019 at 11:54 PM, K4thos said: (alongside ADD_SPELL_HEADER, ADD_ITEM_HEADER from this topic) This has been already reported..... Quote
Grammarsalad Posted June 15, 2019 Posted June 15, 2019 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. Quote
DavidW Posted June 16, 2019 Author Posted June 16, 2019 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). Quote
Recommended Posts
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.