@chimeric For some reason the ADD_ITEM_EFFECT requires specifying the ability type for weapons:
LPF ADD_ITEM_EFFECT
INT_VAR
type = 1 // Melee
opcode = 328
target = 2
timing = 0
duration = 1
parameter2 = 366
special = 1
END
You can also skip all those prof/cat/string checks, the only check you need to make here is field 0x74. If it is not '1', its not a weapon, and specifying the type will further restrict it to melee weapons. Just make sure the item file is at least that large (FILE_SIZE > 0x73) before reading it.
Okay, that worked completely fine with weapons. The next step is to patch damaging spells the same way. I'm targeting the OFFENSIVEDAMAGE type. Don't know what the minimum file size is with spells, but that doesn't really matter. Here:
COPY_EXISTING_REGEXP ~.*\.spl~ override
READ_BYTE 0x27 type
PATCH_IF (type = 10) THEN BEGIN
LPF ADD_SPELL_EFFECT
INT_VAR
opcode = 328
target = 2
timing = 0
duration = 1
parameter2 = 366
special = 1
END
END
BUT_ONLY
Somehow this doesn't quite work. The effect gets added alright, but the spell state is not recognized, because "special = 1" (IWD 2 mode) doesn't go through. Any idea why?
Somehow this doesn't quite work. The effect gets added alright, but the spell state is not recognized, because "special = 1" (IWD 2 mode) doesn't go through. Any idea why?
Outdated version of weidu maybe? IIRC there was either a typo in that function or just complete lack of support preventing it from accepting values for the special field at one point. I think at least as far back as v2.39.
But it applied the patching properly with weapons. Not with the spells, for some reason. In one case, it recognizes the Special field, as it did also for that global equipped effect you gave me the code for. But with this ADD_SPELL_EFFECT, not. I'll try to find an update for Weidu. If there isn't one, I can still try to bypass this problem and add a Cast Spell to everything instead, and that spell will have a custom spell state application I'll enter by hand.
I'm having some other issues with custom states. Apparently they don't behave properly in all situations for high position numbers. The last "official" position in SPLSTATE.IDS (BG:EE without SoD) is 255, CYNICISM_EQUIPPED. I don't know how high one can go after this and get proper responses with CheckSpellState. My "equipped" states for weapons, what I've asked the code for a week ago, range from 348 to 363, rather high to avoid clashing with anything. And scripts notice them, but weirdly... For example:
returns true if a club or a staff is equipped and selected, and returns false if I deselect it, just as intended. But If I ask about another of these states, one that is off at the moment, like so:
CheckSpellState(LastSummonerOf(Myself),WAKI_EQUIPPED) then this also returns true. Official states don't do this, so I tried planting WAKI_EQUIPPED in the empty stretch of the file between 126, WIZARD_SHIELD, and 249, PRONE. And then the trigger stopped returning true when the state was not on. This means I'm going to have to fit my states in the working range of SPLSTATE.IDS, and I don't know, as I said, how far above 255 I can go there, or, if I stick my states in the gap, the positions may well be occupied by extensions or other mods. An alternative would be to draw on the official and irrelevant spell states instead of my own "equipped" states. For example, I could make wakizashis apply constantly HOPELESSNESS or EYE_OF_THE_MIND. Would that have any side effects? The spell state HELD doesn't actually hold a character, does it?
SPLSTATE values 256 - 6271 (with a few gaps) are reserved by the engine, set by variuos STATES, STATS, current BARDSONG, and EXE version. Setting them manually has so far no known effect (with 2 visual-only exceptions). You can read more here: https://forums.beamdog.com/discussion/comment/901480/#Comment_901480
You should always use a dynamically generated SPLSTATE for custom entries, determined at installation from those available, not preset values. There is no reason to "reserve" SPLSTATEs.
Only a few of the SPLSTATES 0-255 have hardcoded features, the rest are arbitrary, among them: DISEASED PRONE EVASION SUPPRESS_HP_INFO DOESNT_AWAKEN_ON_DAMAGE maybe SNEAK_ATTACK_IMMUNITY (I made a more complete list is on G3, but I don't remember offhand).
Okay, and how about factoring Strength into damage of spell attacks simulating strikes? You've said it's possible. Did you mean a series of Apply Effects List - > STR > X, or is there some more congenial method?
For AoE's this is a subspell, for single-target spells this is the spell itself: CheckSpell (One ability header, min level[1], projectile[1]):
// For each Strength value:
Opcode 326 (Apply Effects List)
Target: 9 (Original Caster)
Parameter1: (Strength value)
Parameter2: STR_CHECK
Resource: (MidSpell for this STR value)
For each STR value: MidSpell (Ability header for every caster level [1 - CAP], projectile[1]):
// One for each ability header:
Opcode 146 (Cast Spell)
Target: 1 (Self)
Parameter1: (level entry in Subspell for this Caster Level & STR value combination)
Parameter2: 2 (Cast Instantly (at level))
Resource: Subspell for this STR value
Subspell (Ability header for every Caster Level + STR value combination needed): Projectile: [spell projectile] for single-target or [None(1)] for AoE's
(actual spell effects)
Explanation about casting level:
Lets say this spell scales level 1-30. The Midspell for STR=1 will have abilities with minlevel 1-30. The casting level of their 146 effects will be 1-30. The effects for STR=1 will occupy ability levels 1-30 of (Subspell). The Midspell for STR=2 will have abilities with minlevel 1-30. The casting level of their 146 effects will be 31-60. The effects for STR=2 will occupy ability level 31-60 of (Subspell). The Midspell for STR=2 will have abilities with minlevel 1-30. The casting level of their 146 effects will be 61-90. The effects for STR=2 will occupy ability level 61-90 of (Subspell). etc... This way, all effects originate from the same resource, making it simple to prevent stacking or provide immunity with opcodes 206/321.
Note: Normal spellcasting has a caster level limit of 255, this does not apply when you specify the casting level through opcodes.
For repeating area-effect spells (i.e. cloudkill), its complicated. You can use the above, but it will require the caster remain in the same area as the spell, and it will use the current STR score every time it triggers, rather than the STR score at the time of casting. Technically it also does this for instant-area-effect spells, but normally its not a noticeable issue with them. It can be done properly, but I haven't formulated the process yet.
I don't recommend attempting to factor in Extra Strength, it should be doable, but the payoff won't likely be worth the effort.
For anyone interested in 3E mechanics, this can be used to overhaul the spell system to incorporate a casters ability-score modifier to affect saving throws for their spells. Though you may be better off rebuilding the spells from scratch than trying to patch what exists.
This is terribly complicated. The part about adding a new option to SPLTPROT.2DA can be used as written? That file is what Apply Effects List uses, yes?
As for the rest... I don't think I need it. I don't want to patch all spells that do damage. I just need my attack-simulating spells to include a series of A.E.L. effects reflecting the caster's stats. I know how to do that, Self-targeted A.E.L.s inside a Living actor spell take the measure of the caster and send the extra damage to Living actor. It's just a lot of work to nest those checks when there is no straight = for a stat.
By the way, you've told me how to patch spells by their casting time, but the offset is going to be different for every ability, and there is no telling how many there will be. Might be just one, might be one for every casting level. If I try to read them without knowing the length of the file... scatch that, they all have the same casting time in the spells that we know. And I can read the first one.
This is terribly complicated. The part about adding a new option to SPLTPROT.2DA can be used as written? That file is what Apply Effects List uses, yes?
As for the rest... I don't think I need it. I don't want to patch all spells that do damage. I just need my attack-simulating spells to include a series of A.E.L. effects reflecting the caster's stats. I know how to do that, Self-targeted A.E.L.s inside a Living actor spell take the measure of the caster and send the extra damage to Living actor.
As long as it works under the circumstances (which the simpler the spell is the more likely it should work). The extra redirect become necessary as the spell becomes more complex, as the game starts to lose track of targets the further you go down that rabbit hole.
And the new line for SPLPROT won't throw off my existing spell effects with that opcode, will it?
It shouldn't affect anything that is already in the file. It either adds the new entry to the end, or replaces one of the empty rows if it detects one, maintaining row count for existing entries.
So how is this addition going to work on others' machines? First I tell Weidu to patch their file as you wrote, then I'll copy over my A.E.L.s with the =, the spells I created after I ran the code on my setup, and it should show for them as it does for me?
All right. I think I'm in the home run with this. By the way, my Near Infinity doesn't want to recognize WILDMAGE in KIT.IDS. It sees the entry and I can set it, but it shows as "Unknown" - only with a number. May not be a problem, but is this a permanent feature of NI, or is it because I'm using an old version? This version has some convenient features later removed, but it might have a few left-over bugs.
The value will not be the same on different installs, since their is no way to know what entries they will already be using. It's fine for pre-existing entries(no-one should be changing those), but not custom ones.
First you patch their SPLPROT.2DA, adding an entry for STR=. Then you either patch your files using the value it returns when you copy them to the override. Or, if you've already create the files with the AEL effect on your side, you can also just write down the offset(s) the STR= value is at in the spell, and write over just that offset after adding the entry to SPLPROT.2da.
DEFINE_ACTION_FUNCTION ADD_SPLPROT (code) END
LAF ADD_SPLPROT INT_VAR stat = 36 value = ~-1~ relation = 1 RET STR_CHECK = new_row END
// Then:
COPY ~myfile.spl~ override
SAY NAME1 @###
LPF ADD_SPELL_EFFECT
INT_VAR
opcode = 326
target = 2
timing = 0
duration = 1
parameter2 = STR_CHECK
special = 1
END
// Or if the effect is already there:
COPY ~myfile.spl~ override
SAY NAME1 @###
WRITE_LONG 0xa2 STR_CHECK // use the offset for the parameter2 of the AEL effects.
WRITE_LONG 0xd2 STR_CHECK // use the offset for the parameter2 of the AEL effects.
WRITE_LONG 0x102 STR_CHECK // use the offset for the parameter2 of the AEL effects.
I don't know anything about that WILDMAGE issue, but your assumption is likely.
Okay... one thing after another. But thanks a lot, I appreciate it. Here I was patching my SPLs, going by the casting time of their first spell ability, and I got an installation error from a Winter Wolf pelt SPL. Don't ask. It's called CDDETECT.SPL. That one doesn't have any abilities or global effects, but it's there. I guess I'm going to need a way to select only files with at least one spell ability.
INT_VAR
opcode = 328
target = 2
timing = 0
duration = 1
parameter2 = STR_CHECK
special = 1
END
You mean 326, right? 328 is Set Spell State, that was a different issue. Here it should be 326 pointing to the Strength stat as a filter...
You can populate RACE.ids and RACETEXT.2da(SODRACE.2da) up to values 255, any race that has all fields in both will show up at chargen. RACEFEAT.2da controls infravision, however, no other race-based files accept other race entries, instead they use the same values as Humans or the default (except Tiefling, which is treated as an Elf). Most of it is only a problem during Chargen, which can be worked around by modding the UI.
I guess I'm going to need a way to select only files with at least one spell ability.
Also I'd like to take advantage of exceptional strength. If it's not terribly difficult - for you, I mean. What what I need to change in your = code for that? Also would there be a doubling-up of bonuses for Str 18, regular, and exceptional Strength, if used on a fighter?
Also I'd like to take advantage of exceptional strength. If it's not terribly difficult - for you, I mean. What what I need to change in your = code for that? Also would there be a doubling-up of bonuses for Str 18, regular, and exceptional Strength, if used on a fighter?
You will need all of these setup checks added to SPLPROT:
LAF ADD_SPLPROT INT_VAR stat = 36 value = ~-1~ relation = 1 RET STR_CHECK = new_row END // Strength = 'Parameter1'
LAF ADD_SPLPROT INT_VAR stat = 36 value = 18 relation = 5 RET STR_N18 = new_row END // Strength != 18
LAF ADD_SPLPROT INT_VAR stat = 37 value = 0 relation = 5 RET EXSTR_N0 = new_row END // EXStrength != 0
LAF ADD_SPLPROT INT_VAR stat = 37 value = 1 relation = 3 RET EXSTR_L1 = new_row END // EXStrength < 1
LAF ADD_SPLPROT INT_VAR stat = 37 value = 50 relation = 2 RET EXSTR_G50 = new_row END // EXStrength > 50
LAF ADD_SPLPROT INT_VAR stat = 0x103 value = EXSTR_L1 relation = EXSTR_G50 RET STR_1_50 = new_row END
// EXStrength != 1-50
LAF ADD_SPLPROT INT_VAR stat = 37 value = 51 relation = 3 RET EXSTR_L51 = new_row END // EXStrength < 50
LAF ADD_SPLPROT INT_VAR stat = 37 value = 75 relation = 2 RET EXSTR_G75 = new_row END // EXStrength > 76
LAF ADD_SPLPROT INT_VAR stat = 0x103 value = EXSTR_L51 relation = EXSTR_G75 RET STR_51_75 = new_row END
// EXStrength != 51-75
LAF ADD_SPLPROT INT_VAR stat = 37 value = 76 relation = 3 RET EXSTR_L76 = new_row END // EXStrength < 76
LAF ADD_SPLPROT INT_VAR stat = 37 value = 90 relation = 2 RET EXSTR_G90 = new_row END // EXStrength > 90
LAF ADD_SPLPROT INT_VAR stat = 0x103 value = EXSTR_L76 relation = EXSTR_G90 RET STR_76_90 = new_row END
// EXStrength != 76-90
LAF ADD_SPLPROT INT_VAR stat = 37 value = 91 relation = 3 RET EXSTR_L91 = new_row END // EXStrength < 91
LAF ADD_SPLPROT INT_VAR stat = 37 value = 99 relation = 2 RET EXSTR_G99 = new_row END // EXStrength > 100
LAF ADD_SPLPROT INT_VAR stat = 0x103 value = EXSTR_L91 relation = EXSTR_G99 RET STR_91_99 = new_row END
// EXStrength != 91-99
LAF ADD_SPLPROT INT_VAR stat = 37 value = 100 relation = 5 RET EXSTR_N100 = new_row END // EXStrength != 100
LAF ADD_SPLPROT INT_VAR stat = 0x104 value = STR_N18 relation = EXSTR_N0 RET STR_CHECK_18 = new_row END
// Strength = 18/0 (not ((not STR18) or (not EX00)))
LAF ADD_SPLPROT INT_VAR stat = 0x104 value = STR_N18 relation = STR_1_50 RET STR_CHECK_18_1_50 = new_row END
// Strength = 18/1 - 18/50
LAF ADD_SPLPROT INT_VAR stat = 0x104 value = STR_N18 relation = STR_51_75 RET STR_CHECK_18_51_75 = new_row END
// Strength = 18/51 - 18/75
LAF ADD_SPLPROT INT_VAR stat = 0x104 value = STR_N18 relation = STR_76_90 RET STR_CHECK_18_76_90 = new_row END
// Strength = 18/76 - 18/90
LAF ADD_SPLPROT INT_VAR stat = 0x104 value = STR_N18 relation = STR_76_90 RET STR_CHECK_18_91_99 = new_row END
// Strength = 18/90 - 18/99
LAF ADD_SPLPROT INT_VAR stat = 0x104 value = STR_N18 relation = STR_100 RET STR_CHECK_18_100 = new_row END
// Strength = 18/100
Add the A.E.L. checks for Strength values 1-17 and 19-25 as previous using %STR_CHECK%, then add an A.E.L. for each of these checks (Parameter1 will be irrelevant for these):
Another question, to any takers. I'm giving some special abilities to classes, and I want to emulate the Shadowdancer, how he can hide in plain sight. What are the mechanics of that?
@chimeric: Hide in Plain Sight is currently hardcoded; only the Shadowdancer kit can do it. But any thief can hide in plain sight or set traps in the middle of combat provided that he or she is blinded. Unfortunately, a blind thief still suffers all of the normal penalties and restrictions of blindness.
Oho-ho! That's perfect. I'm still going to look at the shadowdancer, but the trick with blindness (to expire in 1 second) should be it. (How is it that one is not seen when one just can't see anybody else? Is this a sort of "nah-nah-nah, looking through my fingers" sort of logic? If Hide is normally prevented by being Seen, then does blindness also makes one not Seen?)
Oho-ho! That's perfect. I'm still going to look at the shadowdancer, but the trick with blindness (to expire in 1 second) should be it. (How is it that one is not seen when one just can't see anybody else? Is this a sort of "nah-nah-nah, looking through my fingers" sort of logic? If Hide is normally prevented by being Seen, then does blindness also makes one not Seen?)
Stealth is blocked by being able to see enemies while not already invisible. You can be blind or they can be invisible(to you). It might even work if they have opcode 101 against you. You can also use opcode 262(visual range bonus) in place of Blindness to avoid complications with immunities.
Okay. Sounds like the way to go. I'm going to have to use a hot key for another part of this mod... I can't get them to work, except what's set inside the game options. I assign a trigger to an unoccupied letter for my scripts, I free a letter in the options for my scripts, and there is still no response.
Okay. Sounds like the way to go. I'm going to have to use a hot key for another part of this mod... I can't get them to work, except what's set inside the game options. I assign a trigger to an unoccupied letter for my scripts, I free a letter in the options for my scripts, and there is still no response.
Comments
For some reason the ADD_ITEM_EFFECT requires specifying the ability type for weapons: You can also skip all those prof/cat/string checks, the only check you need to make here is field 0x74. If it is not '1', its not a weapon, and specifying the type will further restrict it to melee weapons. Just make sure the item file is at least that large (FILE_SIZE > 0x73) before reading it.
I'm having some other issues with custom states. Apparently they don't behave properly in all situations for high position numbers. The last "official" position in SPLSTATE.IDS (BG:EE without SoD) is 255, CYNICISM_EQUIPPED. I don't know how high one can go after this and get proper responses with CheckSpellState. My "equipped" states for weapons, what I've asked the code for a week ago, range from 348 to 363, rather high to avoid clashing with anything. And scripts notice them, but weirdly... For example: returns true if a club or a staff is equipped and selected, and returns false if I deselect it, just as intended. But If I ask about another of these states, one that is off at the moment, like so:
CheckSpellState(LastSummonerOf(Myself),WAKI_EQUIPPED)
then this also returns true. Official states don't do this, so I tried planting WAKI_EQUIPPED in the empty stretch of the file between 126, WIZARD_SHIELD, and 249, PRONE. And then the trigger stopped returning true when the state was not on. This means I'm going to have to fit my states in the working range of SPLSTATE.IDS, and I don't know, as I said, how far above 255 I can go there, or, if I stick my states in the gap, the positions may well be occupied by extensions or other mods. An alternative would be to draw on the official and irrelevant spell states instead of my own "equipped" states. For example, I could make wakizashis apply constantly HOPELESSNESS or EYE_OF_THE_MIND. Would that have any side effects? The spell state HELD doesn't actually hold a character, does it?
You should always use a dynamically generated SPLSTATE for custom entries, determined at installation from those available, not preset values. There is no reason to "reserve" SPLSTATEs.
Only a few of the SPLSTATES 0-255 have hardcoded features, the rest are arbitrary, among them:
DISEASED
PRONE
EVASION
SUPPRESS_HP_INFO
DOESNT_AWAKEN_ON_DAMAGE
maybe SNEAK_ATTACK_IMMUNITY
(I made a more complete list is on G3, but I don't remember offhand).
- For instant-area-effect spells only:
- For AoE's this is a subspell, for single-target spells this is the spell itself:
- For each STR value:
- Subspell (Ability header for every Caster Level + STR value combination needed):
Explanation about casting level:AoE spell (One ability header, min level[1]):
CheckSpell (One ability header, min level[1], projectile[1]):
MidSpell (Ability header for every caster level [1 - CAP], projectile[1]):
Projectile: [spell projectile] for single-target or [None(1)] for AoE's (actual spell effects)
The Midspell for STR=1 will have abilities with minlevel 1-30. The casting level of their 146 effects will be 1-30. The effects for STR=1 will occupy ability levels 1-30 of (Subspell).
The Midspell for STR=2 will have abilities with minlevel 1-30. The casting level of their 146 effects will be 31-60. The effects for STR=2 will occupy ability level 31-60 of (Subspell).
The Midspell for STR=2 will have abilities with minlevel 1-30. The casting level of their 146 effects will be 61-90. The effects for STR=2 will occupy ability level 61-90 of (Subspell).
etc...
This way, all effects originate from the same resource, making it simple to prevent stacking or provide immunity with opcodes 206/321.
For repeating area-effect spells (i.e. cloudkill), its complicated. You can use the above, but it will require the caster remain in the same area as the spell, and it will use the current STR score every time it triggers, rather than the STR score at the time of casting. Technically it also does this for instant-area-effect spells, but normally its not a noticeable issue with them. It can be done properly, but I haven't formulated the process yet.
I don't recommend attempting to factor in Extra Strength, it should be doable, but the payoff won't likely be worth the effort.
For anyone interested in 3E mechanics, this can be used to overhaul the spell system to incorporate a casters ability-score modifier to affect saving throws for their spells. Though you may be better off rebuilding the spells from scratch than trying to patch what exists.
As for the rest... I don't think I need it. I don't want to patch all spells that do damage. I just need my attack-simulating spells to include a series of A.E.L. effects reflecting the caster's stats. I know how to do that, Self-targeted A.E.L.s inside a Living actor spell take the measure of the caster and send the extra damage to Living actor. It's just a lot of work to nest those checks when there is no straight = for a stat.
By the way, you've told me how to patch spells by their casting time, but the offset is going to be different for every ability, and there is no telling how many there will be. Might be just one, might be one for every casting level. If I try to read them without knowing the length of the file... scatch that, they all have the same casting time in the spells that we know. And I can read the first one.
All right. I think I'm in the home run with this. By the way, my Near Infinity doesn't want to recognize WILDMAGE in KIT.IDS. It sees the entry and I can set it, but it shows as "Unknown" - only with a number. May not be a problem, but is this a permanent feature of NI, or is it because I'm using an old version? This version has some convenient features later removed, but it might have a few left-over bugs.
First you patch their SPLPROT.2DA, adding an entry for STR=.
Then you either patch your files using the value it returns when you copy them to the override. Or, if you've already create the files with the AEL effect on your side, you can also just write down the offset(s) the STR= value is at in the spell, and write over just that offset after adding the entry to SPLPROT.2da.
I don't know anything about that WILDMAGE issue, but your assumption is likely.
Add the A.E.L. checks for Strength values 1-17 and 19-25 as previous using %STR_CHECK%, then add an A.E.L. for each of these checks (Parameter1 will be irrelevant for these): It will ignore exceptional strength if Strength isn't 18, so there will only be a single match.
If you have altered the Exceptional Strength ranges, you will have to alter/add/remove some of these check s ranges accordingly.
You can be blind or they can be invisible(to you). It might even work if they have opcode 101 against you.
You can also use opcode 262(visual range bonus) in place of Blindness to avoid complications with immunities.