2026/02/14 Updates

Updated PRC8 version.
Hathran can now select an ethran as a cohort.
Preliminary Circle Magic work done.
Added Choke Hold, Pain Touch, Ki Shout, Great Ki Shout, Freezing the Lifeblood, Falling Star Strikea nd Unbalancing Strike feats.
Warforged get Immunity Energy Drain, not Immunity: Ability Drain.
Forsakers can use alchemical items.
Added VectorToPerpendicular().
Added GetIsAlchemical().
Added GenerateRandomName().
Added _DoChokeHold().
Updated Shaman bonus feat list.
Updated fighter bonus feat lists.
Added Favored of the Companions to the Vow of Poverty bonus feat list.
Ur-Priest can't enter RKV, BFZ or Thrall of Orcus.
This commit is contained in:
Jaysyn904
2026-02-14 19:53:55 -05:00
parent 066590fe88
commit 41a3c945f9
94 changed files with 51583 additions and 49651 deletions

View File

@@ -1233,6 +1233,12 @@ void DeletePRCLocalIntsT(object oPC, object oItem = OBJECT_INVALID)
DeleteLocalInt(oItem,"DispIronPowerD");
// Dragonwrack
DeleteLocalInt(oItem,"DWright");
// Circle Magic
DeleteLocalInt(OBJECT_SELF, "CircleMagicActive");
DeleteLocalInt(OBJECT_SELF, "CircleMagicTotal");
DeleteLocalString(OBJECT_SELF, "CircleMagicClass");
DeleteLocalInt(OBJECT_SELF, "CircleMagicMaxParticipants");
DeleteLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_ADJUSTMENT);
}
// LEFT HAND

View File

@@ -1335,9 +1335,15 @@ const int FEAT_CLOUDY_CONJURATION = 3001;
const int FEAT_RANGED_RECALL = 5187;
const int FEAT_SOMATIC_WEAPONRY = 5186;
// Forgotten Realms Campaign Setting
const int FEAT_INSCRIBE_RUNE = 2462;
const int EPIC_FEAT_INSCRIBE_EPIC_RUNES = 2549;
//:: Forgotten Realms Campaign Setting
const int FEAT_INSCRIBE_RUNE = 2462;
const int EPIC_FEAT_INSCRIBE_EPIC_RUNES = 2549;
const int FEAT_CIRCLE_MAGIC = 4350;
const int FEAT_CIRCLE_LEADER_RASHEMAN = 4351;
const int FEAT_CIRCLE_LEADER_THAYAN = 4352;
const int FEAT_GREAT_CIRCLE_LEADER_THAYAN = 4353;
const int FEAT_GREAT_CIRCLE_LEADER_RASHEMAN = 4435;
// Miniature Handbook
const int FEAT_SHIELDMATE = 3258;
@@ -3524,7 +3530,6 @@ const int FEAT_WARLOCK_SHADOWMASTER_SHADES = 4485;
const int FEAT_VERMINLORD = 5323;
//Master Alchemist and various crafting
const int FEAT_SKILL_FOCUS_ALCHEMY = 24000;
const int FEAT_EPIC_SKILL_FOCUS_ALCHEMY = 24001;
const int FEAT_MAGICAL_ARTISAN_CRAFT_MAGIC_ARMS = 24002;
@@ -3937,22 +3942,24 @@ const int FEAT_BRUTAL_THROW = 2689;
const int FEAT_MARTIAL_STALKER = 25998;
// Complete Warrior Feats
const int FEAT_EXTRA_RAGE = 2690;
const int FEAT_FLYING_KICK = 2802;
const int FEAT_ANVIL_OF_THUNDER = 5290;
const int FEAT_HAMMERS_EDGE = 5289;
const int FEAT_HIGH_SWORD_LOW_AXE = 5288;
const int FEAT_SPINNING_HALBERD = 5287;
const int FEAT_THREE_MOUNTAINS = 5286;
const int FEAT_MONKEY_GRIP = 5197;
const int FEAT_CRESCENT_MOON = 5194;
const int FEAT_QUICK_STAFF = 5190;
const int FEAT_RANGED_DISARM = 5192;
const int FEAT_BEAR_FANG = 5189;
const int FEAT_IMPROVED_RAPID_SHOT = 5188;
const int FEAT_EARTHS_EMBRACE = 5177;
const int FEAT_SHIELD_CHARGE = 3256;
const int FEAT_SHIELD_SLAM = 3257;
const int FEAT_EXTRA_RAGE = 2690;
const int FEAT_FLYING_KICK = 2802;
const int FEAT_ANVIL_OF_THUNDER = 5290;
const int FEAT_HAMMERS_EDGE = 5289;
const int FEAT_HIGH_SWORD_LOW_AXE = 5288;
const int FEAT_SPINNING_HALBERD = 5287;
const int FEAT_THREE_MOUNTAINS = 5286;
const int FEAT_MONKEY_GRIP = 5197;
const int FEAT_CRESCENT_MOON = 5194;
const int FEAT_QUICK_STAFF = 5190;
const int FEAT_RANGED_DISARM = 5192;
const int FEAT_BEAR_FANG = 5189;
const int FEAT_IMPROVED_RAPID_SHOT = 5188;
const int FEAT_EARTHS_EMBRACE = 5177;
const int FEAT_SHIELD_CHARGE = 3256;
const int FEAT_SHIELD_SLAM = 3257;
const int FEAT_FREEZING_THE_LIFEBLOOD = 26016;
const int FEAT_CW_PAIN_TOUCH = 26018;
// CityScape Feats
const int FEAT_EFFICIENT_DEFENDER = 5293;
@@ -3977,7 +3984,12 @@ const int FEAT_WAND_MASTERY = 5314;
const int FEAT_MOUNTAIN_STANCE = 5193;
// Oriental Adventures Feats
const int FEAT_GREAT_DIPLOMAT = 5274;
const int FEAT_GREAT_DIPLOMAT = 5274;
const int FEAT_CHOKE_HOLD = 26013;
const int FEAT_FALLING_STAR_STRIKE = 26014;
const int FEAT_GREAT_KI_SHOUT = 26016;
const int FEAT_KI_SHOUT = 26017;
const int FEAT_UNBALANCING_STRIKE = 26019;
// DungeonScape Feats
const int FEAT_TRAP_ENGINEER = 5292;

