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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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!!! */
|
||||
|
||||
|
||||
@@ -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 (){}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user