Immunity to Epic Spells for creatures?
#1
Posté 06 mars 2012 - 09:46
How can you make creatures immune to these spells/feats, and or make them make their saves to them?
#2
Posté 06 mars 2012 - 09:53
#3
Posté 06 mars 2012 - 09:54
#4
Posté 06 mars 2012 - 10:10
I can script but really it take me a long time to figure things out.
#5
Posté 06 mars 2012 - 11:00
There also is an example in the CSL library i am maintaining more relevant to NWN2 named _SCDexSpellHooks.nss but i have a lot of custom things going on.
in module loaded
#include "x2_inc_switches"
void main()
{
SetModuleOverrideSpellscript("_SCDexSpellHooks"); // THIS RUNS BEFORE ANY SPELL IS CAST AND ALLOWS YOU TO ABORT THE CALL
}
excerpt from my spellhook
/*
This file is called each time a hook is done.
Bascially the code here, and a few other scripts is run by each spell before it is run
*/
#include "_HkSpell"
#include "_CSLCore_Player"
// includes function to stop the spell script from firing
#include "x2_inc_switches"
//#include "_SCUtility"
void SCfizzleSpell( string sMessage = "Spell Fizzled", object oCaster = OBJECT_SELF );
void SCIncStack(object oCaster, int nInc = 1, int nStackLimit = 3);
int SCSpellStack(int iSpell, object oCaster);
//Note that this follows the reasoning that things should be permitted
void main()
{
//if ( GetIsSinglePlayer() ) {DEBUGGING = 0;}
//DEBUGGING = GetLocalInt( GetModule(), "DEBUGLEVEL" );
//if ( GetLocalInt( OBJECT_SELF, "SC_TESTER" ) )
//{
// SendMessageToPC( OBJECT_SELF, "Running SpellHook Now for "+IntToString(GetSpellId()));;
//}
//if ( GetLocalInt( OBJECT_SELF, "SC_TESTER" ) )
//{
// SendMessageToPC( OBJECT_SELF, "Spellhook:"+SCCacheStatsToString( OBJECT_SELF ) );
//}
// note that HKPERM_damagemodtype sets it for multiple castings...
//this does random damage types
//SetLocalInt( OBJECT_SELF, "HKTEMP_damagemodtype", CSLPickOneInt(DAMAGE_TYPE_COLD, DAMAGE_TYPE_FIRE, DAMAGE_TYPE_ACID, DAMAGE_TYPE_ELECTRICAL, DAMAGE_TYPE_SONIC, DAMAGE_TYPE_NEGATIVE, DAMAGE_TYPE_POSITIVE ) );
//if (DEBUGGING >= 8) { CSLDebug( "Hook: Damage Set to <color=red>"+CSLDamagetypeToString( GetLocalInt( OBJECT_SELF, "HKTEMP_damagemodtype"))+"</color>" ); }
// int nSpellDC=GetSpellSaveDC();
object oTarget = HkGetSpellTarget();
object oCaster = OBJECT_SELF;
int iSpellId=GetSpellId();
if (DEBUGGING >= 4) { CSLDebug( "_SCDexSpellHooks: Caster = "+GetName( oCaster )+" Target = "+GetName( oTarget )+" Spell="+IntToString( iSpellId ), oCaster ); }
int iAttributes = CSLGetSpellAttributes( oCaster );
//CSLDebug( CSLCharacterStatsToString( oCaster ) );
if (DEBUGGING >= 4) { CSLDebug( "_SCDexSpellHooks: 2", oCaster ); }
//SendMessageToPC( OBJECT_SELF, "Spellhook:"+SCCacheStatsToString( OBJECT_SELF ) );
if ( !GetIsObjectValid(GetAreaFromLocation(GetLocation(oCaster))) )
{
SCfizzleSpell("Invalid Area.", oCaster );
SetLocalString( oCaster, "CSL_ERRORSTRING", "Invalid Area" );
return;
}
if ( CSLGetIsDM( oCaster, FALSE ) ) // || GetIsDMPossessed( oCaster )
{
if (DEBUGGING >= 6) { CSLDebug( "Ending SpellHook, DM's are Not Restricted by This" ); }
return; // DM's can do anything without regard for this
}
if (DEBUGGING >= 4) { CSLDebug( "_SCDexSpellHooks: 3", oCaster ); }
if ( !HkCheckIfCanCastSpell( oCaster, iSpellId, GetLastSpellCastclass() ) )
{
string sErrorMessage = GetLocalString( oCaster, "CSL_ERRORSTRING" );
if ( sErrorMessage != "" )
{
SCfizzleSpell(sErrorMessage, oCaster);
}
else
{
SCfizzleSpell("You don't have the requirements to cast spells of that level!", oCaster);
}
SetLocalString( oCaster, "CSL_ERRORSTRING", "" );
return;
}
}
#6
Posté 07 mars 2012 - 02:52
If it fires on casting (before the effects of the spell are dealt) then you might be able to give the bosses custom OnSpellCastAt scripts that temporarily grant them a huge spell resistance value for a few seconds depending on what spell is cast at them.
Or is spell resistance determined when the spell is cast rather than when the impact script runs?
Spell hooking would be far more reliable, but an OnSpellCastAt script would be the easier of the two to impliment.
#7
Posté 07 mars 2012 - 03:46
The oncastat event is triggered by the signalevent inside the spell as well.
Spellresistance is inside the script, and determined by the myresistspell function ( which wraps resistspell and technically is broken )
Spell hooking is by far the easiest, as onspellcastat i think is mainly for NPC's and the AI so they react properly.
#8
Posté 07 mars 2012 - 05:24
Pain I will certainly be having some more questions about your CLS library. I know it has several features I am looking for, but I am nervous about integrating it properly.
I will save that for another thread though.
#9
Posté 07 mars 2012 - 10:41
You first need to determine if the spell being cast is one of the spells you are looking for. So you need to define an integer as the ID# of the spell being cast. Then you need a conditional to check if the integer is equal to the spellID# of the spell you are looking for. Let's use vampiric feast for example.
Copy the code for vampiric feast into the conditional and follow it with the function to make the rest of the spell script not fire after the spellhook fires. Sorry, I don't have access to the name of it right now.
Now, you modify the code to vampiric feast to not affect any creatures that have the same tags as the bosses that you want to be immune to it.
Now you are done.
Replay if any of that is unclear and I'll try to explain it better.
#10
Posté 07 mars 2012 - 11:05
you use the following -->
object oTarget = GetSpellTarget();
object oCaster = OBJECT_SELF;
int iSpellId=GetSpellId();
// define a var named BOSS with a value of 1 on the target creature
if ( GetLocalInt( oTarget, "BOSS") && ( iSpellId == SPELL_EPIC_RUIN || iSpellId == ANOTHERSPELL ) // ANOTHERSPELL can be the row number in spells.2da or any SPELL_* constant that you can lookup in the toolset
{
// do the spell hook fizzle code here
}
M. Reider is saying edit the spell itself --> nx_s2_vampiric_feast.nss
Use the open convo/script menu item in the toolset and type in "vamp" and it will search for this spell for you.
edit this line
if (spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, OBJECT_SELF)) // Only ever effects enemies {to
if (spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, OBJECT_SELF) && !GetLocalInt( oTarget, "BOSS") ) // Only ever effects enemies who do not have the BOSS variable set to 1 or higher {
( note: i am aiming you at using the BOSS variable as i've actually found other scripters from before my time using that variable, in my code you cannot clone bosses for example and i use that variable. )
You still should run the SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, GetSpellId(), FALSE)); on the target if it's blocked via BOSS, so the caster can be attacked by that boss.
Modifié par painofdungeoneternal, 07 mars 2012 - 11:13 .
#11
Posté 07 mars 2012 - 11:11
I'd probably encode the local string something like this: 002101231258 (for spells 21, 123 & 1258), making sure that every spell id you want to check against had four characters (filling in zero prefixes where necessary). Then a loop in the spell hooking script could check each four-digit substring (converted to an integer) against the incoming spell id. The loop would end when a particular four-digit substring returned as "" (ie. a blank string).
AOE spells are tricky though, as you have to do a search around the spell target location for the creatures. I recently created a simplified version of Wild Magic using similar principles. I interogated spells.2da to determine whether a spell is AOE or not, then used the spellshape search functions.
Modifié par DannJ, 07 mars 2012 - 11:19 .
#12
Posté 07 mars 2012 - 11:14
Here is a pastebin link for a script that should work for vampiric feast --> http://pastebin.com/t0PqwYbF
Dannj, I'd use a delimiter like
",91,23,4567,"
Then do a string find for ","+spellid+",", if it comes back -1 its not there, if it's 0 or higher it's there.
string sDelimiter=",";
string sValue = YOUR_SPELL_ID;
int iPosition = FindSubString( sDelimiter+sHaystack+sDelimiter, sDelimiter+sValue+sDelimiter );
if ( iPosition == -1)
{
// not found
}
else
{
// found
}
Modifié par painofdungeoneternal, 07 mars 2012 - 11:26 .
#13
Posté 07 mars 2012 - 11:21
string sType = Get2DAString("spells", "TargetType", iSpell);
switch (iRandom)
{
case 1:
{ // Caster backlash
SendMessageToPC(OBJECT_SELF, "Wild Magic - Backlash");
BackLash(iDmg);
break;
}
case 2:
{ // Random effect
SetModuleOverrideSpellScriptFinished();
SendMessageToPC(OBJECT_SELF, "Wild Magic - Spell Warped");
object oTarget = GetSpellTargetObject();
if (GetIsObjectValid(oTarget))
lLoc = GetLocation(oTarget);
float fRadius = IntToFloat(iDmg)/3;
if (sType == "0x3E" || sType == "0x3F" || sType == "0x2E" || sType == "0x2F")
{
oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fRadius, lLoc, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
while (GetIsObjectValid(oTarget))
{
if(oTarget != OBJECT_SELF && spellsIsTarget(oTarget,SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF))
{
RandomDamage(iDmg, oTarget);
iDmg = d8(iSpellLevel);
}
oTarget = GetNextObjectInShape(SHAPE_SPHERE, fRadius, lLoc, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE );
}//end while
}// end if
else
RandomDamage(iDmg, oTarget);
break;
}
}// end switch
Modifié par DannJ, 07 mars 2012 - 11:25 .
#14
Posté 07 mars 2012 - 11:26
DannJ wrote...
I can envisage a way of encoding multiple spell ids into a single local string variable on a creature, so that all you'd have to do is compare a substring of the variable with the incoming spell id. There'd be no need to hardcode checks for specific spell ids then, and you'd have the flexibility of being able to make any creature in the module immune to certain spells on an individual basis with just one local variable.
I'd probably encode the local string something like this: 002101231258 (for spells 21, 123 & 1258), making sure that every spell id you want to check against had four characters (filling in zero prefixes where necessary). Then a loop in the spell hooking script could check each four-digit substring (converted to an integer) against the incoming spell id. The loop would end when a particular four-digit substring returned as "" (ie. a blank string).
Pain, isn't there a way to do this using bitwise math rather than reinventing the wheel with strings? I can't quite wrap my head around how you'd store the spell ID's as bit flags but it seems like it should be doable.
#15
Posté 07 mars 2012 - 11:27
#16
Posté 07 mars 2012 - 11:32
MasterChanger wrote...
DannJ wrote...
I can envisage a way of encoding multiple spell ids into a single local string variable on a creature, so that all you'd have to do is compare a substring of the variable with the incoming spell id. There'd be no need to hardcode checks for specific spell ids then, and you'd have the flexibility of being able to make any creature in the module immune to certain spells on an individual basis with just one local variable.
I'd probably encode the local string something like this: 002101231258 (for spells 21, 123 & 1258), making sure that every spell id you want to check against had four characters (filling in zero prefixes where necessary). Then a loop in the spell hooking script could check each four-digit substring (converted to an integer) against the incoming spell id. The loop would end when a particular four-digit substring returned as "" (ie. a blank string).
Pain, isn't there a way to do this using bitwise math rather than reinventing the wheel with strings? I can't quite wrap my head around how you'd store the spell ID's as bit flags but it seems like it should be doable.
No, what he said did not really seem to be a direction that makes things simpler.
You can't store spells as bit flags really since bits are on and off flags, and there can only be 31 of them. By the time you are done, just using the spell constants makes much more sense.
Note that the spell resist function checks for immunity for a specific spell, but that is defined in a 2da ( not sure which ones it is controlled by ).
/**
* Returns Item property spell immunity vs. specific spell. You must specify the
* spell to which the user will be immune(IP_CONST_IMMUNITYSPELL_*).
* The IP_CONST_IMMUNITYSPELL_* value can be retrieved off this type of item property using GetItemPropertyCostTableValue.
* @param nSpell
* @return an item property
*/
itemproperty ItemPropertySpellImmunitySpecific(int nSpell);
/**
* Create a Spell Immunity effect.
* There is a known bug with this function. There *must* be a parameter specified
* when this is called (even if the desired parameter is SPELL_ALL_SPELLS),
* otherwise an effect of type EFFECT_TYPE_INVALIDEFFECT will be returned.
*
*
* Returns an effect of spell immunity.
*
*
*
* Returns an effect of spell immunity to SPELL_* type spells.
*
* Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nImmunityToSpell is invalid.
*
* nImmunityToSpell can be any integer value from the spells.2da file (thus constants SPELL_* are valid), or SPELL_ALL_SPELLS for all spells.
*
* The target this effect is applied to must be a creature for it to work. This effect cannot be applied instantly, only temporarily or permanently.
*
*
* This will provide direct and total immunity to nImmunityToSpell, *if* that spell uses the function ResistSpell() within it.
*
* The fact that no friendly spell uses ResistSpell() means that friendly spells will never be resisted, directly or not.
*
* Also, natural abilites obviously are not spells, so will also not be affected by this.
*
* Effect functions are Constructors, which are special methods that help construct effect "objects". You can declare and link effects, and apply them using an ApplyEffectToObject() Command. Once applied, each effect can be got seperately via. looping valid effects on the target (GetFirst/NextEffect()). See the Effect Tutorial for more details.
*
*
* @bug (Lexicon) Previously noted bug has been fixed in a patch some time ago.
*
*
* <b>Examples: ( From Lexicon )</b>
* \\code
* // Sample code for applying direct Immunity to the Fireball spell,
* // to a target (lucky him!)
*
* void main()
* {
* // This is the Object to apply the effect to.
* object oTarget = OBJECT_SELF;
*
* // Create the effect to apply
* effect eImmunity = EffectSpellImmunity(SPELL_FIREBALL);
*
* // Create the visual portion of the effect. This is instantly
* // applied and not persistant with wether or not we have the
* // above effect.
* effect eVis = EffectVisualEffect(VFX_IMP_MAGIC_RESISTANCE_USE);
*
* // Apply the visual effect to the target
* ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
* // Apply the effect to the object
* ApplyEffectToObject(DURATION_TYPE_PERMANENT, eImmunity, oTarget);
* }
* \\endcode
*
* @onerror Returns an effect of type EFFECT_TYPE_INVALIDEFFECT if nImmunityToSpell is invalid.
* @param nImmunityToSpell \\ref SPELL_* ( DEFAULT: SPELL_ALL_SPELLS )
* @return
*/
effect EffectSpellImmunity(int nImmunityToSpell=SPELL_ALL_SPELLS);
Modifié par painofdungeoneternal, 07 mars 2012 - 11:43 .
#17
Posté 07 mars 2012 - 11:46
painofdungeoneternal wrote...
So if I was a wizard, and i cast it at the minions of the boss, then i'd have it misfire just because he might be a target?
You don't have to stop the spell working. You can use the spell hooking to give targets temporary and unbelievably high spell resistance. Even as bugged as it is, spell resistance still works for the first spell cast at the target (which is all you need in this case). The temporary SR effect only needs to last a few seconds.
Because spell hooking runs before the spell impact script does, there (hopefully) should be enough time for the SR effect to take hold before the impact script is run.
That all assumes that the epic spells don't ignore SR completely...
Modifié par DannJ, 07 mars 2012 - 11:47 .
#18
Posté 07 mars 2012 - 11:52
By the time you've got that all tested, you could recompile vampiric feast and not do too much harm to custom content out there. What you are describing is a lot more complex.
Modifié par painofdungeoneternal, 07 mars 2012 - 11:55 .
#19
Posté 08 mars 2012 - 12:14
painofdungeoneternal wrote...
I don't use stock spell resist, i would not really trust it. Even then to rely on spell resist means you can use that item property instead which gives immunity to a specific spell. You might have to adjust the epic spells to respect that though.
If an SR effect proves too buggy, you could always grant one of the SR feats, then take it way again a few seconds later. Having to work around bugs is what gives NWN2 its own unique charm.
By the time you've got that all tested, you could recompile vampiric feast and not do too much harm to custom content out there. What you are describing is a lot more complex.
If you only wanted immunity to one spell, then I'd agree with you. If you wanted the flexibility to grant immunities on an individual basis, then one complex script can make the rest relatively simple (as specific spell immunity is just one local variable away).
Besides - what's the fun of scripting if things don't get complex?
This thread has piqued my interest now, so I've half a mind to try to get the script I described up and running for my own amusement. Yes - I'm easily amused...
#20
Posté 08 mars 2012 - 01:02
The search radius for AOE spells is hardcoded (10.0m), but if there's a way of extracting this from spells.2DA for each spell then I'd be interested.
[Edit - it's been debugged, compiled and tested now. Everything works as I thought it would. Yes - even I was surprised.]
#include "NW_I0_SPELLS"
void CheckImmunity(object oTarget, int iSpellID)
{
string sImmunity = GetLocalString(oTarget,"Immunity");
if (sImmunity == "")
return;
int i;
string sSpell = GetSubString(sImmunity, 0, 4);
while (sSpell != "")
{
if (StringToInt(sSpell) == iSpellID)
{
effect eSR = EffectSpellResistanceIncrease(99);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eSR, oTarget, 3.0);
}//end if
i++;
sSpell = GetSubString(sImmunity, i*4, 4);
}//end while
}
void main()
{
object oArea = GetArea(OBJECT_SELF);
int iWildmagic = GetLocalInt(oArea, "X2_L_WILD_MAGIC");
if (iWildmagic == 1)
{
float fRadius = 10.0;// search radius
int iSpell = GetSpellId();
string sType = Get2DAString("spells", "TargetType", iSpell);
location lLoc = GetSpellTargetLocation();
object oTarget = GetSpellTargetObject();
if (GetIsObjectValid(oTarget))
lLoc = GetLocation(oTarget);
if (sType == "0x3E" || sType == "0x3F" || sType == "0x2E" || sType == "0x2F")
{
oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fRadius, lLoc, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
while (GetIsObjectValid(oTarget))
{
if (oTarget != OBJECT_SELF && spellsIsTarget(oTarget,SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF)) {
CheckImmunity(oTarget, iSpell);
}
oTarget = GetNextObjectInShape(SHAPE_SPHERE, fRadius, lLoc, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE );
}//end while
}// end if AOE
else
CheckImmunity(oTarget, iSpell);
}// end wildmagic if
}
Modifié par DannJ, 08 mars 2012 - 06:52 .
#21
Posté 08 mars 2012 - 04:49
#22
Posté 08 mars 2012 - 06:49
A single local string variable determines which spells a creature is immune to, with support for multiple spells. It works both for single-target and area-of-effect spells. However someone would have to verify whether epic spells allow for spell resistance checks.
#23
Posté 08 mars 2012 - 09:59
#24
Posté 08 mars 2012 - 04:22
DannJ wrote...
However someone would have to verify whether epic spells allow for spell resistance checks.
Pretty sure the answer is no, at least with vanilla scripts. I'm pretty sure they don't consider Spell Focus feats, Spellcraft bonus to saves against spells, or really act like spells at all.
#25
Posté 08 mars 2012 - 04:57
if (!MyResistSpell(OBJECT_SELF, oTarget, fDelay))
So vampiric feast should obey it, however epic spells being feats causes some problems. Any spells that ignore mantles ( acid damage for example which you have in various spells including hellball would not be blockable )
Note that just giving the boss a item property of immunity, or via spawn in script giving them immunity would also work. Generally you don't want to have a lot of overhead on the spell hook, and in a complex setting with a lot of spells being cast that spell resistance is going to help that boss a lot more than it should every so often. If the resistspell function works, then the following probably works as well but you'd have to test it of course.
effect eImmune = EffectSpellImmunity(SPELL_*);





Retour en haut