View File

@@ -715,6 +715,34 @@ void _DoReapingMauler(object oPC, object oTarget, int nRounds)
}
}
}
void _DoChokeHold(object oPC, object oTarget)
{
// Size immunity: more than one size larger blocks effect
int nAttackerSize = GetCreatureSize(oPC);
int nTargetSize = GetCreatureSize(oTarget);
if (nTargetSize > nAttackerSize + 1) return;
// Stunning immunity blocks Choke Hold
if (GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT)) return;
int nDC = 10 + (GetHitDice(oPC) / 2) + GetAbilityModifier(ABILITY_WISDOM, oPC);
if (!FortitudeSave(oTarget, nDC, SAVING_THROW_TYPE_NONE, oPC))
{
SetLocalInt(oTarget, "UnconsciousGrapple", TRUE);
effect eUncon = EffectLinkEffects(EffectSleep(), EffectVisualEffect(VFX_IMP_SLEEP));
eUncon = EffectLinkEffects(eUncon, EffectKnockdown());
float fDur = RoundsToSeconds(d3());
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(eUncon), oTarget, fDur);
FloatingTextStringOnCreature("Choke Hold Success", oPC, FALSE);
DelayCommand(fDur, DeleteLocalInt(oTarget, "UnconsciousGrapple"));
// End the grapple after knocking the target out
EndGrapple(oPC, oTarget);
if (GetLocalInt(oPC, "GrappleOriginator"))
RemoveEventScript(oPC, EVENT_ONHEARTBEAT, "prc_grapple", TRUE, FALSE);
}
}
void _PostCharge(object oPC, object oTarget)
{
@@ -1822,7 +1850,51 @@ int DoGrappleOptions(object oPC, object oTarget, int nExtraBonus, int nSwitch =
else
FloatingTextStringOnCreature("You have failed your Crushing Weight Attempt",oPC, FALSE);
}
else if (nSwitch == GRAPPLE_PIN)
else if (nSwitch == GRAPPLE_PIN)
{
// Pins the target
if (nSuccess)
{
FloatingTextStringOnCreature("You have pinned your target", oPC, FALSE);
SetIsPinned(oTarget);
int nRounds = _CountPinRounds(oTarget); // always increment
int nClass = GetLevelByClass(CLASS_TYPE_REAPING_MAULER, oPC);
if (nClass)
{
_DoReapingMauler(oPC, oTarget, nRounds);
}
else if (GetHasFeat(FEAT_CHOKE_HOLD, oPC))
{
DelayCommand(6.0, _DoChokeHold(oPC, oTarget));
}
if (GetHasFeat(FEAT_EARTHS_EMBRACE, oPC))
{
// Add in unarmed damage
int nDamageSize = FindUnarmedDamage(oPC);
int nDie = StringToInt(Get2DACache("iprp_monstcost", "Die", nDamageSize));
int nNum = StringToInt(Get2DACache("iprp_monstcost", "NumDice", nDamageSize));
int nRoll;
//Potential die options
if(nDie == 4) nRoll = d4(nNum);
else if(nDie == 6) nRoll = d6(nNum);
else if(nDie == 8) nRoll = d8(nNum);
else if(nDie == 10) nRoll = d10(nNum);
else if(nDie == 12) nRoll = d12(nNum);
else if(nDie == 20) nRoll = d20(nNum);
FloatingTextStringOnCreature("Earth's Embrace chokes the life from your foe", oPC, FALSE);
int nDam = d12() + nRoll;
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDam, DAMAGE_TYPE_BLUDGEONING), oTarget);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACDecrease(4)), oPC, 6.0);
}
}
else
FloatingTextStringOnCreature("You have failed your Grapple Pin Attempt",oPC, FALSE);
}
/* else if (nSwitch == GRAPPLE_PIN)
{
// Pins the target
if (nSuccess)
@@ -1835,6 +1907,24 @@ int DoGrappleOptions(object oPC, object oTarget, int nExtraBonus, int nSwitch =
int nRounds = _CountPinRounds(oTarget);
_DoReapingMauler(oPC, oTarget, nRounds);
}
if (GetHasFeat(FEAT_CHOKE_HOLD, oPC))
{
int nRounds = GetLocalInt(oTarget, "PinnedRounds");
if (nRounds >= 1)
{
int nDC = 10 + (GetHitDice(oPC) / 2) + GetAbilityModifier(ABILITY_WISDOM, oPC);
if(!FortitudeSave(oTarget, nDC, SAVING_THROW_TYPE_NONE, oPC))
{
SetLocalInt(oTarget, "UnconsciousGrapple", TRUE);
effect eUncon = EffectLinkEffects(EffectStunned(), EffectVisualEffect(VFX_DUR_MIND_AFFECTING_DISABLED));
eUncon = EffectLinkEffects(eUncon, EffectKnockdown());
float fDur = RoundsToSeconds(d3());
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(eUncon), oTarget, fDur);
FloatingTextStringOnCreature("Choke Hold Success", oPC, FALSE);
DelayCommand(fDur, DeleteLocalInt(oTarget, "UnconsciousGrapple"));
}
}
}
if (GetHasFeat(FEAT_EARTHS_EMBRACE, oPC))
{
// Add in unarmed damage
@@ -1862,7 +1952,7 @@ int DoGrappleOptions(object oPC, object oTarget, int nExtraBonus, int nSwitch =
else
FloatingTextStringOnCreature("You have failed your Grapple Pin Attempt",oPC, FALSE);
}
else
*/ else
{
FloatingTextStringOnCreature("DoGrappleOptions: Error, invalid option "+IntToString(nSwitch)+" passed to function by "+GetName(oPC),oPC, FALSE);
FloatingTextStringOnCreature("DoGrappleOptions: Error, GetGrapple(oPC) "+IntToString(GetGrapple(oPC))+" GetGrapple(oTarget) "+IntToString(GetGrapple(oTarget)),oPC, FALSE);

View File

@@ -7,7 +7,7 @@
const string COHORT_DATABASE = "PRCCOHORTS";
const string COHORT_TAG = "prc_cohort";
//in the database there is the folloxing data structures:
//in the database there is the following data structures:
/*
int CohortCount (total number of cohorts)
object Cohort_X_obj (cohort itself)
@@ -287,6 +287,95 @@ void CancelGreatFeats(object oSpawn)
}
}
// Generates a random name for a creature based on their race and gender
string GenerateRandomName(object oCreature)
{
string sName;
int nRace = MyPRCGetRacialType(oCreature);
int nGender = GetGender(oCreature);
// First name based on race and gender
switch(nRace)
{
case RACIAL_TYPE_DWARF:
if(nGender == GENDER_FEMALE)
sName += RandomName(NAME_FIRST_DWARF_FEMALE);
else
sName += RandomName(NAME_FIRST_DWARF_MALE);
break;
case RACIAL_TYPE_ELF:
if(nGender == GENDER_FEMALE)
sName += RandomName(NAME_FIRST_ELF_FEMALE);
else
sName += RandomName(NAME_FIRST_ELF_MALE);
break;
case RACIAL_TYPE_GNOME:
if(nGender == GENDER_FEMALE)
sName += RandomName(NAME_FIRST_GNOME_FEMALE);
else
sName += RandomName(NAME_FIRST_GNOME_MALE);
break;
case RACIAL_TYPE_HUMAN:
if(nGender == GENDER_FEMALE)
sName += RandomName(NAME_FIRST_HUMAN_FEMALE);
else
sName += RandomName(NAME_FIRST_HUMAN_MALE);
break;
case RACIAL_TYPE_HALFELF:
if(nGender == GENDER_FEMALE)
sName += RandomName(NAME_FIRST_HALFELF_FEMALE);
else
sName += RandomName(NAME_FIRST_HALFELF_MALE);
break;
case RACIAL_TYPE_HALFORC:
if(nGender == GENDER_FEMALE)
sName += RandomName(NAME_FIRST_HALFORC_FEMALE);
else
sName += RandomName(NAME_FIRST_HALFORC_MALE);
break;
case RACIAL_TYPE_HALFLING:
if(nGender == GENDER_FEMALE)
sName += RandomName(NAME_FIRST_HALFLING_FEMALE);
else
sName += RandomName(NAME_FIRST_HALFLING_MALE);
break;
}
sName += " ";
// Surname based on race
switch(nRace)
{
case RACIAL_TYPE_DWARF:
sName += RandomName(NAME_LAST_DWARF);
break;
case RACIAL_TYPE_ELF:
sName += RandomName(NAME_LAST_ELF);
break;
case RACIAL_TYPE_GNOME:
sName += RandomName(NAME_LAST_GNOME);
break;
case RACIAL_TYPE_HUMAN:
sName += RandomName(NAME_LAST_HUMAN);
break;
case RACIAL_TYPE_HALFELF:
sName += RandomName(NAME_LAST_HALFELF);
break;
case RACIAL_TYPE_HALFORC:
sName += RandomName(NAME_LAST_HALFORC);
break;
case RACIAL_TYPE_HALFLING:
sName += RandomName(NAME_LAST_HALFLING);
break;
}
// Sanity check
if(sName == " ")
sName = "";
return sName;
}
void AddCohortToPlayerByObject(object oCohort, object oPC, int bDoSetup = TRUE)
{
//add it to the pc
@@ -296,6 +385,164 @@ void AddCohortToPlayerByObject(object oCohort, object oPC, int bDoSetup = TRUE)
SetMaxHenchmen(nMaxHenchmen);
object oSkin = GetPCSkin(oCohort);
if(bDoSetup)
{
//if it was a premade one, give it a random name
//randomize its appearance using DoRandomAppearance
if(GetResRef(oCohort) != "")
{
// Generate and assign random name
string sName = GenerateRandomName(oCohort);
AssignCommand(oCohort, SetName(oCohort, sName));
//use disguise code to alter head etc
DoRandomAppearance(MyPRCGetRacialType(oCohort), oCohort);
//DoRandomAppearance removed wings/tails need to re-add
if(GetRacialType(oCohort) == RACIAL_TYPE_FEYRI)
SetCreatureWingType(CREATURE_WING_TYPE_DEMON, oCohort);
else if(GetRacialType(oCohort) == RACIAL_TYPE_AVARIEL)
SetCreatureWingType(CREATURE_WING_TYPE_BIRD, oCohort);
else if(GetRacialType(oCohort) == RACIAL_TYPE_GLOURA)
SetCreatureWingType(CREATURE_WING_TYPE_BUTTERFLY, oCohort);
}
//if its a custom made cohort, need to cancel GreatX feats
else
CancelGreatFeats(oCohort);
//set it to the pcs level
int nLevel = GetCohortMaxLevel(GetLeadershipScore(oPC), oPC);
SetXP(oCohort, nLevel*(nLevel-1)*500);
SetLocalInt(oCohort, "MastersXP", GetXP(oPC));
DelayCommand(1.0, AssignCommand(oCohort, SetIsDestroyable(FALSE, TRUE, TRUE)));
DelayCommand(1.0, AssignCommand(oCohort, SetLootable(oCohort, TRUE)));
//set its maximum level lag
if(GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS))
{
//bonus cohort, no cap
}
else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP)
&& GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC)
&& GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+1)
{
//thrallherd with switch, 1 level lag
SetLocalInt(oCohort, "CohortLevelLag", 1);
}
else if(GetPRCSwitch(PRC_THRALLHERD_LEADERSHIP)
&& GetLevelByClass(CLASS_TYPE_THRALLHERD, oPC) >= 10
&& GetCurrentCohortCount(oPC) <= GetPRCSwitch(PRC_BONUS_COHORTS)+2)
{
//twofold master with switch, 2 level lag
SetLocalInt(oCohort, "CohortLevelLag", 2);
}
else
{
//other cohort have a 2 level lag
SetLocalInt(oCohort, "CohortLevelLag", 2);
}
//strip its equipment & inventory
object oTest = GetFirstItemInInventory(oCohort);
object oToken = GetHideToken(oCohort);
while(GetIsObjectValid(oTest))
{
if(GetHasInventory(oTest))
{
object oTest2 = GetFirstItemInInventory(oTest);
while(GetIsObjectValid(oTest2))
{
// Avoid blowing up the hide and token that just had the eventscripts stored on them
if(oTest2 != oSkin && oTest2 != oToken)
DestroyObject(oTest2);
oTest2 = GetNextItemInInventory(oTest);
}
}
// Avoid blowing up the hide and token that just had the eventscripts stored on them
if(oTest != oSkin && oTest != oToken)
DestroyObject(oTest);
oTest = GetNextItemInInventory(oCohort);
}
int nSlot;
for(nSlot = 0;nSlot<14;nSlot++)
{
oTest = GetItemInSlot(nSlot, oCohort);
DestroyObject(oTest);
}
//get rid of any gold it has
TakeGoldFromCreature(GetGold(oCohort), oCohort, TRUE);
}
//clean up any leftovers on the skin
ScrubPCSkin(oCohort, oSkin);
DeletePRCLocalInts(oSkin);
//turn on its scripts
//normal MoB set
AddEventScript(oCohort, EVENT_VIRTUAL_ONPHYSICALATTACKED, "prc_ai_mob_attck", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONBLOCKED, "prc_ai_mob_block", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONCOMBATROUNDEND, "prc_ai_mob_combt", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONDAMAGED, "prc_ai_mob_damag", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONDISTURBED, "prc_ai_mob_distb", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONPERCEPTION, "prc_ai_mob_percp", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONSPAWNED, "prc_ai_mob_spawn", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONSPELLCASTAT, "prc_ai_mob_spell", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONDEATH, "prc_ai_mob_death", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONRESTED, "prc_ai_mob_rest", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONUSERDEFINED, "prc_ai_mob_userd", TRUE, FALSE);
//dont run this, cohort-specific script replaces it
//AddEventScript(oCohort, EVENT_VIRTUAL_ONCONVERSATION, "prc_ai_mob_conv", TRUE, TRUE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONHEARTBEAT, "prc_ai_mob_heart", TRUE, FALSE);
//cohort specific ones
AddEventScript(oCohort, EVENT_VIRTUAL_ONCONVERSATION, "prc_ai_coh_conv", TRUE, FALSE);
AddEventScript(oCohort, EVENT_VIRTUAL_ONHEARTBEAT, "prc_ai_coh_hb", TRUE, FALSE);
//mark the master on the cohort
SetLocalObject(oCohort, "MasterObject", oPC);
//DEBUG
//various tests
if (DEBUG) DoDebug("Cohort Name="+GetName(oCohort));
if (DEBUG) DoDebug("Cohort HD="+IntToString(GetHitDice(oCohort)));
if (DEBUG) DoDebug("Cohort XP="+IntToString(GetXP(oCohort)));
if (DEBUG) DoDebug("Cohort GetIsPC="+IntToString(GetIsPC(oCohort)));
// And now gear it up
if (!GetPRCSwitch(PRC_DISABLE_COHORT_STARTING_GEAR))
{
int i;
int nHD = GetHitDice(oCohort);
for(i = 0;i<nHD;i++)
{
GenerateBossTreasure(oCohort);
}
object oGear = GetFirstItemInInventory(oCohort);
while(GetIsObjectValid(oGear))
{
SetIdentified(oGear, TRUE);
SetDroppableFlag(oGear, FALSE);
SetItemCursedFlag(oGear, TRUE);
SetStolenFlag(oGear, TRUE);
SetPlotFlag(oGear, TRUE);
AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyWeightReduction(IP_CONST_REDUCEDWEIGHT_10_PERCENT), oGear);
oGear = GetNextItemInInventory(oCohort);
}
AssignCommand(oCohort, ClearAllActions());
AssignCommand(oCohort, ActionEquipMostDamagingMelee());
AssignCommand(oCohort, ActionEquipMostEffectiveArmor());
}
}
/* void AddCohortToPlayerByObject(object oCohort, object oPC, int bDoSetup = TRUE)
{
//add it to the pc
int nMaxHenchmen = GetMaxHenchmen();
SetMaxHenchmen(99);
AddHenchman(oPC, oCohort);
SetMaxHenchmen(nMaxHenchmen);
object oSkin = GetPCSkin(oCohort);
if(bDoSetup)
{
//if it was a premade one, give it a random name
@@ -518,6 +765,7 @@ void AddCohortToPlayerByObject(object oCohort, object oPC, int bDoSetup = TRUE)
AssignCommand(oCohort, ActionEquipMostEffectiveArmor());
}
}
*/
void RemoveCohortFromPlayer(object oCohort, object oPC)
{

View File

@@ -76,7 +76,7 @@
/* This variable MUST be updated with every new version of the PRC!!! */
const string PRC_VERSION = "PRC8 4.83";
const string PRC_VERSION = "PRC8 4.84";
/* This variable MUST be updated every time 'assemble_spellbooks.bat' is run!!! */

View File

@@ -16,6 +16,9 @@ const string PRC_ForcedRestDetector_Generation = "PRC_ForcedRestDetector_Generat
// FUNCTION DECLARATIONS
///////////////////////////////////////////////////////////////////////////////
//:: Returns TRUE is the item is on the Alchemy crafting 2DA.
int GetIsAlchemical(object oItem);
// Returns the number of henchmen a player has.
int GetNumHenchmen(object oPC);
@@ -35,6 +38,36 @@ void PRCForceRested(object oPC);
// FUNCTION DEFINITIONS
///////////////////////////////////////////////////////////////////////////////
/**
* Returns a perpendicular vector to the input vector
* Rotates the vector 90 degrees in the XY plane while preserving Z
*
* @param vInput The input vector
* @return A perpendicular vector
*/
vector VectorToPerpendicular(vector vInput)
{
return Vector(-vInput.y, vInput.x, vInput.z);
}
int GetIsAlchemical(object oItem)
{
if(!GetIsObjectValid(oItem)) return FALSE;
string sResRef = GetResRef(oItem);
int nRows = StringToInt(Get2DACache("prc_craft_alchem", "ResRef", -1));
// Check if item's ResRef exists in alchemical crafting 2DA
int i;
for(i = 0; i < nRows; i++)
{
if(sResRef == Get2DACache("prc_craft_alchem", "ResRef", i))
return TRUE;
}
return FALSE;
}
int GetNumHenchmen(object oPC)
{
if (!GetIsPC(oPC)) return -1;
@@ -225,4 +258,6 @@ void PRCForceRested(object oPC)
//The PC has been forced rested--fix the problems this causes.
SetLocalInt(oPC, "PRC_ForceRested", 1);
ExecuteScript("prc_rest", oPC);
}
}
//:: void main (){}

View File

@@ -24,6 +24,15 @@ const int SPELL_BCM_RENDING_CLAWS = 17997;
//:: Complete Warrior
const int SPELL_RANGED_DISARM = 3493;
const int SPELL_FT_FREEZING_LIFEBLOOD = 17968;
const int SPELL_FT_PAIN_TOUCH = 17970;
//:: Oriental Adventures
const int SPELL_FT_CHOKE_HOLD = 17966;
const int SPELL_FT_FALLING_STAR_STRIKE = 17967;
const int SPELL_FT_KI_SHOUT = 17969;
const int SPELL_FT_UNBALANCING_STRIKE = 17971;
//:: Tome of Battle
const int SPELL_TOB_SNAP_KICK = 3794;

View File

@@ -536,7 +536,34 @@ int SilenceDeafnessFailure(object oCaster, int nSpellLevel, int nMetamagic, stri
return FALSE;
}
int Forsaker(object oCaster, object oTarget)
int Forsaker(object oCaster, object oTarget)
{
// Friendly spells autofail on Forsakers
if (GetLevelByClass(CLASS_TYPE_FORSAKER, oTarget) && GetIsFriend(oTarget, oCaster))
{
FloatingTextStringOnCreature("Target is a Forsaker, spell failed!", oCaster, FALSE);
return FALSE;
}
// Forsakers can't use magic
if (GetLevelByClass(CLASS_TYPE_FORSAKER, oCaster))
{
object oSpellCastItem = PRCGetSpellCastItem();
// Allow alchemical items
if(GetIsObjectValid(oSpellCastItem) && GetIsAlchemical(oSpellCastItem))
{
return TRUE;
}
FloatingTextStringOnCreature("Forsakers cannot cast spells!", oCaster, FALSE);
return FALSE;
}
return TRUE;
}
/* int Forsaker(object oCaster, object oTarget)
{
// Friendly spells autofail on Forsakers
if (GetLevelByClass(CLASS_TYPE_FORSAKER, oTarget) && GetIsFriend(oTarget, oCaster))
@@ -553,7 +580,7 @@ int Forsaker(object oCaster, object oTarget)
}
return TRUE;
}
} */
int NSB_SpellCast(object oCaster, int nSpellID, int nCastingClass, int nMetamagic, int nSpellbookType, string sComponent, object oSpellCastItem)
{
@@ -3326,7 +3353,80 @@ int X2PreSpellCastCode2()
int bSpellIsHostile = Get2DACache("spells", "HostileSetting", nOrigSpellID) == "1";
int nSpellbookType = GetSpellbookTypeForClass(nCastingClass);
int nCasterAlignment = GetAlignmentGoodEvil(oCaster);
string sComponent = GetStringUpperCase(Get2DACache("spells", "VS", nSpellID));
string sComponent = GetStringUpperCase(Get2DACache("spells", "VS", nSpellID));
//---------------------------------------------------------------------------
// Check for Circle Magic participant sacrifices.
//---------------------------------------------------------------------------
if (GetLocalInt(oCaster, "CircleMagicSacrifice"))
{
object oLeader = GetLocalObject(oCaster, "CircleMagicLeader");
string sCircleClass = GetLocalString(oLeader, "CircleMagicClass");
// Validate class compatibility
if (sCircleClass == "RED_WIZARD" && GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster) == 0)
{
FloatingTextStringOnCreature("Only Red Wizards may join this circle.", oCaster);
DeleteLocalInt(oCaster, "CircleMagicSacrifice");
DeleteLocalObject(oCaster, "CircleMagicLeader");
return FALSE;
}
if (sCircleClass == "HATHRAN" && GetLevelByClass(CLASS_TYPE_HATHRAN, oCaster) == 0)
{
FloatingTextStringOnCreature("Only Hathrans may join this circle.", oCaster);
DeleteLocalInt(oCaster, "CircleMagicSacrifice");
DeleteLocalObject(oCaster, "CircleMagicLeader");
return FALSE;
}
// Valid case: record contribution and suppress spell
if (oTarget == oLeader && GetLocalInt(oLeader, "CircleMagicActive"))
{
int nLevel = PRCGetSpellLevelForClass(nSpellID, nCastingClass);
int nTotal = GetLocalInt(oLeader, "CircleMagicTotal") + nLevel;
int nParticipants = GetLocalInt(oLeader, "CircleMagicParticipants") + 1;
SetLocalInt(oLeader, "CircleMagicTotal", nTotal);
SetLocalInt(oLeader, "CircleMagicParticipants", nParticipants);
// Suppress the spell
DeleteLocalInt(oCaster, "CircleMagicSacrifice");
DeleteLocalObject(oCaster, "CircleMagicLeader");
PRCSetUserSpecificSpellScriptFinished();
return FALSE; // Prevents spell effects
}
else
{
FloatingTextStringOnCreature("Invalid target or circle not active.", oCaster);
DeleteLocalInt(oCaster, "CircleMagicSacrifice");
DeleteLocalObject(oCaster, "CircleMagicLeader");
return FALSE;
}
}
//---------------------------------------------------------------------------
// Check for Circle Leader finalization.
//---------------------------------------------------------------------------
if (GetLocalInt(oCaster, "CircleMagicActive"))
{
int nTotal = GetLocalInt(oCaster, "CircleMagicTotal");
int nParticipants = GetLocalInt(oCaster, "CircleMagicParticipants");
if (nParticipants >= 2 && nTotal > 0)
{
// Apply augmentation via caster level adjustment variable
SetLocalInt(oCaster, PRC_CASTERLEVEL_ADJUSTMENT, nTotal / 2);
FloatingTextStringOnCreature("Circle Magic augmentation applied (" + IntToString(nTotal) + " levels).", oCaster);
}
else
{
FloatingTextStringOnCreature("Circle Magic requires at least two participants to apply a bonus.", oCaster);
}
// Clear circle state
DeleteLocalInt(oCaster, "CircleMagicActive");
DeleteLocalInt(oCaster, "CircleMagicTotal");
DeleteLocalString(oCaster, "CircleMagicClass");
DeleteLocalInt(oCaster, "CircleMagicMaxParticipants");
DeleteLocalInt(oCaster, "CircleMagicParticipants");
}
//---------------------------------------------------------------------------
// This small addition will check to see if the target is mounted and the
@@ -3408,11 +3508,11 @@ int X2PreSpellCastCode2()
ApplyInfusionPoison(oCaster, nItemCL);
nContinue = FALSE;
}
}
}
//---------------------------------------------------------------------------
// No casting while using expertise
//---------------------------------------------------------------------------
// No casting while using expertise
//---------------------------------------------------------------------------
if(nContinue)
if (GetActionMode(oCaster, ACTION_MODE_EXPERTISE) || GetActionMode(oCaster, ACTION_MODE_IMPROVED_EXPERTISE))
{
@@ -3481,8 +3581,7 @@ int X2PreSpellCastCode2()
nContinue = Nondetection(oTarget, oCaster, nSchool, nCasterLevel);
//---------------------------------------------------------------------------
// Mystery Effects
//---------------------------------------------------------------------------
// Mystery Effects //---------------------------------------------------------------------------
if (nContinue)
nContinue = WarpSpell(oCaster, nSpellID);
@@ -3493,8 +3592,7 @@ int X2PreSpellCastCode2()
nContinue = FloodShadow(oCaster, nSpellID);
//---------------------------------------------------------------------------
// Incarnum Effects
//---------------------------------------------------------------------------
// Incarnum Effects //---------------------------------------------------------------------------
if (nContinue)
nContinue = MageShackles(oCaster, nSpellID);
@@ -3502,8 +3600,7 @@ int X2PreSpellCastCode2()
nContinue = Abrogation(oCaster, nCasterLevel, nSpellID);
//---------------------------------------------------------------------------
// Binding Effects
//---------------------------------------------------------------------------
// Binding Effects //---------------------------------------------------------------------------
if (nContinue)
nContinue = AmonInfluence(oTarget, oCaster, nSpellID, nSpellLevel, bSpellIsHostile);