Eldritch Disciple should have Verminlord as an epic bonus feat. Only spellcasters can use Craft (Alchemy). Added Great Charisma and removed Great Wisdom as Force Missile Mage epic bonus feats. Frenzied Berserker was missing Great STR 10 as an epic bonus feat. Oozemaster had several epic bonus feats only grantable at 10th lvl. Pyromancer's Great Charisma bonus feats were pointing at wrong or non-existent feats. Corrected Frenzied Berserker's skill list. Corrected Legendary Dreadnought's skill list. Added placeholders for Combat Form feats. Added Combat Forms masterfeats. Fixed ASF issue with Eldritch Sculptor's 2nd blast. Gated debug in CheckIfDeleveled(). Updated AddRacialRestrictions() for latest races. Vow of Poverty & Forsaker work better together at level up. Maybe fixed the mass ability buffs not hitting all targets issue. Needs mulitplayer testing. Updated some creature abilities to use PRC functions.
332 lines
14 KiB
Plaintext
332 lines
14 KiB
Plaintext
//:://////////////////////////////////////////////
|
|
//:: Vow of Poverty Extra Feat
|
|
//:: ft_vowpoverty_ft
|
|
//:://////////////////////////////////////////////
|
|
/** @file
|
|
This allows you to choose the extra feat each even level.
|
|
|
|
@original author Fencas
|
|
@date Created - 2025-01-12
|
|
*/
|
|
//:://////////////////////////////////////////////
|
|
//:://////////////////////////////////////////////
|
|
|
|
#include "inc_dynconv"
|
|
#include "prc_inc_function"
|
|
#include "NW_I0_GENERIC"
|
|
#include "inc_persist_loca"
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Constant definitions */
|
|
//////////////////////////////////////////////////
|
|
|
|
const int STAGE_SELECT_ABIL = 0;
|
|
const int STAGE_CONFIRM_SELECTION = 1;
|
|
|
|
const int STRREF_SELECTED_HEADER2 = 16824210; // "Is this correct?"
|
|
const int STRREF_YES = 4752; // "Yes"
|
|
const int STRREF_NO = 4753; // "No"
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function definitions */
|
|
//////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void main()
|
|
{
|
|
object oPC = GetPCSpeaker();
|
|
object oSkin = GetPCSkin(oPC);
|
|
int i, j, nTest, nRow;
|
|
int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE);
|
|
int nStage = GetStage(oPC);
|
|
int nLevel = GetPersistantLocalInt(oPC, "VoPFeatCheck");
|
|
|
|
// Check which of the conversation scripts called the scripts
|
|
if(nValue == 0) // All of them set the DynConv_Var to non-zero value, so something is wrong -> abort
|
|
{
|
|
if(DEBUG) DoDebug("ft_vowpoverty_ft: Aborting due to error.");
|
|
return;
|
|
}
|
|
|
|
if(nValue == DYNCONV_SETUP_STAGE)
|
|
{
|
|
// Check if this stage is marked as already set up
|
|
// This stops list duplication when scrolling
|
|
if(!GetIsStageSetUp(nStage, oPC))
|
|
{
|
|
// Maneuver selection stage
|
|
if(nStage == STAGE_SELECT_ABIL)
|
|
{
|
|
//Check which Feats have been added by this ability
|
|
int nTotalRows = Get2DARowCount("prc_vop_feats");
|
|
effect eCheckEffect = GetFirstEffect(oPC);
|
|
while (GetIsEffectValid(eCheckEffect))
|
|
{
|
|
for(nRow=0; nRow <= nTotalRows; nRow++)
|
|
{
|
|
string nFeat = Get2DAString("prc_vop_feats","FeatIndex",nRow);
|
|
if(GetEffectTag(eCheckEffect) == "VoPFeat"+nFeat) SetLocalInt(oPC,"VoPFeat"+nFeat,1);
|
|
}
|
|
eCheckEffect = GetNextEffect(oPC);
|
|
}
|
|
|
|
SetHeader("Choose an Exalted Feat for this new level under a Vow of Poverty:");
|
|
//Add new option depending if it was not selected and char has all prereqs
|
|
for(nRow=0; nRow <= nTotalRows; nRow++)
|
|
{
|
|
//Get prereqs from 2DA
|
|
string sName = Get2DAString("prc_vop_feats","Name",nRow);
|
|
int nFeat = StringToInt(Get2DAString("prc_vop_feats","FeatIndex",nRow));
|
|
int nPreReq1 = StringToInt(Get2DAString("prc_vop_feats","PreReq1",nRow));
|
|
int nPreReq2 = StringToInt(Get2DAString("prc_vop_feats","PreReq2",nRow));
|
|
int nCon = StringToInt(Get2DAString("prc_vop_feats","Con",nRow));
|
|
int nWis = StringToInt(Get2DAString("prc_vop_feats","Wis",nRow));
|
|
int nCha = StringToInt(Get2DAString("prc_vop_feats","Cha",nRow));
|
|
int nBAB = StringToInt(Get2DAString("prc_vop_feats","BAB",nRow));
|
|
int nLaw = StringToInt(Get2DAString("prc_vop_feats","Law",nRow));
|
|
|
|
int nAllPreReq = 1;
|
|
|
|
//Check if prereqs exist and, if so, if they are met - if not, set bol to 0
|
|
if(nPreReq1>0 && !GetHasFeat(nPreReq1, oPC)) nAllPreReq = 0;
|
|
if(nPreReq1==213 && GetLevelByClass(CLASS_TYPE_MONK, oPC) >= 1) nAllPreReq = 1; //for Ki Strike and monks
|
|
if(nPreReq2>0 && !GetHasFeat(nPreReq2, oPC)) nAllPreReq = 0;
|
|
if(nCon>0 && GetAbilityScore(oPC,ABILITY_CONSTITUTION, TRUE) < nCon) nAllPreReq = 0;
|
|
if(nWis>0 && GetAbilityScore(oPC,ABILITY_WISDOM, TRUE) < nWis) nAllPreReq = 0;
|
|
if(nCha>0 && GetAbilityScore(oPC,ABILITY_CHARISMA, TRUE) < nCha) nAllPreReq = 0;
|
|
if(nBAB>0 && GetBaseAttackBonus(oPC) < nBAB) nAllPreReq = 0;
|
|
if(nLaw>0 && !(GetAlignmentLawChaos(oPC) == ALIGNMENT_LAWFUL)) nAllPreReq = 0;
|
|
|
|
if (!GetHasFeat(nFeat, oPC) && !GetLocalInt(oPC, "VoPFeat"+IntToString(nFeat)) && nAllPreReq == 1) AddChoice(sName, nFeat, oPC);
|
|
}
|
|
|
|
AddChoice("Cancel (you will get no Exalted Feats this level)", 0, oPC);
|
|
SetDefaultTokens(); //If there are more than 10 options, add Next
|
|
MarkStageSetUp(STAGE_SELECT_ABIL, oPC);
|
|
}
|
|
else if(nStage == STAGE_CONFIRM_SELECTION)
|
|
{
|
|
int nFeat = GetLocalInt(oPC, "VoP_SelectedFeat");
|
|
string sText = GetStringByStrRef(StringToInt(Get2DAString("feat", "FEAT", nFeat))) + "\n\n";
|
|
sText += GetStringByStrRef(StringToInt(Get2DAString("feat", "DESCRIPTION", nFeat))) + "\n\n";
|
|
sText += GetStringByStrRef(STRREF_SELECTED_HEADER2); // "Is this correct?"
|
|
SetHeader(sText);
|
|
AddChoice(GetStringByStrRef(STRREF_YES), TRUE, oPC);
|
|
AddChoice(GetStringByStrRef(STRREF_NO), FALSE, oPC);
|
|
MarkStageSetUp(STAGE_CONFIRM_SELECTION, oPC);
|
|
}
|
|
}
|
|
|
|
// Do token setup
|
|
SetupTokens();
|
|
}
|
|
else if(nValue == DYNCONV_EXITED)
|
|
{
|
|
if(DEBUG) DoDebug("ft_vowpoverty_ab/ft: Running exit handler");
|
|
if(GetLocalInt(oPC, "PRC_VoP_Exit_Ran_Forsaker_Check")) return;
|
|
SetLocalInt(oPC, "PRC_VoP_Exit_Ran_Forsaker_Check", TRUE);
|
|
DelayCommand(3.0f, DeleteLocalInt(oPC, "PRC_VoP_Exit_Ran_Forsaker_Check"));
|
|
|
|
if (GetLevelByClass(CLASS_TYPE_FORSAKER, oPC) > 0)
|
|
{
|
|
int nForsakerLvl = GetLevelByClass(CLASS_TYPE_FORSAKER, oPC);
|
|
int nLvlCheck;
|
|
for (nLvlCheck = 1; nLvlCheck <= nForsakerLvl; nLvlCheck++)
|
|
{
|
|
if (!GetPersistantLocalInt(oPC, "ForsakerBoost"+IntToString(nLvlCheck)))
|
|
{
|
|
AssignCommand(oPC, ClearAllActions(TRUE));
|
|
SetPersistantLocalInt(oPC, "ForsakerBoostCheck", nLvlCheck);
|
|
DelayCommand(3.5f, StartDynamicConversation("prc_forsake_abil", oPC, DYNCONV_EXIT_NOT_ALLOWED, FALSE, TRUE, oPC));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(nValue == DYNCONV_ABORTED)
|
|
{
|
|
// This section should never be run, since aborting this conversation should
|
|
// always be forbidden and as such, any attempts to abort the conversation
|
|
// should be handled transparently by the system
|
|
if(DEBUG) DoDebug("ft_vowpoverty_ft: ERROR: Conversation abort section run");
|
|
DeleteLocalInt(oPC, "VoP_SelectedFeat");
|
|
}
|
|
// Handle PC response
|
|
else
|
|
{
|
|
int nChoice = GetChoice(oPC);
|
|
if(nStage == STAGE_SELECT_ABIL)
|
|
{
|
|
if (nChoice == 0)
|
|
{
|
|
// Cancel chosen
|
|
DeletePersistantLocalInt(oPC,"VoPFeatCheck");
|
|
AllowExit(DYNCONV_EXIT_FORCE_EXIT);
|
|
return;
|
|
}
|
|
SetLocalInt(oPC, "VoP_SelectedFeat", nChoice);
|
|
nStage = STAGE_CONFIRM_SELECTION;
|
|
MarkStageNotSetUp(STAGE_SELECT_ABIL, oPC);
|
|
}
|
|
else if(nStage == STAGE_CONFIRM_SELECTION)
|
|
{
|
|
if (nChoice == TRUE) // Yes
|
|
{
|
|
int nFeat = GetLocalInt(oPC, "VoP_SelectedFeat");
|
|
SetPersistantLocalInt(oPC, "VoPFeat"+IntToString(nLevel), 1);
|
|
SetPersistantLocalInt(oPC, "VoPFeatID" + IntToString(nFeat), 1);
|
|
|
|
if (GetPRCSwitch("PRC_NWNXEE_ENABLED") && GetPRCSwitch("PRC_PRCX_ENABLED"))
|
|
{
|
|
PRC_Funcs_AddFeat(oPC, nFeat);
|
|
}
|
|
else
|
|
{
|
|
effect eBonusFeat = EffectBonusFeat(nFeat);
|
|
eBonusFeat = UnyieldingEffect(eBonusFeat);
|
|
eBonusFeat = TagEffect(eBonusFeat, "VoPFeat"+IntToString(nFeat));
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBonusFeat, oPC);
|
|
}
|
|
|
|
DeleteLocalInt(oPC, "VoP_SelectedFeat");
|
|
DeletePersistantLocalInt(oPC,"VoPFeatCheck");
|
|
AllowExit(DYNCONV_EXIT_FORCE_EXIT);
|
|
}
|
|
else // No
|
|
{
|
|
nStage = STAGE_SELECT_ABIL;
|
|
MarkStageNotSetUp(STAGE_CONFIRM_SELECTION, oPC);
|
|
}
|
|
}
|
|
|
|
if(DEBUG) DoDebug("ft_vowpoverty_ft: New stage: " + IntToString(nStage));
|
|
|
|
// Store the stage value. If it has been changed, this clears out the choices
|
|
SetStage(nStage, oPC);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* void main()
|
|
{
|
|
object oPC = GetPCSpeaker();
|
|
object oSkin = GetPCSkin(oPC);
|
|
int i, j, nTest, nRow;
|
|
int nValue = GetLocalInt(oPC, DYNCONV_VARIABLE);
|
|
int nStage = GetStage(oPC);
|
|
int nLevel = GetPersistantLocalInt(oPC, "VoPFeatCheck");
|
|
|
|
// Check which of the conversation scripts called the scripts
|
|
if(nValue == 0) // All of them set the DynConv_Var to non-zero value, so something is wrong -> abort
|
|
{
|
|
if(DEBUG) DoDebug("ft_vowpoverty_ft: Aborting due to error.");
|
|
return;
|
|
}
|
|
|
|
if(nValue == DYNCONV_SETUP_STAGE)
|
|
{
|
|
// Check if this stage is marked as already set up
|
|
// This stops list duplication when scrolling
|
|
if(!GetIsStageSetUp(nStage, oPC))
|
|
{
|
|
// Maneuver selection stage
|
|
if(nStage == STAGE_SELECT_ABIL)
|
|
{
|
|
//Check which Feats have been added by this ability
|
|
int nTotalRows = Get2DARowCount("prc_vop_feats");
|
|
effect eCheckEffect = GetFirstEffect(oPC);
|
|
while (GetIsEffectValid(eCheckEffect))
|
|
{
|
|
for(nRow=0; nRow <= nTotalRows; nRow++)
|
|
{
|
|
string nFeat = Get2DAString("prc_vop_feats","FeatIndex",nRow);
|
|
if(GetEffectTag(eCheckEffect) == "VoPFeat"+nFeat) SetLocalInt(oPC,"VoPFeat"+nFeat,1);
|
|
}
|
|
eCheckEffect = GetNextEffect(oPC);
|
|
}
|
|
|
|
SetHeader("Choose an Exalted Feat for this new level under a Vow of Poverty:");
|
|
//Add new option depending if it was not selected and char has all prereqs
|
|
for(nRow=0; nRow <= nTotalRows; nRow++)
|
|
{
|
|
//Get prereqs from 2DA
|
|
string sName = Get2DAString("prc_vop_feats","Name",nRow);
|
|
int nFeat = StringToInt(Get2DAString("prc_vop_feats","FeatIndex",nRow));
|
|
int nPreReq1 = StringToInt(Get2DAString("prc_vop_feats","PreReq1",nRow));
|
|
int nPreReq2 = StringToInt(Get2DAString("prc_vop_feats","PreReq2",nRow));
|
|
int nCon = StringToInt(Get2DAString("prc_vop_feats","Con",nRow));
|
|
int nWis = StringToInt(Get2DAString("prc_vop_feats","Wis",nRow));
|
|
int nCha = StringToInt(Get2DAString("prc_vop_feats","Cha",nRow));
|
|
int nBAB = StringToInt(Get2DAString("prc_vop_feats","BAB",nRow));
|
|
int nLaw = StringToInt(Get2DAString("prc_vop_feats","Law",nRow));
|
|
|
|
int nAllPreReq = 1;
|
|
|
|
//Check if prereqs exist and, if so, if they are met - if not, set bol to 0
|
|
if(nPreReq1>0 && !GetHasFeat(nPreReq1, oPC)) nAllPreReq = 0;
|
|
if(nPreReq1==213 && GetLevelByClass(CLASS_TYPE_MONK, oPC) >= 1) nAllPreReq = 1; //for Ki Strike and monks
|
|
if(nPreReq2>0 && !GetHasFeat(nPreReq2, oPC)) nAllPreReq = 0;
|
|
if(nCon>0 && GetAbilityScore(oPC,ABILITY_CONSTITUTION, TRUE) < nCon) nAllPreReq = 0;
|
|
if(nWis>0 && GetAbilityScore(oPC,ABILITY_WISDOM, TRUE) < nWis) nAllPreReq = 0;
|
|
if(nCha>0 && GetAbilityScore(oPC,ABILITY_CHARISMA, TRUE) < nCha) nAllPreReq = 0;
|
|
if(nBAB>0 && GetBaseAttackBonus(oPC) < nBAB) nAllPreReq = 0;
|
|
if(nLaw>0 && !(GetAlignmentLawChaos(oPC) == ALIGNMENT_LAWFUL)) nAllPreReq = 0;
|
|
|
|
if (!GetHasFeat(nFeat, oPC) && !GetLocalInt(oPC, "VoPFeat"+IntToString(nFeat)) && nAllPreReq == 1) AddChoice(sName, nFeat, oPC);
|
|
}
|
|
|
|
AddChoice("Cancel (you will get no Exalted Feats this level)", 0, oPC);
|
|
SetDefaultTokens(); //If there are more than 10 options, add Next
|
|
MarkStageSetUp(STAGE_SELECT_ABIL, oPC);
|
|
}
|
|
}
|
|
|
|
// Do token setup
|
|
SetupTokens();
|
|
}
|
|
else if(nValue == DYNCONV_EXITED)
|
|
{
|
|
if(DEBUG) DoDebug("ft_vowpoverty_ft: Running exit handler");
|
|
}
|
|
else if(nValue == DYNCONV_ABORTED)
|
|
{
|
|
// This section should never be run, since aborting this conversation should
|
|
// always be forbidden and as such, any attempts to abort the conversation
|
|
// should be handled transparently by the system
|
|
if(DEBUG) DoDebug("ft_vowpoverty_ft: ERROR: Conversation abort section run");
|
|
}
|
|
// Handle PC response
|
|
else
|
|
{
|
|
int nChoice = GetChoice(oPC);
|
|
if(nStage == STAGE_SELECT_ABIL)
|
|
{
|
|
SetPersistantLocalInt(oPC, "VoPFeat"+IntToString(nLevel),1); //Register the choice was made
|
|
|
|
// Store the feat ID for recalling onClientEnter
|
|
SetPersistantLocalInt(oPC, "VoPFeatID" + IntToString(nChoice), 1);
|
|
|
|
//If it was not cancelled, give chosen feat to PC (as EffectBonusFeat)
|
|
if(nChoice > 0)
|
|
{
|
|
effect eBonusFeat = EffectBonusFeat(nChoice);
|
|
eBonusFeat = UnyieldingEffect(eBonusFeat);
|
|
eBonusFeat = TagEffect(eBonusFeat, "VoPFeat"+IntToString(nChoice));
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBonusFeat, oPC);
|
|
}
|
|
|
|
// And we're all done
|
|
DeletePersistantLocalInt(oPC,"VoPFeatCheck");
|
|
AllowExit(DYNCONV_EXIT_FORCE_EXIT);
|
|
}
|
|
|
|
if(DEBUG) DoDebug("ft_vowpoverty_ft: New stage: " + IntToString(nStage));
|
|
|
|
// Store the stage value. If it has been changed, this clears out the choices
|
|
SetStage(nStage, oPC);
|
|
}
|
|
} */
|
|
|