2025/11/20 Update
Updated epic swashbucker tlk. Added notes on Martial Study. Updated Shadow Servant to scale with Shadow Master level, per PnP. Made Disciple of Baalzebul's CHA boost intrinsic. Swarm of Arrows is an Eldritch Knight epic bonus feat. Epic eldritch theurge is 11th level, not 21st level. Updated Shadowdancer weapon proficiencies. WP: Scythe was a usable feat for Warblade. Gaseous Form added to iprp_spells. Set Favoured Soul's Regen X Wounds spells to the correct spell level. Updated Twinfiend to not suck. Tweaked GetMaxPossibleHP for Undead & Constructs. Updated PRCIsFlying() for newer CEP2 wings. More fixes and updated for NUI levelup menu. (@Rakiov) Added support for de-leveling AMS classes (@Rakiov) Zakya Rakshasa have a claw & bite attack. Added check to end grapples after target death. Removed debug message in GetHighestSpellAvailableByDescriptor() Monsters won't summon uncontrolled undead. Added Signal Event to Luminous Armor. Corrected Signal Event on Shield of Faith.
This commit is contained in:
164
nwn/nwnprc/trunk/scripts/mshdw_shadserv.nss
Normal file
164
nwn/nwnprc/trunk/scripts/mshdw_shadserv.nss
Normal file
@@ -0,0 +1,164 @@
|
||||
//::////////////////////////////////////////////////////////
|
||||
//:: ;-. ,-. ,-. ,-.
|
||||
//:: | ) | ) / ( )
|
||||
//:: |-' |-< | ;-:
|
||||
//:: | | \ \ ( )
|
||||
//:: ' ' ' `-' `-'
|
||||
//::///////////////////////////////////////////////////////
|
||||
//::
|
||||
/*
|
||||
Impactscript for Shadow Servant.
|
||||
(this is handled in the Familiar script)
|
||||
|
||||
Shadow Servant (Su): At 1st level, your shadow familiar permanently
|
||||
transforms into a Medium shadow elemental. It loses all familiar
|
||||
traits, but gains new abilities as your shadow servant.
|
||||
|
||||
Should your shadow servant die, you can summon a replacement after
|
||||
24 hours pass. Your shadow servant cannot travel farther from you
|
||||
than 30 feet + 10 feet for each of your master of shadow levels
|
||||
(40 feet at 1st level and a maximum of 130 feet at 10th level). If
|
||||
it is forcibly separated from you by more than this distance, the
|
||||
servant dissipates instantly, and you must wait 24 hours to summon
|
||||
a new one.
|
||||
|
||||
|
||||
|
||||
*/
|
||||
//::
|
||||
//:://////////////////////////////////////////////
|
||||
//:: Script: mshadw_shadserv.nss
|
||||
//:: Author: Jaysyn
|
||||
//:: Created: 2025-11-11 19:25:58
|
||||
//:://////////////////////////////////////////////
|
||||
#include "prc_inc_json"
|
||||
#include "prc_inc_spells"
|
||||
|
||||
const string SHADOW_SERVANT_RESREF = "prc_shadow_serv";
|
||||
|
||||
// Watch function: despawns Shadow Servant if master is dead or out of range
|
||||
void ShadowServantWatch(object oShadow, object oPC)
|
||||
{
|
||||
if(DEBUG) DoDebug("mshadw_shadserv >> ShadowServantWatch: Starting function.");
|
||||
|
||||
int nMaster = GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oPC);
|
||||
|
||||
float fRange = 30.0 + (nMaster * 10);
|
||||
|
||||
if (!GetIsObjectValid(oShadow) || !GetIsObjectValid(oPC)) return;
|
||||
|
||||
if (GetIsDead(oPC) ||
|
||||
GetDistanceBetween(oShadow, oPC) > FeetToMeters(fRange))
|
||||
{
|
||||
DestroyObject(oShadow);
|
||||
return;
|
||||
}
|
||||
|
||||
DelayCommand(1.0, ShadowServantWatch(oShadow, oPC));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
object oPC = OBJECT_SELF;
|
||||
|
||||
int nMaster = GetLevelByClass(CLASS_TYPE_MASTER_OF_SHADOW, oPC);
|
||||
|
||||
int nDexBonus = (nMaster >= 5 && (nMaster % 2)) ? (nMaster - 3) : 0;
|
||||
|
||||
float fRange = 30.0 + (nMaster * 10);
|
||||
|
||||
// Target location
|
||||
location lTarget = GetSpellTargetLocation();
|
||||
|
||||
// Distance check
|
||||
if (GetDistanceBetweenLocations(GetLocation(oPC), lTarget) > FeetToMeters(fRange))
|
||||
{
|
||||
SendMessageToPC(oPC, "That location is too far away.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Load template
|
||||
json jShadow = TemplateToJson(SHADOW_SERVANT_RESREF, RESTYPE_UTC);
|
||||
if (jShadow == JSON_NULL)
|
||||
{
|
||||
SendMessageToPC(oPC, "mshdw_shadserv: TemplateToJson failed <20> bad resref or resource missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Original HD
|
||||
int nOriginalHD = json_GetCreatureHD(jShadow);
|
||||
if (nOriginalHD <= 0)
|
||||
{
|
||||
SendMessageToPC(oPC, "mshdw_shadserv: json_GetCreatureHD failed <20> template missing HD data.");
|
||||
return;
|
||||
}
|
||||
|
||||
//:: Add Hit Dice
|
||||
int nHDToAdd = nMaster -1;
|
||||
|
||||
if (nHDToAdd < 0) nHDToAdd = 0;
|
||||
|
||||
jShadow = json_AddHitDice(jShadow, nHDToAdd);
|
||||
|
||||
if (jShadow == JSON_NULL)
|
||||
{
|
||||
SendMessageToPC(oPC, "mshdw_shadserv: json_AddHitDice failed - JSON became invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
//:: Update feats
|
||||
jShadow = json_AddFeatsFromCreatureVars(jShadow, nOriginalHD);
|
||||
if (jShadow == JSON_NULL)
|
||||
{
|
||||
SendMessageToPC(oPC, "mshdw_shadserv: json_AddFeatsFromCreatureVars failed <20> JSON became invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
//:: Update stats
|
||||
jShadow = json_ApplyAbilityBoostFromHD(jShadow, nOriginalHD);
|
||||
if (jShadow == JSON_NULL)
|
||||
{
|
||||
SendMessageToPC(oPC, "mshdw_shadserv: json_ApplyAbilityBoostFromHD failed <20> JSON became invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
//:: Bonus DEX from Shadow Servant class ability
|
||||
jShadow = json_UpdateTemplateStats(jShadow, 0, nDexBonus);
|
||||
|
||||
// Size increase
|
||||
if (nMaster > 2)
|
||||
{
|
||||
jShadow = json_AdjustCreatureSize(jShadow, 1, TRUE);
|
||||
if (jShadow == JSON_NULL)
|
||||
{
|
||||
SendMessageToPC(oPC, "mshdw_shadserv: json_AdjustCreatureSize failed - JSON became invalid.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
object oShadow = JsonToObject(jShadow, lTarget);
|
||||
effect eSummon = ExtraordinaryEffect(EffectSummonCreature("", VFX_FNF_SUMMON_UNDEAD, 0.0, 0, VFX_IMP_UNSUMMON, oShadow));
|
||||
|
||||
ApplyEffectAtLocation(DURATION_TYPE_PERMANENT, eSummon, lTarget);
|
||||
|
||||
if (!GetIsObjectValid(oShadow))
|
||||
{
|
||||
SendMessageToPC(oPC, "mshdw_shadserv: JsonToObject failed - could not create creature from edited template.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set faction to caster<65>s
|
||||
ChangeFaction(oShadow, oPC);
|
||||
SetLocalObject(oShadow, "ANIMATOR", oPC);
|
||||
|
||||
SetCurrentHitPoints(oShadow, GetMaxPossibleHP(oShadow));
|
||||
|
||||
effect eGhost = EffectVisualEffect(VFX_DUR_GHOST_TRANSPARENT);
|
||||
eGhost = UnyieldingEffect(eGhost);
|
||||
|
||||
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGhost, oShadow);
|
||||
|
||||
|
||||
// Start watch loop
|
||||
DelayCommand(6.1, ShadowServantWatch(oShadow, oPC));
|
||||
}
|
||||
@@ -3,8 +3,19 @@
|
||||
|
||||
void main()
|
||||
{
|
||||
ExecuteScript("nw_ch_summon_9", OBJECT_SELF);
|
||||
ExecuteScript("prc_npc_spawn", OBJECT_SELF);
|
||||
object oNPC = OBJECT_SELF;
|
||||
|
||||
ExecuteScript("nw_ch_summon_9", oNPC);
|
||||
ExecuteScript("prc_npc_spawn", oNPC);
|
||||
|
||||
//:: Used for the Twinfiend Pit Fiend summon
|
||||
int nUltravision = GetLocalInt(oNPC,"INNATE_ULTRAVISION");
|
||||
if(nUltravision)
|
||||
{
|
||||
effect eUltra = EffectUltravision();
|
||||
eUltra = UnyieldingEffect(eUltra);
|
||||
DelayCommand(0.0f, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eUltra, oNPC));
|
||||
}
|
||||
|
||||
//use companion appearances
|
||||
/*if(GetPRCSwitch(MARKER_PRC_COMPANION))
|
||||
|
||||
@@ -31,7 +31,7 @@ void main()
|
||||
int bKing = GetHasFeat(FEAT_KING_LIES, oPC) ? 4 : 0;
|
||||
int bDevil = GetHasFeat(FEAT_TONGUE_DEVIL, oPC) ? iInt : 0;
|
||||
|
||||
if (bKing>0) KingofLies(oPC, oSkin,bKing);
|
||||
//if (bKing>0) KingofLies(oPC, oSkin,bKing); Handled in stat 2DA now
|
||||
if (bDevil>0) DevilTongue(oPC, oSkin,bDevil);
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "psi_inc_psifunc"
|
||||
#include "inc_ecl"
|
||||
#include "prc_inc_assoc"
|
||||
#include "prc_inc_combmove"
|
||||
|
||||
void PreyOnTheWeak(object oDead)
|
||||
{
|
||||
@@ -48,6 +49,9 @@ void main()
|
||||
{
|
||||
object oDead = GetLastBeingDied();
|
||||
object oKiller = MyGetLastKiller();
|
||||
|
||||
DelayCommand(0.1f, EndGrapple(oDead, oKiller));
|
||||
DelayCommand(0.2f, EndGrapple(oKiller, oDead));
|
||||
|
||||
// We are not actually dead until -10
|
||||
// Unless it's a spell death
|
||||
|
||||
@@ -34,6 +34,28 @@ const string CHAT_COMMAND_INDICATOR_1 = "~~";
|
||||
const string CHAT_COMMAND_INDICATOR_2 = "..";
|
||||
const int CHAT_COMMAND_INDICATOR_LENGHT = 2;
|
||||
|
||||
void ForceRemoveAllSpells(object oPC)
|
||||
{
|
||||
int classId;
|
||||
for(classId = 1; classId < CLASS_TYPE_INVALID; classId++)
|
||||
{
|
||||
int j;
|
||||
for (j = 0; j <= 40; j++)
|
||||
{
|
||||
DelayCommand(0.0f, CallSpellUnlevelScript(oPC, classId, j));
|
||||
}
|
||||
}
|
||||
SendMessageToPC(oPC, "Finished removing spells.");
|
||||
}
|
||||
|
||||
void ResetCharacterXPAndRemoveSpells(object oPC)
|
||||
{
|
||||
int xp = GetXP(oPC);
|
||||
ForceRemoveAllSpells(oPC);
|
||||
SetXP(oPC, 1);
|
||||
SetXP(oPC, xp);
|
||||
}
|
||||
|
||||
int GetIsChatCommand(string sString)
|
||||
{
|
||||
string sTest = GetStringLeft(sString, CHAT_COMMAND_INDICATOR_LENGHT);
|
||||
@@ -111,6 +133,50 @@ void main()
|
||||
string firstWord = JsonGetString(JsonArrayGet(sCommandSplit, 0));
|
||||
|
||||
// if first word is /pa we are using the power attack interface
|
||||
if (firstWord == "/relevel")
|
||||
{
|
||||
int confirmed = GetLocalInt(oPC, "RelevelConfirm");
|
||||
if(confirmed)
|
||||
{
|
||||
SendMessageToPC(oPC, "Please wait as we relevel you, this may take some time...");
|
||||
DelayCommand(1.0f, ResetCharacterXPAndRemoveSpells(oPC));
|
||||
DeleteLocalInt(oPC, "RelevelConfirm");
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessageToPC(oPC, "This will relevel you back to level 1 while preserving XP, type /relevel again to confirm.");
|
||||
SetLocalInt(oPC, "RelevelConfirm", 1);
|
||||
CloseNUILevelUpWindow(oPC, TRUE);
|
||||
}
|
||||
SetPCChatMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteLocalInt(oPC, "RelevelConfirm");
|
||||
SetPCChatMessage();
|
||||
}
|
||||
if (firstWord == "/resetSpells")
|
||||
{
|
||||
int confirmed = GetLocalInt(oPC, "ResetSpellsConfirm");
|
||||
if (confirmed)
|
||||
{
|
||||
SendMessageToPC(oPC, "Please wait as we remove your spells, this may take some time...");
|
||||
DelayCommand(1.0f, ForceRemoveAllSpells(oPC));
|
||||
DeleteLocalInt(oPC, "ResetSpellsConfirm");
|
||||
CloseNUILevelUpWindow(oPC, TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessageToPC(oPC, "This will reset all spell choices among all PRC classes. Type /resetSpells again to confirm.");
|
||||
SetLocalInt(oPC, "ResetSpellsConfirm", 1);
|
||||
}
|
||||
SetPCChatMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteLocalInt(oPC, "ResetSpellsConfirm");
|
||||
SetPCChatMessage();
|
||||
}
|
||||
if(firstWord == "/pa")
|
||||
{
|
||||
if(JsonGetLength(sCommandSplit) >= 2)
|
||||
@@ -147,17 +213,6 @@ void main()
|
||||
SetPCChatMessage();
|
||||
return;
|
||||
}
|
||||
if (firstWord == "/lvl")
|
||||
{
|
||||
if (JsonGetLength(sCommandSplit) >= 2)
|
||||
{
|
||||
int classPos = StringToInt(JsonGetString(JsonArrayGet(sCommandSplit, 1)));
|
||||
int nClass = GetClassByPosition(classPos, oPC);
|
||||
OpenNUILevelUpWindow(nClass, oPC);
|
||||
SetPCChatMessage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute scripts hooked to this event for the player triggering it
|
||||
|
||||
@@ -142,8 +142,8 @@ int GetHighestSpellAvailableByDescriptor(object oPC, string sDescriptor)
|
||||
sSpellLabel = Get2DACache(sFile, "Label", i);
|
||||
if(sSpellLabel != "") // Non-blank row
|
||||
{
|
||||
SendMessageToPC(oPC, "GetHighestSpellAvailableByDescriptor >> Entered function.");
|
||||
SendMessageToPC(oPC, "Row " + IntToString(i) +
|
||||
if (DEBUG) DoDebug("GetHighestSpellAvailableByDescriptor >> Entered function.");
|
||||
if (DEBUG) DoDebug("Row " + IntToString(i) +
|
||||
" Label = " + sSpellLabel +
|
||||
" SpellID = " + IntToString(nSpellID) +
|
||||
" HasSpell " + IntToString(PRCGetHasSpell(nSpellID, oPC)));
|
||||
|
||||
474
nwn/nwnprc/trunk/scripts/prc_unlvl_script.nss
Normal file
474
nwn/nwnprc/trunk/scripts/prc_unlvl_script.nss
Normal file
@@ -0,0 +1,474 @@
|
||||
//::///////////////////////////////////////////////
|
||||
//:: PRC Unlevel Logic
|
||||
//:: prc_unlvl_script
|
||||
//:://////////////////////////////////////////////
|
||||
/*
|
||||
This is the logic for removing spells from a PRC class in case of
|
||||
unleveling or for fixing issues
|
||||
*/
|
||||
//:://////////////////////////////////////////////
|
||||
//:: Created By: Rakiov
|
||||
//:: Created On: 22.09.2025
|
||||
//:://////////////////////////////////////////////
|
||||
|
||||
#include "tob_inc_tobfunc"
|
||||
#include "tob_inc_moveknwn"
|
||||
#include "inv_inc_invfunc"
|
||||
#include "shd_inc_mystknwn"
|
||||
#include "shd_inc_shdfunc"
|
||||
#include "true_inc_truknwn"
|
||||
#include "true_inc_trufunc"
|
||||
#include "prc_nui_com_inc"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
/// ///
|
||||
/// Implementations ///
|
||||
/// ///
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int FindSpellbookId(int nClass, int spellId)
|
||||
{
|
||||
string sFile = GetClassSpellbookFile(nClass);
|
||||
int totalSpells = Get2DARowCount(sFile);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < totalSpells; i++)
|
||||
{
|
||||
int currentSpellId = StringToInt(Get2DACache(sFile, "SpellID", i));
|
||||
if (currentSpellId == spellId)
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RemoveSpellsFromPlayer(object oPC, int nClass, string spellArray, string totalSpellsId, int nType=0)
|
||||
{
|
||||
// if we found the spell, then we remove it.
|
||||
int totalRemoved = persistant_array_get_size(oPC, spellArray);
|
||||
if (DEBUG) DoDebug("Found " + IntToString(totalRemoved) + " spells in " + spellArray + ", removing them.");
|
||||
string sFile = GetClassSpellbookFile(nClass);
|
||||
int i;
|
||||
for (i = 0; i < totalRemoved; i++)
|
||||
{
|
||||
int spellId = persistant_array_get_int(oPC, spellArray, i);
|
||||
int spellbookId = FindSpellbookId(nClass, spellId);
|
||||
|
||||
if (spellbookId != 0)
|
||||
{
|
||||
// remove spell from player
|
||||
string spellName = Get2DACache(sFile, "Label", spellbookId);
|
||||
if (DEBUG) DoDebug( "Removing spell " + spellName);
|
||||
int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", spellbookId));
|
||||
object oSkin = GetPCSkin(oPC);
|
||||
RemoveIPFeat(oPC, ipFeatID);
|
||||
if (GetIsBladeMagicClass(nClass))
|
||||
{
|
||||
string sDisciplineArray = _MANEUVER_LIST_DISCIPLINE + IntToString(nType) + "_" + Get2DACache(sFile, "Discipline", spellbookId);
|
||||
int totalDiscSpells = GetPersistantLocalInt(oPC, sDisciplineArray);
|
||||
SetPersistantLocalInt(oPC, sDisciplineArray, totalDiscSpells - 1);
|
||||
if (DEBUG) DoDebug(sDisciplineArray + " total maneuvers is now " + IntToString(totalDiscSpells-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
persistant_array_delete(oPC, spellArray);
|
||||
|
||||
int totalSpellCount = GetPersistantLocalInt(oPC, totalSpellsId);
|
||||
// decrement the amount of spells known.
|
||||
SetPersistantLocalInt(oPC, totalSpellsId,
|
||||
totalSpellCount - totalRemoved
|
||||
);
|
||||
if (DEBUG) DoDebug(totalSpellsId + " total spells is now " + IntToString(totalSpellCount - totalRemoved));
|
||||
}
|
||||
|
||||
string GetMaxSpellsKnownName(int nClass)
|
||||
{
|
||||
if (GetIsShadowMagicClass(nClass))
|
||||
{
|
||||
return _MYSTERY_LIST_TOTAL_KNOWN;
|
||||
}
|
||||
if (GetIsInvocationClass(nClass))
|
||||
{
|
||||
return _INVOCATION_LIST_TOTAL_KNOWN;
|
||||
}
|
||||
if (GetIsBladeMagicClass(nClass))
|
||||
{
|
||||
return _MANEUVER_LIST_TOTAL_KNOWN;
|
||||
}
|
||||
if (GetIsTruenamingClass(nClass))
|
||||
{
|
||||
return _UTTERANCE_LIST_TOTAL_KNOWN;
|
||||
}
|
||||
if (GetIsPsionicClass(nClass))
|
||||
{
|
||||
return _POWER_LIST_TOTAL_KNOWN;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
string GetGeneralArrayId(int nClass)
|
||||
{
|
||||
if (GetIsShadowMagicClass(nClass))
|
||||
{
|
||||
return _MYSTERY_LIST_GENERAL_ARRAY;
|
||||
}
|
||||
if (GetIsInvocationClass(nClass))
|
||||
{
|
||||
return _INVOCATION_LIST_GENERAL_ARRAY;
|
||||
}
|
||||
if (GetIsBladeMagicClass(nClass))
|
||||
{
|
||||
return _MANEUVER_LIST_GENERAL_ARRAY;
|
||||
}
|
||||
if (GetIsTruenamingClass(nClass))
|
||||
{
|
||||
return _UTTERANCE_LIST_GENERAL_ARRAY;
|
||||
}
|
||||
if (GetIsPsionicClass(nClass))
|
||||
{
|
||||
return _POWER_LIST_GENERAL_ARRAY;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
string GetBaseListName(int nClass)
|
||||
{
|
||||
if (GetIsShadowMagicClass(nClass))
|
||||
{
|
||||
return _MYSTERY_LIST_NAME_BASE;
|
||||
}
|
||||
if (GetIsInvocationClass(nClass))
|
||||
{
|
||||
return _INVOCATION_LIST_NAME_BASE;
|
||||
}
|
||||
if (GetIsBladeMagicClass(nClass))
|
||||
{
|
||||
return _MANEUVER_LIST_NAME_BASE;
|
||||
}
|
||||
if (GetIsTruenamingClass(nClass))
|
||||
{
|
||||
return _UTTERANCE_LIST_NAME_BASE;
|
||||
}
|
||||
if (GetIsPsionicClass(nClass))
|
||||
{
|
||||
return _POWER_LIST_NAME_BASE;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
string GetLevelArrayListName(int nClass)
|
||||
{
|
||||
if (GetIsShadowMagicClass(nClass))
|
||||
{
|
||||
return _MYSTERY_LIST_LEVEL_ARRAY;
|
||||
}
|
||||
if (GetIsInvocationClass(nClass))
|
||||
{
|
||||
return _INVOCATION_LIST_LEVEL_ARRAY;
|
||||
}
|
||||
if (GetIsBladeMagicClass(nClass))
|
||||
{
|
||||
return _MANEUVER_LIST_LEVEL_ARRAY;
|
||||
}
|
||||
if (GetIsTruenamingClass(nClass))
|
||||
{
|
||||
return _UTTERANCE_LIST_LEVEL_ARRAY;
|
||||
}
|
||||
if (GetIsPsionicClass(nClass))
|
||||
{
|
||||
return _POWER_LIST_LEVEL_ARRAY;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void RemoveSpellsAtLevel(object oPC, int nClass, int level, int nList = 0)
|
||||
{
|
||||
if (GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS)
|
||||
{
|
||||
string sFile = GetClassSpellbookFile(nClass);
|
||||
string spellsAtLevelList = ("SpellsKnown_" + IntToString(nClass) + "_AtLevel" + IntToString(level));
|
||||
string spellLevelBook = GetSpellsKnown_Array(nClass);
|
||||
if (level == 0)
|
||||
{
|
||||
spellsAtLevelList = spellLevelBook;
|
||||
int totalSpells = Get2DARowCount(sFile);
|
||||
int i;
|
||||
for (i = 0; i < totalSpells; i++)
|
||||
{
|
||||
int featID = StringToInt(Get2DACache(sFile, "FeatID", i));
|
||||
if (featID && GetHasFeat(featID, oPC, TRUE))
|
||||
{
|
||||
string spellName = Get2DACache(sFile, "Label", i);
|
||||
if (DEBUG) DoDebug( "Removing spellID " + IntToString(i) + ", spell name: " + spellName);
|
||||
int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", i));
|
||||
WipeSpellFromHide(ipFeatID, oPC);
|
||||
}
|
||||
}
|
||||
|
||||
persistant_array_delete(oPC, spellsAtLevelList);
|
||||
|
||||
}
|
||||
else if (persistant_array_exists(oPC, spellsAtLevelList))
|
||||
{
|
||||
if (DEBUG) DoDebug( "Removing spells in " + spellsAtLevelList);
|
||||
int knownSpellsCount = persistant_array_get_size(oPC, spellsAtLevelList);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < knownSpellsCount; i++)
|
||||
{
|
||||
int spellId = persistant_array_get_int(oPC, spellsAtLevelList, i);
|
||||
int spellbookId = FindSpellbookId(nClass, spellId);
|
||||
|
||||
if (spellbookId)
|
||||
{
|
||||
array_extract_int(oPC, spellLevelBook, spellbookId);
|
||||
|
||||
// wipe the spell from the player
|
||||
string spellName = Get2DACache(sFile, "Label", spellbookId);
|
||||
if (DEBUG) DoDebug( "Removing spellID " + IntToString(spellbookId) + ", spell name: " + spellName);
|
||||
int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", spellbookId));
|
||||
WipeSpellFromHide(ipFeatID, oPC);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
persistant_array_delete(oPC, spellsAtLevelList);
|
||||
|
||||
if (nClass == CLASS_TYPE_BEGUILER
|
||||
|| nClass == CLASS_TYPE_DREAD_NECROMANCER
|
||||
|| nClass == CLASS_TYPE_WARMAGE)
|
||||
{
|
||||
int nAdvLearn = GetPersistantLocalInt(oPC, "AdvancedLearning_"+IntToString(nClass)) - knownSpellsCount;
|
||||
SetPersistantLocalInt(oPC, "AdvancedLearning_"+IntToString(nClass), nAdvLearn);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int chosenList = (nList != 0) ? nList : nClass;
|
||||
string baseList = GetBaseListName(nClass);
|
||||
string totalCountId = GetMaxSpellsKnownName(nClass);
|
||||
|
||||
if (GetIsBladeMagicClass(nClass))
|
||||
{
|
||||
|
||||
// remove maneuvers
|
||||
int maneuver;
|
||||
for (maneuver = 1; maneuver <= MANEUVER_TYPE_MANEUVER; maneuver++)
|
||||
{
|
||||
string spellArray = baseList + IntToString(chosenList) + IntToString(maneuver);
|
||||
if (level == 0)
|
||||
{
|
||||
spellArray += GetGeneralArrayId(nClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
spellArray += GetLevelArrayListName(nClass) + IntToString(level);
|
||||
}
|
||||
if (persistant_array_exists(oPC, spellArray))
|
||||
{
|
||||
string totalSpellsId = baseList + IntToString(chosenList) + IntToString(maneuver) + totalCountId;
|
||||
RemoveSpellsFromPlayer(oPC, nClass, spellArray, totalSpellsId, maneuver);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (GetIsTruenamingClass(nClass))
|
||||
{
|
||||
// Lexicon 1
|
||||
string spellArray = baseList + IntToString(chosenList) + "1";
|
||||
if (level == 0)
|
||||
{
|
||||
spellArray += GetGeneralArrayId(nClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
spellArray += GetLevelArrayListName(nClass) + IntToString(level);
|
||||
}
|
||||
if (persistant_array_exists(oPC, spellArray))
|
||||
{
|
||||
string totalSpellsId = baseList + IntToString(chosenList) + totalCountId;
|
||||
RemoveSpellsFromPlayer(oPC, nClass, spellArray, totalSpellsId);
|
||||
}
|
||||
|
||||
// Lexicon 2
|
||||
spellArray = baseList + IntToString(chosenList) + "2";
|
||||
if (level == 0)
|
||||
{
|
||||
spellArray += GetGeneralArrayId(nClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
spellArray += GetLevelArrayListName(nClass) + IntToString(level);
|
||||
}
|
||||
if (persistant_array_exists(oPC, spellArray))
|
||||
{
|
||||
string totalSpellsId = baseList + IntToString(chosenList) + totalCountId;
|
||||
RemoveSpellsFromPlayer(oPC, nClass, spellArray, totalSpellsId);
|
||||
}
|
||||
|
||||
// Lexicon 3
|
||||
spellArray = baseList + IntToString(chosenList) + "3";
|
||||
if (level == 0)
|
||||
{
|
||||
spellArray += GetGeneralArrayId(nClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
spellArray += GetLevelArrayListName(nClass) + IntToString(level);
|
||||
}
|
||||
if (persistant_array_exists(oPC, spellArray))
|
||||
{
|
||||
string totalSpellsId = baseList + IntToString(chosenList) + totalCountId;
|
||||
RemoveSpellsFromPlayer(oPC, nClass, spellArray, totalSpellsId);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
string spellArray = (baseList + IntToString(chosenList));
|
||||
if (level == 0)
|
||||
{
|
||||
spellArray += GetGeneralArrayId(nClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
spellArray += GetLevelArrayListName(nClass) + IntToString(level);
|
||||
}
|
||||
if (persistant_array_exists(oPC, spellArray))
|
||||
{
|
||||
if (DEBUG) DoDebug( "Removing spells from " + spellArray);
|
||||
string totalSpellsId = baseList + IntToString(chosenList) + totalCountId;
|
||||
RemoveSpellsFromPlayer(oPC, nClass, spellArray, totalSpellsId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int IsClassPRCSpellCaster(int nClass, object oPlayer)
|
||||
{
|
||||
// This controls who can use the Spellbook NUI, if for some reason you don't
|
||||
// want a class to be allowed to use this you can comment out their line here
|
||||
|
||||
// Bard and Sorc are allowed if they took a PRC that makes them use the spellbook
|
||||
if (GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_SPONTANEOUS
|
||||
|| GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_PREPARED)
|
||||
return TRUE;
|
||||
|
||||
// Arcane Spont
|
||||
if (nClass == CLASS_TYPE_ASSASSIN
|
||||
|| nClass == CLASS_TYPE_BEGUILER
|
||||
|| nClass == CLASS_TYPE_CELEBRANT_SHARESS
|
||||
|| nClass == CLASS_TYPE_DREAD_NECROMANCER
|
||||
|| nClass == CLASS_TYPE_DUSKBLADE
|
||||
|| nClass == CLASS_TYPE_HARPER
|
||||
|| nClass == CLASS_TYPE_HEXBLADE
|
||||
|| nClass == CLASS_TYPE_KNIGHT_WEAVE
|
||||
|| nClass == CLASS_TYPE_SHADOWLORD
|
||||
|| nClass == CLASS_TYPE_SUBLIME_CHORD
|
||||
|| nClass == CLASS_TYPE_SUEL_ARCHANAMACH
|
||||
|| nClass == CLASS_TYPE_WARMAGE)
|
||||
return TRUE;
|
||||
|
||||
// Psionics
|
||||
if (nClass == CLASS_TYPE_FIST_OF_ZUOKEN
|
||||
|| nClass == CLASS_TYPE_PSION
|
||||
|| nClass == CLASS_TYPE_PSYWAR
|
||||
|| nClass == CLASS_TYPE_WILDER
|
||||
|| nClass == CLASS_TYPE_PSYCHIC_ROGUE
|
||||
|| nClass == CLASS_TYPE_WARMIND)
|
||||
return TRUE;
|
||||
|
||||
// Invokers
|
||||
if (nClass == CLASS_TYPE_WARLOCK
|
||||
|| nClass == CLASS_TYPE_DRAGON_SHAMAN
|
||||
|| nClass == CLASS_TYPE_DRAGONFIRE_ADEPT)
|
||||
return TRUE;
|
||||
|
||||
// Divine Spont
|
||||
if (nClass == CLASS_TYPE_ARCHIVIST //while technically prepared, they use the spont system of casting
|
||||
|| nClass == CLASS_TYPE_FAVOURED_SOUL
|
||||
|| nClass == CLASS_TYPE_JUSTICEWW)
|
||||
return TRUE;
|
||||
|
||||
// ToB Classes
|
||||
if (nClass == CLASS_TYPE_WARBLADE
|
||||
|| nClass == CLASS_TYPE_SWORDSAGE
|
||||
|| nClass == CLASS_TYPE_CRUSADER)
|
||||
return TRUE;
|
||||
|
||||
// Mystery Classes
|
||||
if (nClass == CLASS_TYPE_SHADOWCASTER
|
||||
|| nClass == CLASS_TYPE_SHADOWSMITH)
|
||||
return TRUE;
|
||||
|
||||
// Truenamers
|
||||
if (nClass == CLASS_TYPE_TRUENAMER)
|
||||
return TRUE;
|
||||
|
||||
// RHD Casters
|
||||
if ((nClass == CLASS_TYPE_SHAPECHANGER
|
||||
&& GetRacialType(oPlayer) == RACIAL_TYPE_ARANEA
|
||||
&& !GetLevelByClass(CLASS_TYPE_SORCERER))
|
||||
|| (nClass == CLASS_TYPE_OUTSIDER
|
||||
&& GetRacialType(oPlayer) == RACIAL_TYPE_RAKSHASA
|
||||
&& !GetLevelByClass(CLASS_TYPE_SORCERER))
|
||||
|| (nClass == CLASS_TYPE_ABERRATION
|
||||
&& GetRacialType(oPlayer) == RACIAL_TYPE_DRIDER
|
||||
&& !GetLevelByClass(CLASS_TYPE_SORCERER))
|
||||
|| (nClass == CLASS_TYPE_MONSTROUS
|
||||
&& GetRacialType(oPlayer) == RACIAL_TYPE_ARKAMOI
|
||||
&& !GetLevelByClass(CLASS_TYPE_SORCERER))
|
||||
|| (nClass == CLASS_TYPE_MONSTROUS
|
||||
&& GetRacialType(oPlayer) == RACIAL_TYPE_HOBGOBLIN_WARSOUL
|
||||
&& !GetLevelByClass(CLASS_TYPE_SORCERER))
|
||||
|| (nClass == CLASS_TYPE_MONSTROUS
|
||||
&& GetRacialType(oPlayer) == RACIAL_TYPE_REDSPAWN_ARCANISS
|
||||
&& !GetLevelByClass(CLASS_TYPE_SORCERER))
|
||||
|| (nClass == CLASS_TYPE_MONSTROUS
|
||||
&& GetRacialType(oPlayer) == RACIAL_TYPE_MARRUTACT
|
||||
&& !GetLevelByClass(CLASS_TYPE_SORCERER))
|
||||
|| (nClass == CLASS_TYPE_FEY
|
||||
&& GetRacialType(oPlayer) == RACIAL_TYPE_GLOURA
|
||||
&& !GetLevelByClass(CLASS_TYPE_BARD)))
|
||||
return TRUE;
|
||||
|
||||
// Binders
|
||||
if (nClass == CLASS_TYPE_BINDER)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CheckAndRemoveSpellsForClassAtLevel(object oPC, int nClass, int level)
|
||||
{
|
||||
if (IsClassPRCSpellCaster(nClass, oPC))
|
||||
{
|
||||
if (GetIsInvocationClass(nClass))
|
||||
{
|
||||
RemoveSpellsAtLevel(oPC, nClass, level, INVOCATION_LIST_EXTRA);
|
||||
RemoveSpellsAtLevel(oPC, nClass, level, INVOCATION_LIST_EXTRA_EPIC);
|
||||
}
|
||||
if (GetIsPsionicClass(nClass))
|
||||
{
|
||||
RemoveSpellsAtLevel(oPC, nClass, level, POWER_LIST_EXP_KNOWLEDGE);
|
||||
RemoveSpellsAtLevel(oPC, nClass, level, POWER_LIST_EPIC_EXP_KNOWLEDGE);
|
||||
}
|
||||
RemoveSpellsAtLevel(oPC, nClass, level);
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int nClass = StringToInt(GetScriptParam("UnLevel_ClassChoice"));
|
||||
int nLevel = StringToInt(GetScriptParam("UnLevel_LevelChoice"));
|
||||
if (nClass)
|
||||
{
|
||||
CheckAndRemoveSpellsForClassAtLevel(OBJECT_SELF, nClass, nLevel);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user