Hi All,
I was doing some script work on module 2 of my campaign and observed that some scripts I was using were not recognising an item offering “immunity” to certain saving throws that I was asking the scripts to make. I thrashed this out with KevL, who patiently helped me get to the bottom of this. So my thanks goes to him for helping me get there.
Anyway, the bottom line is, when it comes to detecting immunities using the “My” functions, MySavingThrow (and MyResistSpell), they rely on the resultant effect rather than check any items carried. So, for example, when writing our own scripts that use these functions, the immunity check can sometimes be missed if we (for whatever reason) alter the end result.
For example, I have a script that the PCs have to save versus fear, and if they fail have a shaken type effect, which applies a penalty to attack and saves. The point is, the final applied effect I have written does not apply the EffectFrightened, which is the effect that is hard-coded to work with the “My” functions mentioned above. So, when I use the “My” functions in a way similar to the way you see them in spells, they fail to recognise the altered spell effect and so any immunity checks for “fear” fail and the PC with an item that makes them immune to “fear” fails to do so.
The bottom line is that any scripts you have written that use the “My” functions will only work properly (especially with respect to immunities) if your scripts end up applying the effect the functions were expecting, so that the hard-code immunity check is fired correctly.
I am currently working on a LBSavingThrowResult function script that I hope will work with custom scripts that require immunity checks (which I imagine would include most of us). I do have one already, but I want to take a closer look at it to see if I can improve on it before posting it. At least you are informed of this “issue” now.
NB: All testing has only been with the Fear (EffectFrightened) to date.
Here are my results …
This script woks for me as a “complete” replacement for the two “My” functions …
It has been tested with a few effects, but should be good with all. Just double check results to be sure you are happy with them.
NB: UPDATE: Can be used with all effect types, but care should be taken when asking for a poison save check - MUST pass the SAVING_THROW_TYPE_POISON.
//////////////////////////////////////////////////////////////////////////////////
// SAVING THROW WITH VFX DEPENDING UPON IMMUNE OR RESISTED OR FAILED (LANCE BOTELLE)
// SIMPLY RETURNS 0 ON SAVE FAIL, RETURNS 1 ON SAVE MADE, RETURNS 2 ON IMMUNE (WITH VFXs)
// NB 1: ANY POISON SAVE IGNORES ANY DC PASSED & USES THE POISON.2DA REF INSTEAD (iSupMess IS MADE 1 IF NOT IMMUNE)
// NB 2: Reports integer only. Any result from that (like death) must be handled seperately.
// NB 3: This does not test for spell cast timings between effect and requirement.
// Therefore, the moment this function is called, the check is made with VFX.
// oTarget is the PC or creature making the saving throw.
// iSAVETYPE CAN BE: SAVING_THROW_FORT, SAVING_THROW_REFLEX OR SAVING_THROW_WILL
// iSUBTYPE IS SAVING_THROW_TYPE_XXXX FORMAT (SAVING_THROW_TYPE_ALL IS DEFAULT)
// iDC IS THE DC TO SAVE AGAINST (DEFAULT 15) USE iSupMess = 1 IF USING SPECIFIC FEEDBACK.
// According to official source: If using with an area of effect script (On Enter
// On Exit or On Heartbeat) wemust pass GetAreaOfEffectCreator into oSaveVersus.
//////////////////////////////////////////////////////////////////////////////////
int LBSavingThrowResult(int iSAVETYPE, object oTarget, int iDC = 15, int iSUBTYPE = SAVING_THROW_TYPE_ALL, object oSaveVersus = OBJECT_SELF, int iSupMess = 0);
int LBSavingThrowResult(int iSAVETYPE, object oTarget, int iDC = 15, int iSUBTYPE = SAVING_THROW_TYPE_ALL, object oSaveVersus = OBJECT_SELF, int iSupMess = 0)
{
// LIMIT THE DC
if (iDC<1){iDC = 1;}else if (iDC > 255){iDC = 255;}
// NOT A PLAYER SPELL INTERCEPT
int iRESULT = ResistSpell(oSaveVersus, oTarget);
// NOW CHECK FOR OTHER IMMUNITIES/RESISTANCES OR SAVES
if(iRESULT < 1)
{
// POISON HAS TO BE HANDLED VIA HARD-CODE IF NO IMMUNITY
if(iSUBTYPE == SAVING_THROW_TYPE_POISON)
{
if(GetIsImmune(oTarget, IMMUNITY_TYPE_POISON, oSaveVersus)){iRESULT = 2;}
else{iSupMess = 1;}
}
// DETERMINE HOW PC SAVES FIRST (DOUBLE CHECK ANY IMMUNITY VIA SPELL AFTER ANY DEFAULT)
else if(iSAVETYPE == SAVING_THROW_FORT){iRESULT = FortitudeSave(oTarget, iDC, iSUBTYPE, oSaveVersus);}
else if(iSAVETYPE == SAVING_THROW_REFLEX){iRESULT = ReflexSave(oTarget, iDC, iSUBTYPE, oSaveVersus);}
else if(iSAVETYPE == SAVING_THROW_WILL){iRESULT = WillSave(oTarget, iDC, iSUBTYPE, oSaveVersus);}
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// PLAY A VFX ACCORDING TO RESULT
//////////////////////////////////////////////////////////////////////////////////////////////////
// PC SAVES
if(iRESULT == 1)
{
effect eSR = EffectVisualEffect( VFX_DUR_SPELL_SPELL_RESISTANCE ); // uses NWN2 VFX
ApplyEffectToObject(DURATION_TYPE_INSTANT, eSR, oTarget);
if(iSupMess == 0)
{
SendMessageToPC(GetFirstPC(FALSE), "<i>" + GetName(oTarget) + " shakes off the effect!</i>");
}
}
// PC IMMUNE
else if(iRESULT == 2)
{
// BRIEF INDICATION
effect eGlobe = EffectVisualEffect( VFX_DUR_SPELL_GLOBE_INV_LESS ); // uses NWN2 VFX
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eGlobe, oTarget, 0.5);
if(iSupMess == 0)
{
SendMessageToPC(GetFirstPC(FALSE), "<i>" + GetName(oTarget) + " is immune to the effect!</i>");
}
}
// PC FAILS
else
{
effect eSR = EffectVisualEffect( VFX_DUR_MIND_AFFECTING_FEAR ); // uses NWN2 VFX
ApplyEffectToObject(DURATION_TYPE_INSTANT, eSR, oTarget);
if(iSupMess == 0)
{
SendMessageToPC(GetFirstPC(FALSE), "<i>" + GetName(oTarget) + " succumbs to the effect!</i>");
}
}
// INFORM RESULT
return iRESULT;
}
Again, this is only currently tested with the fear spell that I have allowed to be changed effect with a lower game setting difficulty. However, as the function checks for general immunity if the spell resistance checks fail, then it still works.