Getting the most out of Opcodes 318 and 177(EFFs).
kjeron
Member Posts: 2,368
Apparently, Opcode 318 is able to block EFF files based on their parent resource field, which I only fully realized after suggesting using it as such here.
Then add the following effect to any CLAB/Feat/etc... that you want to ignore arcane spell failure from armor:
So:
The (SPLPROT) will only update when the item is equipped (same as opcode 177), so it would only be useful for mostly-static checks.
For example, you could properly restrict the Wizard Slayer from all magical weapons, then grant them an enchantment level equal to their proficiency level with any weapon they use (with or without the bonus to hit/damage). Maybe even a THAC0 bonus for zero-proficiency weapons so to offset the non-proficiency penalty so they retain a non-magical option against PfMW.
Or, if you were doing some type of 3E conversion:
Easily Suppressing Arcane Spell Failure:
Patch all items to disable wizard spell casting through an external EFF:COPY_EXISTING_REGEXP ~^.+\.itm$~ override LPF ALTER_EFFECT INT_VAR match_opcode = 145 match_parameter2 = 0 match_timing = 2 opcode = 177 parameter2 = 2 STR_VAR resource = (EXTEFF) END BUT_ONLY CREATE EFF ~(EXTEFF)~ WRITE_SHORT 0x10 145 WRITE_LONG 0x2c 100 WRITE_EVALUATED_ASCII ~(ARCFAIL)~
Then add the following effect to any CLAB/Feat/etc... that you want to ignore arcane spell failure from armor:
opcode=318, target=2, timing=9, probability1=100, resource = (ARCFAIL).
Alternatively, create a separate EFF for each armor tier (ARCFAIL1, ARCFAIL2, ARCFAIL3, etc...), and grant immunity to only those tiers that you want them to ignore. Could work just as well for casting failure % instead of disabled casting.
Accurately granting immunity to just one form of an opcode:
For example: Opcode 39 (Sleep). It is used for: Sleep, Unconsciousness, Knockdown, Hopelessness, and Nausea(Poison).So:
- Take all related effects in every sleep spell and move them to external EFF files, giving them all the same parent resource (EFFECT_SLEEP). Most of these spells could share EFF's, as the variations (saving throws, min/max hd/level, magic resistance, power, probability, etc...) would be put in opcode 177, not the EFF.
The EFF file would mainly have: Opcode, parameter1/2/3(as appropriate), resource1/2/3(if necessary), special, probability1=100, parent_resource=(EFFECT_[TYPE]). All other fields would be zero/empty. - For creatures that should be immune to "Sleep", replace Opcode 102/Parameter2=39 (Immunity to effect: opcode 39) with the following effect:
opcode=318, target=2, timing=9, probability1=100, resource = (EFFECT_SLEEP).
- Do the same for Unconsciousness, Knockdown, Hopelessness, and Nausea(Poison).
Note: Opcode 39 is still bugged some of in the v2.5 betas (and the v2.5 IWDEE release, though significantly less so), so I advise against testing this specific one in the beta patches. - The undead rings, for example, would end up with:
opcode=318, target=2, timing=9, probability1=100, resource = (EFFECT_SLEEP). opcode=318, target=2, timing=9, probability1=100, resource = (EFFECT_UNCONSCIOUS). opcode=318, target=2, timing=9, probability1=100, resource = (EFFECT_HOPELESS). opcode=318, target=2, timing=9, probability1=100, resource = (EFFECT_POISON).
This way they would remain vulnerable to knockdown, such as from Wing Buffet. - Similarly, the EFF's for any effects (even damage) that operate through poison (Stinking Cloud, Cloudkill, Poison) would use the (EFFECT_POISON) parent resource.
While items that grant Poison Immunity would just use:opcode=318, target=2, timing=9, probability1=100, resource = (EFFECT_POISON).
No complication with Disease and Poison dealing the same damage type, or needing to list every spell individually with opcode 206. - Most every related spell would share the same EFF files, since all variation will be in the opcode 177 effect that calls them (saving throws, min/max hd/level, magic resistance, power, probability, etc...), not in the EFF (these fields should be left blank/zero in the EFF). Two sleep spells would use mostly the same EFFs, while a color spray would use another batch of EFF's (for unconsciousness). Some effects would have extras or alternates (mostly visual/audio/lighting effects for different spells).
- Because all "Display String" effects would use the same parent resource for a given purpose, there would be no need to look up string references for each game, or make sure every variation is blocked when granting immunity. Just select the EFF with the correct string for use in a spell, or block all appropriate variations with opcode 318 when granting immunity.
- EFF's called through opcodes 248, 249, and 272 would have to remain separate from these.
- Opcode 337 (remove effects by opcode) would falter against these, but all other effect removal would continue to work as they currently do
- Unlike opcode 337, direct effect removal effects (cure sleep, remove paralysis) do detect those effects in external EFF files.
- Spell school/type protection removal would still look at the school/type in opocde 177, not the EFF.
- Opcode 321 would still remove effects based on the parent resource of opcode 177, not the custom one defined in the EFF.
- Deflection/Reflection would not change, with the exception of opcode 198 (reflect specified effect), which was never really viable specifically because these opcodes have multiple purposes.
- This could be a small(addition) for some effects or require complete(replacement) overhaul for others.
SPLPROT based equipped effects:
On the item: opcode 318, target=1, parameter1/2=(SPLPROT), timing=2, resource=(CUSTOM) opcode 177, target =1, timing=2, resource=(EXTEFF) EXTEFF: opcode/parameters=(whatever), parent resource=(CUSTOM)The (SPLPROT) should be a match against anyone who should not get the effect.
The (SPLPROT) will only update when the item is equipped (same as opcode 177), so it would only be useful for mostly-static checks.
For example, you could properly restrict the Wizard Slayer from all magical weapons, then grant them an enchantment level equal to their proficiency level with any weapon they use (with or without the bonus to hit/damage). Maybe even a THAC0 bonus for zero-proficiency weapons so to offset the non-proficiency penalty so they retain a non-magical option against PfMW.
This specific idea would take a LOT of SPLPROT entries (dual-class mechanics force using a bit-wise check for proficiency), but they are not particularly in limited supply like SPLSTATEs. When dual-wielding, it would apply to all weapons of the same category, as the relative opcode(345) can't be more selective than that, and the +hit/damage can't be selective at all.
Or, if you were doing some type of 3E conversion:
Full Plate: opcode 318, target=1, parameter1/2=(Armor Proficiency>2), timing=2, resource=(CUSTOM) opcode 177, target =1, timing=2, resource=(EXTEFF1) opcode 177, target =1, timing=2, resource=(EXTEFF2) opcode 177, target =1, timing=2, resource=(EXTEFF3) EXTEFF1: opcode=278, parameter1=~-6~, parent resource=(CUSTOM) //THAC0 EXTEFF2: opcode=44, parameter1=~-3~, parent resource=(CUSTOM) // Strength EXTEFF3: opcode=15, parameter1=~-3~, parent resource=(CUSTOM) // Dexterity // Armor Check Penalty for non-proficiency with armor // SPLPROT could either be for an actual proficiency stat, re-purposed for Armor, or SPLSTATEs for each armor tier.
8
Comments
Though it shouldn't be the cause - you should alter opcode 318 to use timing mode 9, otherwise it will be removed by the various "Limited Effect" removals (death, export/pregen, Ctrl+R, opcode 17/BIT17[param2]).
* NI always shows (and saves) the parent resource field as capitalized (reason I didn't catch in it screenshots).
I have a question (let's consider Suppressing Arcane Spell Failure as an example): what is exactly ~ARCFAIL~? An ITM or a SPL (can't be an EFF for sure...)?
OK, thanks for clarifying, I think I understood now......
So, it's correct that resource is non-existent....
Now, speaking of your spell evasion:
1) I noticed that the parent resource (located at offset 0x94) of your EFFs is a SPL, while the resource (located at offset 0x30) of your 318 effects is an ITM -----> this doesn't seem to be a problem, but wanted to point it out in any case.....
2) You should tweak your code and remove opcode #318 (param1 = 0, param2 = 0) ----> you don't need this since you already have opcode #318 (param1 = STATE_NORMAL, param2 = STATE bit_eq specified value). Put here the Save vs. Breath and you're done
Otherwise, if you keep both this and EA >= ANYONE (Save vs. Breath), it may happen that a HELPLESS creature takes damage (correct) upon a successful Save vs. Breath (weird)
Tl;dr ----> EA >= ANYONE returns true even if the targeted CRE is HELPLESS
I'm not seeing a STATE_NORMAL check in the linked evasion code, just plenty of other state checks which could be combined. Checking STATE_NORMAL would be terrible, it would either match anything or nothing, depending on which comparison you use.
Aren't STATEs mutually exclusive? If you're STUNNED, then you're not NORMAL, right?
I mean, you're allowed a Save vs Breath if your state is NORMAL ----> if you fail it, then you're protected against the EFF...... Otherwise (e.g., your state is SLEEPING), you're allowed no save and are protected against the EFF.....
Am I missing something?
STATE_NORMAL is just the absence of every other STATE.
If you check for STATE_NORMAL (0x00000000):
So, if that's the case, then do you think that another relation (e.g., binary less/binary more) can solve the issue?
0x8015602f is just the cumulative value of all states checked by his evasion code.
If any are set, it will block both immunity effects, preventing the savingthrow display and the evasion.
Thank you very much! You basically need to use two EFFs, where the first one must protect you from the real immunity to "Sourcespell" upon a failed Save vs. Breath.
So, to sum up (let's consider Fireball as an example):
COPY_EXISTING ~SPWI304.spl~ ~override~ LPF ADD_SPELL_EFFECT INT_VAR insert_point = 0 opcode = 318 target = 2 parameter1 = 0x8015602f parameter2 = 138 duration = 1 STR_VAR resource = ~EFF1~ END<br><br>LPF ADD_SPELL_EFFECT INT_VAR insert_point = 1 opcode = 318 target = 2 parameter1 = 0x8015602f parameter2 = 138 duration = 1 STR_VAR resource = ~EFFSUB~ END LPF ADD_SPELL_EFFECT INT_VAR insert_point = 2 opcode = 177 target = 2 parameter2 = 2 duration = 1 STR_VAR resource = ~EXTEFF~ END LPF ADD_SPELL_EFFECT INT_VAR insert_point = 3 opcode = 177 target = 2 parameter2 = 2 duration = 1 STR_VAR resource = ~EXTEFF1~ END BUT_ONLY CREATE EFF ~EXTEFF~ WRITE_LONG 0x10 318 WRITE_LONG 0x14 2 WRITE_LONG 0x1c 252 // EVASION WRITE_LONG 0x20 110 // SPLSTATE = specified value WRITE_LONG 0x28 1 WRITE_SHORT 0x2c 100 WRITE_ASCII 0x030 ~EFFSUB~ #8 WRITE_LONG 0x040 BIT1 // Save vs. Breath WRITE_LONG 0x90 1 // Resource type: spell WRITE_ASCII 0x94 ~EFF1~ #8 // Parent resource CREATE EFF ~EXTEFF1~ WRITE_LONG 0x10 324 WRITE_LONG 0x14 2 WRITE_LONG 0x1c 252 // EVASION WRITE_LONG 0x20 110 // SPLSTATE = specified value WRITE_LONG 0x28 1 WRITE_SHORT 0x2c 100 WRITE_ASCII 0x030 ~SPWI304~ #8 WRITE_LONG 0x90 1 // Resource type: spell WRITE_ASCII 0x94 ~EFFSUB~ #8 // Parent resource
Moreover, how can I define my custom 0x8015602f? I guess I first need to add that entry to STATE.ids, right? I tried in this way but it's not working....
/** * Adds a new entry to a specified IDS file and returns its IDS value. * * INT_VAR minValue Minimum IDS value to consider. (Default: 0) * INT_VAR maxValue Maximum IDS value to consider. (Default: 255) * INT_VAR preferredValue Try this IDS value first if available. (Default: unset) * INT_VAR hexadecimal Set to nonzero to add IDS value in hexadecimal notation. (Default: 0) * STR_VAR idsFile The IDS file to add the entry to. * STR_VAR identifier The identifier name for the IDS value. Must not contain whitespace. * RET value The IDS value if entry has been added successfully. -1 if entry could not be added. */ DEFINE_ACTION_FUNCTION ADD_IDS_ENTRY INT_VAR minValue = 0 maxValue = 255 preferredValue = "-1" hexadecimal = 0 STR_VAR idsFile = "" identifier = "" RET value BEGIN OUTER_SET value = "-1" ACTION_IF (minValue < 0) BEGIN OUTER_SET minValue = 0 END ACTION_IF (maxValue < minValue) BEGIN OUTER_SET maxValue = minValue END ACTION_IF (~%idsFile%~ STRING_MATCHES_REGEXP ~.+\..+~ = 0) BEGIN OUTER_PATCH_SAVE idsFile ~%idsFile%~ BEGIN REPLACE_TEXTUALLY ~\(.+\)\.[^.]+~ ~\1~ END END ACTION_IF (FILE_EXISTS_IN_GAME ~%idsFile%.ids~) BEGIN // Try preferred value first OUTER_PATCH ~~ BEGIN PATCH_IF (preferredValue >= minValue AND preferredValue <= maxValue) BEGIN LOOKUP_IDS_SYMBOL_OF_INT retVal ~%idsFile%~ preferredValue PATCH_IF (~%retVal%~ STRING_EQUAL ~%preferredValue%~) BEGIN SET value = preferredValue END END END // Looking for available IDS slot ACTION_IF (value = "-1") BEGIN OUTER_PATCH ~~ BEGIN FOR (v = minValue; v <= maxValue; v += 1) BEGIN LOOKUP_IDS_SYMBOL_OF_INT retVal ~%idsFile%~ v PATCH_IF (~%retVal%~ STRING_EQUAL ~%v%~) BEGIN SET value = v SET v = maxValue + 1 END END END END // Falling back to preferred value if no free slot found ACTION_IF (value = "-1" AND preferredValue >= minValue AND preferredValue <= maxValue) BEGIN OUTER_SET value = preferredValue END // Adding new entry ACTION_IF (value != "-1") BEGIN ACTION_IF (hexadecimal) BEGIN LAF TO_HEX_NUMBER INT_VAR value = value RET hexNumber END OUTER_TEXT_SPRINT idsValue ~0x%hexNumber%~ END ELSE BEGIN OUTER_TEXT_SPRINT idsValue ~%value%~ END APPEND ~%idsFile%.ids~ ~%idsValue% %identifier%~ UNLESS ~%identifier%~ OUTER_SET value = IDS_OF_SYMBOL (~%idsFile%~ ~%identifier%~) ACTION_IF (value < minValue OR value > maxValue) BEGIN OUTER_SET value = "-1" END END END END /** * Converts any decimal number into a hexadecimal number */ DEFINE_ACTION_FUNCTION TO_HEX_NUMBER INT_VAR value = 0 // the decimal number minDigits = 1 // min. number of digits in return value (not counting sign) prefix = 0 // whether to return number with "0x" prefix RET hexNumber // returned as string without prefix BEGIN ACTION_IF (minDigits < 1) BEGIN OUTER_SET minDigits = 1 END ACTION_IF (minDigits > 8) BEGIN OUTER_SET minDigits = 8 END OUTER_TEXT_SPRINT hexNumber ~~ ACTION_DEFINE_ARRAY digit BEGIN ~0~ ~1~ ~2~ ~3~ ~4~ ~5~ ~6~ ~7~ ~8~ ~9~ ~a~ ~b~ ~c~ ~d~ ~e~ ~f~ END ACTION_IF (value < 0) BEGIN OUTER_SET signed = 1 OUTER_SET value = 0 - value END ELSE BEGIN OUTER_SET signed = 0 END OUTER_WHILE (value != 0) BEGIN OUTER_SET curDigit = value BAND 0xf OUTER_SET value = value BLSR 4 OUTER_TEXT_SPRINT hexDigit $EVAL digit(~%curDigit%~) OUTER_TEXT_SPRINT hexNumber ~%hexDigit%%hexNumber%~ END OUTER_WHILE (STRING_LENGTH ~%hexNumber%~ < minDigits) BEGIN OUTER_TEXT_SPRINT hexNumber ~0%hexNumber%~ END ACTION_IF (prefix) BEGIN OUTER_TEXT_SPRINT hexNumber ~0x%hexNumber%~ END ACTION_IF (signed) BEGIN OUTER_TEXT_SPRINT hexNumber ~-%hexNumber%~ END END DEFINE_PATCH_FUNCTION TO_HEX_NUMBER INT_VAR value = 0 minDigits = 1 prefix = 0 RET hexNumber BEGIN INNER_ACTION BEGIN LAF TO_HEX_NUMBER INT_VAR value = value minDigits = minDigits prefix = prefix RET hexNumber END END END OUTER_SET my_custom_state = 0x00000001 BOR 0x00000002 BOR 0x00000004 BOR 0x00000008 LAF ADD_IDS_ENTRY INT_VAR hexadecimal = my_custom_state STR_VAR idsFile = ~STATE~ identifier = ~MY_CUSTOM_STATE~ END OUTER_SET my_custom_state = IDS_OF_SYMBOL(~STATE~ ~MY_CUSTOM_STATE~)<br> COPY_EXISTING ~SPWI304.spl~ override LPF ADD_SPELL_EFFECT INT_VAR insert_point = 0 opcode = 318 target = 2 parameter1 = my_custom_state parameter2 = 138 duration = 1 STR_VAR resource = ~EFF1~ END LPF ADD_SPELL_EFFECT INT_VAR insert_point = 1 opcode = 318 target = 2 parameter1 = my_custom_state parameter2 = 138 duration = 1 STR_VAR resource = ~EFFSUB~ END
sorry, the forum is currently making it a PITA to read formatted posts, but the first part looks good.
You don't need to add anything to STATE.IDS unless you are just wanting it to display in NI. An integer/hex is just as valid as a label. However, that function won't work for STATEs, it's not that kind of file.
This is all you need:
OK, good to know I tweaked your code via adding prefix = 1 when launching TO_HEX_NUMBER; in so doing, NI no longer says that STATE.ids is corrupted (or something like that......)
Yes, the discussion has mainly been about suppressing the distracting saving throw feedback when it's not necessary.
As for STATE_CONFUSED, IIRC there is no way to set it's value with CLONE_EFFECT or ALTER_EFFECT, as it will always result in a negative value.
Is this issue related to WeiDU?
The last bit of a signed integer determines if it is positive or negative, which just happens to be STATE_CONFUSED.
It can be patched to check STATE_CONFUSED, just not with those functions.