I have seen this topic a few times but I forgot to respond. I also got this working, half a year ago. Based on something Lance did, I think. He provided XML code for it once.
What I've done is make a mod which allows wizards to select their oppositions schools. It doesn't work extremely well with the default spell system because you can then learn forbidden spells, and cast them. Because it requires removing all forbidden schools in the relevant .2da file. And then you stop casting via the spell hook. But it works better via the custom spell casting system. Because that also prevents you from learning forbidden spells appropriately. That makes it essentially painless.
This is the code I wrote:
/*
gui_getchardata by Rasael, original by Lance Botelle
*/
#include "inc_spellschools"
void main(string sField, string sValue)
{
//Trim the value string.
sValue = GetStringRight(sValue, GetStringLength(sValue) - 17);
object oChar = OBJECT_SELF;
//SendMessageToPC(oChar, "Running UI Feedback");
//SendMessageToPC(oChar, "Running Script School");
//SpeakString("Running Script School");
// Categorize and store the character data for easier retrieval.
// Assign feats (as Raelius Archmannon suggested) or store in your DB.
//SendMessageToPC(OBJECT_SELF, sField + " = " + sValue);
int iTargetSchool = -1;
if ( sField == "SCHOOL" ) // don't even bother if the field returned is not school, possibly make this fire once for each field desired
{
// Lets do some validation, this should already be set up correctly, but checking for monkey business
// If the caster is a specialist some other things should be set as well
// is the caster a wizard
if ( GetLevelByClass( CLASS_TYPE_WIZARD, oChar ) > 0 )
{
if ( sValue == "Abjuration" )
{
iTargetSchool = SPELL_SCHOOL_ABJURATION;
}
else if ( sValue == "Conjuration" )
{
iTargetSchool = SPELL_SCHOOL_CONJURATION;
}
else if ( sValue == "Divination" )
{
iTargetSchool = SPELL_SCHOOL_DIVINATION;
}
else if ( sValue == "Enchantment" )
{
iTargetSchool = SPELL_SCHOOL_ENCHANTMENT;
}
else if ( sValue == "Evocation" )
{
iTargetSchool = SPELL_SCHOOL_EVOCATION;
}
else if ( sValue == "Illusion" )
{
iTargetSchool = SPELL_SCHOOL_ILLUSION;
}
else if ( sValue == "Necromancy" )
{
iTargetSchool = SPELL_SCHOOL_NECROMANCY;
}
else if ( sValue == "Transmutation" )
{
iTargetSchool = SPELL_SCHOOL_TRANSMUTATION;
}
else
{
iTargetSchool = SPELL_SCHOOL_GENERAL;
//if ( GetLevelByClass( CLASS_TYPE_RED_WIZARD, oChar ) > 0 )
//{
// log message to dms now, we hve an issue, red wizards have to have a speciality.
// set up benefits so it does not matter too much
//}
}
//iTargetSchool = 100;
}
}
//SendMessageToPC(oChar, "Spellschool = "+sValue+" = "+IntToString(iTargetSchool));
CloseGUIScreen(oChar, "archery_copy");
SetLocalInt(oChar, "focus_spellschool", iTargetSchool);
int iHasFeats = iHasFocalSchoolFeat(oChar, iTargetSchool);
}
And this is the include:
/*
inc_spellschools by Rasael
*/
//translates string to school int
int StringToSchool(string sSchool)
{
if(sSchool == "G") {return SPELL_SCHOOL_GENERAL;}
if(sSchool == "A") {return SPELL_SCHOOL_ABJURATION;}
if(sSchool == "C") {return SPELL_SCHOOL_CONJURATION;}
if(sSchool == "D") {return SPELL_SCHOOL_DIVINATION;}
if(sSchool == "E") {return SPELL_SCHOOL_ENCHANTMENT;}
if(sSchool == "V") {return SPELL_SCHOOL_EVOCATION;}
if(sSchool == "I") {return SPELL_SCHOOL_ILLUSION;}
if(sSchool == "N") {return SPELL_SCHOOL_NECROMANCY;}
if(sSchool == "T") {return SPELL_SCHOOL_TRANSMUTATION;}
return 3;
}
//returns the NWN2 default opposed schools
int DefaultOpposedSchool1(int iSchool)
{
if(iSchool == SPELL_SCHOOL_ABJURATION) {return SPELL_SCHOOL_CONJURATION;}
if(iSchool == SPELL_SCHOOL_CONJURATION) {return SPELL_SCHOOL_TRANSMUTATION;}
if(iSchool == SPELL_SCHOOL_DIVINATION) {return SPELL_SCHOOL_ILLUSION;}
if(iSchool == SPELL_SCHOOL_ENCHANTMENT) {return SPELL_SCHOOL_ILLUSION;}
if(iSchool == SPELL_SCHOOL_EVOCATION) {return SPELL_SCHOOL_CONJURATION;}
if(iSchool == SPELL_SCHOOL_ILLUSION) {return SPELL_SCHOOL_ENCHANTMENT;}
if(iSchool == SPELL_SCHOOL_NECROMANCY) {return SPELL_SCHOOL_DIVINATION;}
if(iSchool == SPELL_SCHOOL_TRANSMUTATION) {return SPELL_SCHOOL_CONJURATION;}
return 3;
}
//returns the NWN2 default opposed schools
int DefaultOpposedSchool2(int iSchool)
{
if(iSchool == SPELL_SCHOOL_ABJURATION) {return SPELL_SCHOOL_ILLUSION;}
if(iSchool == SPELL_SCHOOL_CONJURATION) {return SPELL_SCHOOL_ILLUSION;}
if(iSchool == SPELL_SCHOOL_DIVINATION) {return SPELL_SCHOOL_ENCHANTMENT;}
if(iSchool == SPELL_SCHOOL_ENCHANTMENT) {return SPELL_SCHOOL_ABJURATION;}
if(iSchool == SPELL_SCHOOL_EVOCATION) {return SPELL_SCHOOL_ILLUSION;}
if(iSchool == SPELL_SCHOOL_ILLUSION) {return SPELL_SCHOOL_ABJURATION;}
if(iSchool == SPELL_SCHOOL_NECROMANCY) {return SPELL_SCHOOL_ILLUSION;}
if(iSchool == SPELL_SCHOOL_TRANSMUTATION) {return SPELL_SCHOOL_ILLUSION;}
return SPELL_SCHOOL_ILLUSION;
}
//return feat ID tied to opposed school constant, -1 on error or general school
int iFeatForOpposedSchool(int iOpposeSchool)
{
if(iOpposeSchool == SPELL_SCHOOL_ABJURATION) {return 2467;}
if(iOpposeSchool == SPELL_SCHOOL_CONJURATION) {return 2466;}
if(iOpposeSchool == SPELL_SCHOOL_DIVINATION) {return 2465;}
if(iOpposeSchool == SPELL_SCHOOL_ENCHANTMENT) {return 2464;}
if(iOpposeSchool == SPELL_SCHOOL_EVOCATION) {return 2463;}
if(iOpposeSchool == SPELL_SCHOOL_ILLUSION) {return 2462;}
if(iOpposeSchool == SPELL_SCHOOL_NECROMANCY) {return 2461;}
if(iOpposeSchool == SPELL_SCHOOL_TRANSMUTATION) {return 2460;}
return -1;
}
int iHasFocalSchoolFeat(object oTargetWizard, int iFavouredSchool)
{
if(iFavouredSchool == SPELL_SCHOOL_ABJURATION && GetHasFeat(2477, oTargetWizard, TRUE) == FALSE)
{FeatAdd( oTargetWizard, 2477, FALSE, TRUE, TRUE);}
if(iFavouredSchool == SPELL_SCHOOL_CONJURATION && GetHasFeat(2476, oTargetWizard, TRUE) == FALSE)
{FeatAdd( oTargetWizard, 2476, FALSE, TRUE, TRUE);}
if(iFavouredSchool == SPELL_SCHOOL_DIVINATION && GetHasFeat(2475, oTargetWizard, TRUE) == FALSE)
{FeatAdd( oTargetWizard, 2475, FALSE, TRUE, TRUE);}
if(iFavouredSchool == SPELL_SCHOOL_ENCHANTMENT && GetHasFeat(2474, oTargetWizard, TRUE) == FALSE)
{FeatAdd( oTargetWizard, 2474, FALSE, TRUE, TRUE);}
if(iFavouredSchool == SPELL_SCHOOL_EVOCATION && GetHasFeat(2473, oTargetWizard, TRUE) == FALSE)
{FeatAdd( oTargetWizard, 2473, FALSE, TRUE, TRUE);}
if(iFavouredSchool == SPELL_SCHOOL_ILLUSION && GetHasFeat(2472, oTargetWizard, TRUE) == FALSE)
{FeatAdd( oTargetWizard, 2472, FALSE, TRUE, TRUE);}
if(iFavouredSchool == SPELL_SCHOOL_NECROMANCY && GetHasFeat(2471, oTargetWizard, TRUE) == FALSE)
{FeatAdd( oTargetWizard, 2471, FALSE, TRUE, TRUE);}
if(iFavouredSchool == SPELL_SCHOOL_TRANSMUTATION && GetHasFeat(2470, oTargetWizard, TRUE) == FALSE)
{FeatAdd( oTargetWizard, 2470, FALSE, TRUE, TRUE);}
if(iFavouredSchool == SPELL_SCHOOL_GENERAL && GetHasFeat(2478, oTargetWizard, TRUE) == FALSE)
{FeatAdd( oTargetWizard, 2478, FALSE, TRUE, TRUE);}
int iForbiddenSchoolFeats = 0;
if(GetHasFeat(2460, oTargetWizard, TRUE) == TRUE) {iForbiddenSchoolFeats = iForbiddenSchoolFeats + 1;} //Trans
if(GetHasFeat(2461, oTargetWizard, TRUE) == TRUE) {iForbiddenSchoolFeats = iForbiddenSchoolFeats + 1;} //Necro
if(GetHasFeat(2462, oTargetWizard, TRUE) == TRUE) {iForbiddenSchoolFeats = iForbiddenSchoolFeats + 1;} //Illu
if(GetHasFeat(2463, oTargetWizard, TRUE) == TRUE) {iForbiddenSchoolFeats = iForbiddenSchoolFeats + 1;} //Evoc
if(GetHasFeat(2464, oTargetWizard, TRUE) == TRUE) {iForbiddenSchoolFeats = iForbiddenSchoolFeats + 1;} //Enchan
if(GetHasFeat(2465, oTargetWizard, TRUE) == TRUE) {iForbiddenSchoolFeats = iForbiddenSchoolFeats + 1;} //Divin
if(GetHasFeat(2466, oTargetWizard, TRUE) == TRUE) {iForbiddenSchoolFeats = iForbiddenSchoolFeats + 1;} //Conjur
if(GetHasFeat(2467, oTargetWizard, TRUE) == TRUE) {iForbiddenSchoolFeats = iForbiddenSchoolFeats + 1;} //Abjur
int iNeedForbidden = 1;
if(iFavouredSchool == SPELL_SCHOOL_GENERAL) {iNeedForbidden = 0;}
int iRW = GetLevelByClass(CLASS_TYPE_RED_WIZARD, oTargetWizard);
if(iRW > 0) {iNeedForbidden = 2;}
int iFeatAdded;
if(iNeedForbidden > iForbiddenSchoolFeats)
{
if(iForbiddenSchoolFeats == 1 && iNeedForbidden == 2)
{
int iDefault2School = DefaultOpposedSchool2(iFavouredSchool);
int iDefault2SchoolFeat = iFeatForOpposedSchool(iDefault2School);
if(GetHasFeat(iDefault2SchoolFeat, oTargetWizard, TRUE) == FALSE) {FeatAdd( oTargetWizard, iDefault2SchoolFeat, FALSE, TRUE, TRUE);}
else if(GetHasFeat(2465, oTargetWizard, TRUE) == FALSE) {FeatAdd( oTargetWizard, 2465, FALSE, TRUE, TRUE);}
else {FeatAdd( oTargetWizard, 2467, FALSE, TRUE, TRUE);}
iFeatAdded = 1;
}
else if(iForbiddenSchoolFeats == 0 && iNeedForbidden == 1)
{
int iDefault1School = DefaultOpposedSchool1(iFavouredSchool);
int iDefault1SchoolFeat = iFeatForOpposedSchool(iDefault1School);
if(GetHasFeat(iDefault1SchoolFeat, oTargetWizard, TRUE) == FALSE) {FeatAdd( oTargetWizard, iDefault1SchoolFeat, FALSE, TRUE, TRUE);}
else if(GetHasFeat(2465, oTargetWizard, TRUE) == FALSE) {FeatAdd( oTargetWizard, 2465, FALSE, TRUE, TRUE);}
else {FeatAdd( oTargetWizard, 2467, FALSE, TRUE, TRUE);}
iFeatAdded = 1;
}
else if(iForbiddenSchoolFeats == 0 && iNeedForbidden == 2)
{
int iDefault1School = DefaultOpposedSchool1(iFavouredSchool);
int iDefault1SchoolFeat = iFeatForOpposedSchool(iDefault1School);
int iDefault2School = DefaultOpposedSchool2(iFavouredSchool);
int iDefault2SchoolFeat = iFeatForOpposedSchool(iDefault2School);
if(GetHasFeat(iDefault2SchoolFeat, oTargetWizard, TRUE) == FALSE) {FeatAdd( oTargetWizard, iDefault2SchoolFeat, FALSE, TRUE, TRUE);}
if(GetHasFeat(iDefault1SchoolFeat, oTargetWizard, TRUE) == FALSE) {FeatAdd( oTargetWizard, iDefault1SchoolFeat, FALSE, TRUE, TRUE);}
iFeatAdded = 1;
}
}
if(iFeatAdded == 1)
{
if(GetHasFeat(2469, oTargetWizard, TRUE) == FALSE) {FeatAdd( oTargetWizard, 2469, FALSE, TRUE, TRUE);}
}
SetLocalInt(oTargetWizard, "special_school_check", TRUE);
return TRUE;
}
And this is the XML, which you can call up whenever you need it in game and then set the result with a (local) variable on the PC:
<?xml version="1.0" encoding="utf-8">
<UIScene name="spell_school_get" x="ALIGN_LEFT" y="ALIGN_TOP" width="40" height="40" idleexpiretime="1.0" scriptloadable="true"
backoutkey="false" priority="SCENE_SCRIPT" modal="false"
OnAdd="UIObject_Misc_RequestCharacterUpdates(0,true,STATS)"
OnRemove="UIObject_Misc_RequestCharacterUpdates(0,false)"
/>
<UIPane width="40" height="40" hidden="true" focusable="false" >
<UIText width="PARENT_WIDTH" height="PARENT_HEIGHT" update="true" hidden="true"
OnUpdate0="UIObject_OnUpdate_GetGlobalSpellClass(Global:2)"
OnUpdate1="UIObject_Misc_SetLocalVarString(local:0)"
OnUpdate2="UIObject_Misc_ExecuteServerScript(gui_getchardata1,local:0)"
/>
</UIPane>
There's also a conversation via which the player picks his forbidden schools. I can't post that here. If there's any interest I can probably upload everything to the vault some time. Just need to take some time to organize it all.