Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories

New Premium Module: Tyrants of the Moonsea! Read More
Attention, new and old users! Please read the new rules of conduct for the forums, and we hope you enjoy your stay!

[tool] IWD-style Spell Evasion

subtledoctorsubtledoctor Member Posts: 11,460
Just want to drop a note to mention that I've externalized my code adding the IWD "Evasion" passive ability for thieves to BGEE and BG2EE. My "Scales of Balance" mod has an option to add it exactly as it is in IWD, for 7th-level thieves; my "Might & Guile" mod adds it as a feat available to thieves as well as some bards and warriors. My upcoming "Will to Power" adds it as a temporary ability for psionicists, independent of those other applications.

The idea is, you can drop this file into your mod to make the passive ability work; and then you can apply the ability however you see fit - for your new kit, for certain enemies, for all thieves, for a new spell, whatever. If you use this .tpa file as-is, then however you apply it, it should not conflict with other mods using it in different ways.

Get it here. Cheers.

semiticgodStummvonBordwehrLuke93Grammarsaladswit
«1

Comments

  • subtledoctorsubtledoctor Member Posts: 11,460
    edited May 2018
    Worth mentioning: this function can be adapted for use in ways other than the strict IWD-style Evasion ability.

    For instance, by adding a bit more to this (adding a new, different spellstate), you could do something like:
    - give jesters an extra saving throw to evade spells that cause charm and confusion
    - give all paladins an extra saving throw to evade spells that cause fear
    - replace the blanket immunities of Berserk Rage with an extra saving throw to evade the effects

    ...actually, I might just implement that last one myself. :tongue:

    Post edited by subtledoctor on
    GwendolyneGrammarsalad
  • GwendolyneGwendolyne Member Posts: 296
    edited May 2018
    Tremendous news!

    My todo list contains the line 'escape dragon breaths'. It would be awesome if your code spared me hours of coding. I will try it out. ;)

    Post edited by Gwendolyne on
  • subtledoctorsubtledoctor Member Posts: 11,460

    Tremendous news!

    My todo list contains the line 'escape dragon breaths'. It would be awesome it your code spared me hours of coding. I will try it out. ;)

    Just need to know the resource for dragon breath. Is it a .SPL?

    I haven't tried applying this to item effects. I wonder if @kjeron knows whether 324/318 effects in item abilities will block the ability from taking effect, the way it works in spells?

  • GwendolyneGwendolyne Member Posts: 296
    They are innate abilities (spells) : copper, brass, rust, ruby.... dragon breath (metallic, chromatic, planar... dragons) spells. It might work. :)

  • subtledoctorsubtledoctor Member Posts: 11,460
    Oh, for custom spells? Do you want to simply add them to the list of spells thst can be evaded? Or set up a different-but-similar mechanism?

    I suppose I should make this even more modular. I'll take a look at improving it. Maybe add a .2da list of spells that are already subject to Evasion, so there is no chance of redundant application.

    Meantime, to only apply it to your custom spells - either instead of, or in addition to, the spells in this list - take a look here:

    ACTION_PHP_EACH evade_spells AS evaded_spell => ind BEGIN
    ACTION_IF FILE_EXISTS_IN_GAME ~%evaded_spell%.spl~ BEGIN
    COPY_EXISTING ~%evaded_spell%.spl~ ~override~
    LPF DELETE_EFFECT INT_VAR match_opcode = 324 match_parameter2 = 63 END
    LPF DELETE_EFFECT INT_VAR match_opcode = 72 match_parameter1 = 4 match_duration = 0 END
    LPF ADD_SPELL_EFFECT INT_VAR opcode = 177 insert_point = 0 target = 2 parameter1 = 0 parameter2 = 2 timing = 0 duration = 1 savingthrow = 2 STR_VAR resource = EVAL ~d5ev%evadable_ind%~ END
    LPF ADD_SPELL_EFFECT INT_VAR opcode = 206 insert_point = 0 target = 2 parameter1 = (0 - 1) parameter2 = 0 timing = 0 duration = 1 savingthrow = 2 STR_VAR resource = EVAL ~d5ev%evadable_ind%~ END
    BUT_ONLY
    CREATE EFF ~d5ev%evadable_ind%~
    WRITE_LONG 0x10 324
    WRITE_LONG 0x14 2
    WRITE_LONG 0x1c 252
    WRITE_LONG 0x20 110
    WRITE_LONG 0x24 0
    WRITE_LONG 0x28 1
    WRITE_SHORT 0x2c 100
    WRITE_EVALUATED_ASCII 0x30 ~%evaded_spell%~ #8
    WRITE_LONG 0x90 1
    WRITE_EVALUATED_ASCII 0x94 ~d5ev%evadable_ind%~ #8
    END
    OUTER_SET evadable_ind = (evadable_ind + 1)
    END
    As you can maybe see, I needed a way to identify and provide immunity to the .EFF files that create the extra saving throw... to do this programmatically, I resorting to using an integer that increases by 1 each time it processes a spell; then the integer is tacked onto a double-prefix, in this case "d5ev."

    The easiest way to apply this to your own spells would be to create an array of your spell names in the form
    spell_1 => 1
    spell_2 => 1
    etc.
    (The trailing "1" is totally arbitrary.)

    Then apply the code I quoted above to your array, changing only the "d5ev" prefix.

    That will make your doells subject to Evasion; then you only need to set the Evasion spellstate via the d5evade spell.

  • GwendolyneGwendolyne Member Posts: 296
    That's what I intended to do. ;)

    It is for a paladin kit, a kind of cavalier only specialized in dragons fighting. I wrote it for the classic games and could not code all the abilities I wanted. At level 10, he chooses a dragon special enemy and gains bonus against them every 5 levels. With your code, I can give him (at level 30) a percentage to avoid their dragon breath.

    thanks again.

  • kjeronkjeron Member Posts: 2,130

    I haven't tried applying this to item effects. I wonder if @kjeron knows whether 324/318 effects in item abilities will block the ability from taking effect, the way it works in spells?

    318/324 can block items, 206 cannot.

  • subtledoctorsubtledoctor Member Posts: 11,460
    edited May 2018

    It is for a paladin kit, a kind of cavalier only specialized in dragons fighting. I wrote it for the classic games and could not code all the abilities I wanted. At level 10, he chooses a dragon special enemy and gains bonus against them every 5 levels. With your code, I can give him (at level 30) a percentage to avoid their dragon breath.

    In that case it might be better to use a custom spellstate rather than the Evasion state. The spellstate you use is totally arbitrary, so it's easy enough to add a new one and use it for this purpose.

    A function to add a new spellstate is here, and to use the function in your mod you do something like:
    LAF d5_resolve_state STR_VAR new_state_id = ~EVADE_DRAGON_BREATH~ RET new_state_ind END

    OUTER_SET evade_dragon_state = %new_state_ind%
    Then instead of using state 252 (Evasion) in this thread's function, you use %evade_dragon_state%.

  • GwendolyneGwendolyne Member Posts: 296
    Yeah. That's what I had in mind to avoid giving the thief evade ability to a paladin. :)

    I will code this stuff next week.

  • subtledoctorsubtledoctor Member Posts: 11,460

    Yeah. That's what I had in mind to avoid giving the thief evade ability to a paladin. :)

    I will code this stuff next week.

    I've updated the repo, now it is a function that you can apply to any spell, and you can feed the function any spellstate and any saving throw you wish. So this should make it quite easy to apply it in different ways. You simply feed the function four variables:
    - the spellstate you will use
    - the saving throw that will avoid the effects
    - the name of the spell you will apply the function to (or a variable if applying it via an array or list)
    - a 4-character prefix that will have a 1- to 3-digit integer attached as a suffix

    The updated readme on the Github page has more in-depth information.

    GwendolyneRaduziel
  • GrammarsaladGrammarsalad Member Posts: 2,509

    Worth mentioning: this basic framework could be adapted for use in ways other than the strict IWD-style Evasion ability.

    For instance, by adding a bit more to this (adding a new, different spellstate), you could do something like:
    - give jesters an extra saving throw to evade spells that cause charm and confusion
    - give all paladins an extra saving throw to evade spells that cause fear
    - replace the blanket immunities of Berserk Rage with an extra saving throw to evade the effects

    ...actually, I might just implement that last one myself. :tongue:

    ...okay, this is awesome

  • subtledoctorsubtledoctor Member Posts: 11,460
    edited February 2019
    Just wanted to note that I've updated this function to version 3.0.

    First, this fixes a small bug that was adding unnecessary saving throw feedback for targets who should not be affected by Evasion at all. ( @Luke93 I think we missed a bit in our recent discussion of the matter - I think all bases are covered now.)

    Secondly, and I think more interestingly, I have added three variables that can be used with the function. A string variable, "evade_condition," can be set to ~spellstate~, ~class~, or ~kit~. It defaults to ~spellstate~ and in that mode the function works exactly as before, applying the evasion conditions to anyone with the spellstate defined in the "evasion_spellstate" variable (which defaults to 252).

    If you set "evade_condition" to ~class~, then it will ignore the "evasion_spellstate" variable and you can instead define the new "evasion_class" variable, with a class number from the 1st column of CLASS.IDS. Similarly, if you set "evade_condition" to ~kit~, then it will ignore the "evasion_spellstate" variable and you can instead define the new "evasion_kit" variable, with a hexadecimal kit number from the 1st column of KIT.IDS.

    So as an example, in the forthcoming update of Might & Guile, I am giving Rangers an extra opportunity to avoid disease and poison effects. Essentially, to invoke a 5E-ism, rangers will be able to roll saving throws against disease and poison "with advantage." Once I create an array with all of the spells and items* I want to be subject to this resistance, I simply call the function like so:
    ACTION_PHP_EACH ranger_resists AS resisted_effect => ind BEGIN
      LAF add_evade_spell INT_VAR evasion_class = 12 evasion_save = 4 STR_VAR evade_condition = ~class~ evade_res = EVAL ~%resisted_effect%~ evade_prefix = ~D5RR~ END
    END
    

    Bob's your uncle - now this will apply to every ranger in the game, including AI NPCs. No need for spellstates, appending things to kit ability tables, or anything like that. You could do something similar with kits - e.g. I'm considering making the Berserker resistance to Charm/Hold/Sleep/etc. spells into a permanent passive evasion effect instead of an immunity that is only active while raging. All it would take is to collect the applicable spells, and then call this function with the "evasion_kit" variable set to 0x4001.

    * Oh yeah, I haven't mentioned it in this thread, but you can evade item effects with this as well as spells. The base IWD-style evasion, when used with the "expanded evadable spell list," will now also work against items like the Wand of Fire and the Ring of Energy, etc.

    RaduzielLuke93Grammarsalad
  • Luke93Luke93 Member, Mobile Tester Posts: 1,299
    edited February 2019
    ( @Luke93 I think we missed a bit in our recent discussion of the matter - I think all bases are covered now.)

    Speaking of this, I guess CD_STATE_NOTVALID may be an appropriate parameter1 for the 2 opcodes #318 (it's a hex sum of bits, each responsible for one of debilitating states. E.g. confusion, feeblemindedness, silence, various deaths etc.).

    However, after further reflection, we did miss something: Grease and Entangle.
    As far as I know, these two spells don't set any STATE, so we need to patch them by adding opcode #328 (param2 = 28 and 26 respectively). After that, we need to add two more opcodes #318 (param2: splstate = specified value, param1 = 28 and 26 respectively) to every .SPL/ITM that is meant to be evadable.....

  • subtledoctorsubtledoctor Member Posts: 11,460
    edited February 2019
    Luke93 wrote: »
    I guess CD_STATE_NOTVALID may be an appropriate parameter1 for the 2 opcodes #318 (it's a hex sum of bits, each responsible for one of debilitating states. E.g. confusion, feeblemindedness, silence, various deaths etc.).
    I'm not sure what is to be gained? I already summed all the hex values and it works fine. If any other states need to be added, their value can just be added to the sum. (Except STATE_CONFUSED, which cannot be added.) I don't see any reason to refer to a different entry in the IDS file.

    In fact, now that I think about it, this application doesn't refer to 'entries' in the IDS file at all. Referring to CD_STATE_NOTVALID would literally be the same as summing up the chosen states and entering the sum of their values. What's more, to the extent that CD_STATE_NOTVALID includes the faulty 0x80000000 value, it and all of the ones following it (STATE_DISABLED, STATE_DEBUFF, etc.) will actually break the Evasion function and cannot therefore be used. :(
    Luke93 wrote: »
    we did miss something: Grease and Entangle.
    As far as I know, these two spells don't set any STATE, so we need to patch them by adding opcode #328 (param2 = 28 and 26 respectively). After that, we need to add two more opcodes #318 (param2: splstate = specified value, param1 = 28 and 26 respectively) to every .SPL/ITM that is meant to be evadable.....

    Do those stop Evasion in the original IWD? In any event what you suggest sounds right to me, you shouldn't be able to evade a Fireball if you're slip-sliding in a pool of grease. Though I'd prefer not to mess with states... mayb- EDIT - oh I see, you mean spellstates 26 and 28. Yeah, that's a good idea.

  • Luke93Luke93 Member, Mobile Tester Posts: 1,299
    In any event what you suggest sounds right to me, you shouldn't be able to evade a Fireball if you're slip-sliding in a pool of grease. EDIT - oh I see, you mean spellstates 26 and 28. Yeah, that's a good idea.

    Precisely :)

  • Luke93Luke93 Member, Mobile Tester Posts: 1,299
    edited March 2019
    Here we go again: I found another oddity ---> It seems that the string Save vs. Breath Weapons triggers even for characters that do not have the relevant spell state :/ (have a look at the attached screenshot: Holy Smite is not supposed to provide a Save vs. Breath....) Why?

  • Luke93Luke93 Member, Mobile Tester Posts: 1,299
    Summoning @kjeron too (as always.....)

  • Luke93Luke93 Member, Mobile Tester Posts: 1,299
    edited March 2019
    Luke93 wrote: »
    Summoning @kjeron too (as always.....)

    I suspect this has something to do with opcode #177 and its check for EA/ANYONE..... Everything is fine if you check for CLASS/THIEF_ALL........

  • Luke93Luke93 Member, Mobile Tester Posts: 1,299
    edited March 2019
    First, this fixes a small bug that was adding unnecessary saving throw feedback for targets who should not be affected by Evasion at all.

    Nope, unfortunately :( .....

    As I said, it seems that this type of feedback Abdel: Save vs. Breath Weapon: 16 always triggers (even for all those CREs that don't have the Evasion spell state.....). This is probably linked to op#177 and its check for EA/ANYONE......

    As you said, a possible solution would be that of defining the group of people who can evade by being in a certain CLASS..... The problem with this solution is that the Evasion passive ability will be available right from level 1......

  • Luke93Luke93 Member, Mobile Tester Posts: 1,299
    Wait, that is easy to fix :) : just add another two opcodes #318 protecting from the two EFFs if preset target does not have EVASION (parameter1 = 252, parameter2 = SPLSTATE != specified value)

  • kjeronkjeron Member Posts: 2,130
    edited March 2019
    Luke93 wrote: »
    Wait, that is easy to fix :) : just add another two opcodes #318 protecting from the two EFFs if preset target does not have EVASION (parameter1 = 252, parameter2 = SPLSTATE != specified value)
    That's what it already does...
    op318 - helpless STATEs EFFB
    op318 - helpless STATEs EFFA
    op318 - 252 !SPLSTATE EFFB
    op318 - 252 !SPLSTATE EFFA
    op177 - EFFB - op318 252 !SPLSTATE save EFFA
    op177 - EFFA - op324 252 !SPLSTATE nosave SOURCE_RES

    I don't know what circumstances are causing it to not do that for you.

  • Luke93Luke93 Member, Mobile Tester Posts: 1,299
    kjeron wrote: »
    op318 - 252 !SPLSTATE EFFB
    op318 - 252 !SPLSTATE EFFA

    I cannot see these two....

  • Luke93Luke93 Member, Mobile Tester Posts: 1,299
    kjeron wrote: »
    op177 - EFFB - op318 252 !SPLSTATE save EFFA
    op177 - EFFA - op324 252 !SPLSTATE nosave SOURCE_RES

    You meant SPLSTATE (without the "!"), right?

  • kjeronkjeron Member Posts: 2,130
    Luke93 wrote: »
    You meant SPLSTATE (without the "!"), right?
    No, it was shorthand for NOT SPLSTATE.

  • subtledoctorsubtledoctor Member Posts: 11,460
    No, Luke is right, your post has a typo.

    But Kjeron is right, the relevant effects are all there.

    Are you using the latest version?

    (Gonna update it again soon, and shoehorn the ability to use % chance to evade instead of saving throws.)

    Luke93
  • Luke93Luke93 Member, Mobile Tester Posts: 1,299
    edited March 2019
    I'll check that out.....

    Anyway, there should not need for two .EFFs..... I mean, what about this?
    LPF ADD_SPELL_EFFECT
    INT_VAR
    	insert_point = 0
    	opcode = 177			// Use EFF file
    	target = 2
    	parameter2 = 2			// EA = ANYONE
    	duration = 1
    STR_VAR
    	resource = EVAL ~EFF_A~
    END
    
    LPF ADD_SPELL_EFFECT
    INT_VAR
    	insert_point = 0
    	opcode = 318                    // Protection from spell
    	target = 2
    	parameter1 = 252					// EVASION
    	parameter2 = 110                // SPLSTATE = specified value
    	duration = 1
    	savingthrow = BIT1			// Save vs. Breath
    STR_VAR
    	resource = EVAL ~EFF_A~
    END
    
    LPF ADD_SPELL_EFFECT
    INT_VAR
    	insert_point = 0
    	opcode = 318                    // Protection from spell
    	target = 2
    	parameter1 = 26					// ENTANGLE
    	parameter2 = 110                // SPLSTATE = specified value
    	duration = 1
    STR_VAR
    	resource = EVAL ~EFF_A~
    END
    
    LPF ADD_SPELL_EFFECT
    INT_VAR
    	insert_point = 0
    	opcode = 318                    // Protection from spell
    	target = 2
    	parameter1 = 28					// GREASE
    	parameter2 = 110                // SPLSTATE = specified value
    	duration = 1
    STR_VAR
    	resource = EVAL ~EFF_A~
    END
    
    LPF ADD_SPELL_EFFECT
    INT_VAR
    	insert_point = 0
    	opcode = 318                    // Protection from spell
    	target = 2
    	parameter1 = state_helpless_all
    	parameter2 = 138                // STATE bit_eq specified value
    	duration = 1
    STR_VAR
    	resource = EVAL ~EFF_A~
    END
    
    CREATE	"EFF"	"EFF_A"
    	WRITE_LONG 0x010	324		// Immunity from spell and message
    	WRITE_LONG 0x014	2
    	WRITE_LONG 0x01c	252		// EVASION
    	WRITE_LONG 0x020	110		// SPLSTATE = specified value
    	WRITE_LONG 0x028	1		// Duration
    	WRITE_SHORT 0x2c	100		// Probability1
    	WRITE_EVALUATED_ASCII 0x030	~%evaded_spell%~ #8
    	WRITE_LONG 0x090	1		// Resouce type: spell
    	WRITE_EVALUATED_ASCII 0x094	~EFF_A~	#8
    

    The problem is that for some bizarre reason (a bug probably), the saving throw feedback triggers even if the targeted CRE doesn't meet the conditions specified by opcode #318/324 :( ......

  • kjeronkjeron Member Posts: 2,130
    Luke93 wrote: »
    The problem is that for some bizarre reason (a bug probably), the saving throw feedback triggers even if the targeted CRE doesn't meet the conditions specified by opcode #318/324 :( ......
    It's standard check order:
    1. Min/Max Level/HD
    2. Magic Resistance
    3. Saving Throws (Best->Worst), If Equal:(Death->Wand->Poly->Breath->Spell)
    4. parameters specific to the opcode.

    Luke93
  • subtledoctorsubtledoctor Member Posts: 11,460
    Luke93 wrote: »
    Anyway, there should not need for two .EFFs.....

    It used to onlybuse one .EFF, I added the 2nd one to address your concern about too much text feedback :lol:

    I'm honestly not sure what you are reporting. Gotta gove me steps to reptoduce if there's a bug that needs addressing.
    kjeron wrote: »
    Luke93 wrote: »
    The problem is that for some bizarre reason (a bug probably), the saving throw feedback triggers even if the targeted CRE doesn't meet the conditions specified by opcode #318/324 :( ......
    It's standard check order:
    1. Min/Max Level/HD
    2. Magic Resistance
    3. Saving Throws (Best->Worst), If Equal:(Death->Wand->Poly->Breath->Spell)
    4. parameters specific to the opcode.

    I'm dense about this kind of stuff... what is the consequence of this?

    Does this mean saving throws for effect #3 are rolled before effect #1 is processed, so the roll will happen even if effect #1 makes the target immune to the effect that requires a save?

    If that's the case then maybe could simply use subspells instead of .EFF files...?

    I have to say, I don't think errant text feedback is the end of the world, if it can't be avoided and as long as things are actually working correctly. If the only alternative is not to make the mod, well, I'm going to go ahead and make the mod. Every player of course has a choice whether to use it...

  • kjeronkjeron Member Posts: 2,130
    edited March 2019
    I'm dense about this kind of stuff... what is the consequence of this?
    It just means that second EFF was necessary to avoid the unwanted feedback.

  • subtledoctorsubtledoctor Member Posts: 11,460
    kjeron wrote: »
    I'm dense about this kind of stuff... what is the consequence of this?
    It just means that second EFF was necessary to avoid the unwanted feedback.

    Oh! Got it. So hopefully all is well.

Sign In or Register to comment.