2025/12/06 Update
Added Inscribe Epic Runes. Added Great Intelligence and Inscribe Epic Runes as Runecaster epic bons feats. Changed Runecaster epic bonus feat progression to 1 every 4 levels past 10th. Bardic PrCs should be able to take Lingering Song & Extra Music as general feats. Forsakers can't use psionics, invocations, spellfire, shadowcasting, truenaming, binding, soulmelds or Supernatural Martial Maneuvers. Fixed elven courtblade / ambidexterity bug. Added more guardrails to prevent self-damage from onHit spells during PerformAttack(). Updated GetProperTarget() Removed ableist slur. RHD casters should work with JPM now. Reworked Blood in the Water's effect icon. Fixed Seize Item's VFX. RHD casters should be able to enter Dragon Disciple. Sharp Note doesn't step on Dragonfire Inspiration anymore.
This commit is contained in:
@@ -77,6 +77,15 @@ int PreInvocationCastCode()
|
||||
|
||||
int nContinue = !ExecuteScriptAndReturnInt("prespellcode", oInvoker);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Block forsakers from using invocations
|
||||
//---------------------------------------------------------------------------
|
||||
if(GetLevelByClass(CLASS_TYPE_FORSAKER, oInvoker) > 0)
|
||||
{
|
||||
SendMessageToPC(oInvoker, "Forsakers cannot use invocations.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Break any spell require maintaining concentration
|
||||
//---------------------------------------------------------------------------
|
||||
@@ -168,3 +177,4 @@ int PreInvocationCastCode()
|
||||
return nContinue;
|
||||
}
|
||||
|
||||
//:: void main (){}
|
||||
@@ -444,6 +444,7 @@ int Get2DALineFromItemprop(string sFile, itemproperty ip, object oItem)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ITEM_PROPERTY_ECHOBLADE: return 46; break;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
@@ -1319,6 +1319,7 @@ const int FEAT_SOMATIC_WEAPONRY = 5186;
|
||||
|
||||
// Forgotten Realms Campaign Setting
|
||||
const int FEAT_INSCRIBE_RUNE = 2462;
|
||||
const int EPIC_FEAT_INSCRIBE_EPIC_RUNES = 2549;
|
||||
|
||||
// Miniature Handbook
|
||||
const int FEAT_SHIELDMATE = 3258;
|
||||
|
||||
@@ -426,7 +426,7 @@ int Vile_Feat(int iTypeWeap)
|
||||
GetHasFeat(FEAT_VILE_MARTIAL_RAPIER) ||
|
||||
GetHasFeat(FEAT_VILE_MARTIAL_ELVEN_THINBLADE));
|
||||
|
||||
case BASE_ITEM_ELVEN_COURTBLADE: return GetHasFeat(FEAT_VILE_MARTIAL_GREATSWORD ||
|
||||
case BASE_ITEM_ELVEN_COURTBLADE: return (GetHasFeat(FEAT_VILE_MARTIAL_GREATSWORD) ||
|
||||
GetHasFeat(FEAT_VILE_MARTIAL_ELVEN_COURTBLADE));
|
||||
|
||||
case BASE_ITEM_DOUBLE_SCIMITAR: return GetHasFeat(FEAT_VILE_MARTIAL_DBL_SCIMITAR);
|
||||
|
||||
@@ -1322,7 +1322,29 @@ int DoTrip(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE,
|
||||
DelayCommand(0.0, PerformAttack(oTarget, oPC, eNone, 0.0, 0, 0, 0, "Improved Trip Free Attack Hit", "Improved Trip Free Attack Miss"));
|
||||
}
|
||||
}
|
||||
else // If you fail, enemy gets a counter trip attempt, using Strength
|
||||
else // If you fail, enemy gets a counter trip attempt, using Strength
|
||||
{
|
||||
if(!nCounterTrip)
|
||||
{
|
||||
nTargetStat = GetAbilityModifier(ABILITY_STRENGTH, oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_TRIP, FALSE, TRUE);
|
||||
FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE);
|
||||
// Roll counter trip attempt
|
||||
nTargetCheck = nTargetStat + nTargetBonus + d20();
|
||||
nPCCheck = nPCStat + nPCBonus + d20();
|
||||
// If counters aren't allowed, don't knock em down
|
||||
// Its down here to allow the text message to go through
|
||||
SendMessageToPC(oPC, "Enemy Counter Trip Check: "+IntToString(nPCCheck)+" vs "+IntToString(nTargetCheck));
|
||||
|
||||
SetLocalInt(oPC, "TripDifference", nTargetCheck - nPCCheck);
|
||||
DelayCommand(2.0, DeleteLocalInt(oPC, "TripDifference"));
|
||||
}
|
||||
if (nTargetCheck >= nPCCheck && nCounterTrip)
|
||||
{
|
||||
// Knock em down
|
||||
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectKnockdown()), oPC, 6.0);
|
||||
}
|
||||
}
|
||||
/* else // If you fail, enemy gets a counter trip attempt, using Strength
|
||||
{
|
||||
nTargetStat = GetAbilityModifier(ABILITY_STRENGTH, oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_TRIP, FALSE, TRUE);
|
||||
FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE);
|
||||
@@ -1339,7 +1361,7 @@ int DoTrip(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE,
|
||||
}
|
||||
SetLocalInt(oPC, "TripDifference", nTargetCheck - nPCCheck);
|
||||
DelayCommand(2.0, DeleteLocalInt(oPC, "TripDifference"));
|
||||
}
|
||||
} */
|
||||
}
|
||||
else
|
||||
FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE);
|
||||
|
||||
@@ -1,11 +1,68 @@
|
||||
//:: prc_inc_nat_hb
|
||||
//::
|
||||
//:: void main(){}
|
||||
|
||||
|
||||
void DoNaturalWeaponHB(object oPC = OBJECT_SELF);
|
||||
|
||||
#include "prc_inc_combat"
|
||||
#include "prc_inc_template"
|
||||
|
||||
object GetProperTarget(object oPC, object oTarget)
|
||||
/**
|
||||
* Finds a valid enemy target in melee range when the original target is invalid.
|
||||
* Now includes input validation, LOS checks, configurable radius, and target priority.
|
||||
*
|
||||
* @param oPC The creature seeking a new target
|
||||
* @param oTarget The original (invalid) target
|
||||
* @param fRadius Search radius in meters (optional, defaults to melee range)
|
||||
* @return A valid enemy target or OBJECT_INVALID if none found
|
||||
*/
|
||||
object GetProperTarget(object oPC, object oTarget, float fRadius = MELEE_RANGE_METERS)
|
||||
{
|
||||
// Input validation
|
||||
if(!GetIsObjectValid(oPC))
|
||||
{
|
||||
DoDebug("GetProperTarget(): Invalid oPC parameter");
|
||||
return OBJECT_INVALID;
|
||||
}
|
||||
|
||||
// Use target list system for better target selection
|
||||
PurgeTargetList(oPC);
|
||||
|
||||
location lPC = GetLocation(oPC);
|
||||
object oTest = MyFirstObjectInShape(SHAPE_SPHERE, fRadius, lPC, TRUE, OBJECT_TYPE_CREATURE);
|
||||
|
||||
while(GetIsObjectValid(oTest))
|
||||
{
|
||||
// Basic validation checks
|
||||
if(oTest != oPC && // Not self
|
||||
GetIsEnemy(oPC, oTest) && // Is enemy
|
||||
GetIsInMeleeRange(oPC, oTest) && // In melee range
|
||||
!GetIsDead(oTest) && // Is alive
|
||||
LineOfSightObject(oPC, oTest)) // Has line of sight
|
||||
{
|
||||
// Add to target list with priority based on distance (nearest first)
|
||||
AddToTargetList(oTest, oPC, INSERTION_BIAS_DISTANCE, FALSE);
|
||||
}
|
||||
|
||||
oTest = MyNextObjectInShape(SHAPE_SPHERE, fRadius, lPC, TRUE, OBJECT_TYPE_CREATURE);
|
||||
}
|
||||
|
||||
// Get the highest priority target (nearest enemy)
|
||||
object oBestTarget = GetTargetListHead(oPC);
|
||||
PurgeTargetList(oPC);
|
||||
|
||||
if(GetIsObjectValid(oBestTarget))
|
||||
{
|
||||
DoDebug("GetProperTarget(): Selected target " + GetName(oBestTarget) +
|
||||
" for " + GetName(oPC));
|
||||
return oBestTarget;
|
||||
}
|
||||
|
||||
// No valid target found
|
||||
DoDebug("GetProperTarget(): No valid target found for " + GetName(oPC));
|
||||
return OBJECT_INVALID;
|
||||
}
|
||||
/* object GetProperTarget(object oPC, object oTarget)
|
||||
{
|
||||
location lTarget = GetLocation(oPC);
|
||||
// Use the function to get the closest creature as a target
|
||||
@@ -23,7 +80,7 @@ object GetProperTarget(object oPC, object oTarget)
|
||||
}
|
||||
|
||||
return oTarget;
|
||||
}
|
||||
} */
|
||||
|
||||
void DoNaturalAttack(object oWeapon)
|
||||
{
|
||||
@@ -452,6 +509,16 @@ void DoNaturalWeaponHB(object oPC = OBJECT_SELF)
|
||||
oWeapon = CreateObject(OBJECT_TYPE_ITEM, sResRef, lLimbo);
|
||||
DoDebug(PRC_TEXT_WHITE + "prc_inc_nat_hb >> DoNaturalWeaponHB: creature weapon object found!!!");
|
||||
}
|
||||
|
||||
// Check for enhancements after creating the weapon object
|
||||
int nEnhance = GetLocalInt(oPC, "PRC_NAT_WEAPON_ENHANCE");
|
||||
if(nEnhance > 0)
|
||||
{
|
||||
|
||||
DoDebug(PRC_TEXT_WHITE + "prc_inc_nat_hb >> DoNaturalWeaponHB: Applying enhancement.");
|
||||
float fDuration = GetLocalFloat(oPC, "PRC_NAT_WEAPON_ENH_DUR");
|
||||
IPSafeAddItemProperty(oWeapon, ItemPropertyEnhancementBonus(nEnhance), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
|
||||
}
|
||||
|
||||
DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: scheduling a secondary natural attack with "+GetName(oWeapon)+" at delay "+FloatToString(fDelay));
|
||||
//do the attack within a delay
|
||||
|
||||
@@ -302,7 +302,7 @@ object CICraftBrewPotion(object oCreator, int nSpellID )
|
||||
return OBJECT_INVALID;
|
||||
}
|
||||
|
||||
/* //just a tad retarded, don't you think? other crafting feats are not similarly restricted
|
||||
/* //just a tad silly, don't you think? other crafting feats are not similarly restricted
|
||||
//Uses per day
|
||||
int nUsesAllowed;
|
||||
|
||||
@@ -710,6 +710,7 @@ object CICraftScribeScroll(object oCreator, int nSpellID)
|
||||
return oTarget;
|
||||
}
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Returns TRUE if the player used the last spell to brew a potion
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -865,7 +866,6 @@ These dont work as IPs since they are hardcoded */
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Returns TRUE if the player used the last spell to create a scroll
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -1133,6 +1133,9 @@ These dont work as IPs since they are hardcoded */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Returns TRUE if the player used the last spell to craft a staff
|
||||
// -----------------------------------------------------------------------------
|
||||
int CICraftCheckCraftStaff(object oSpellTarget, object oCaster, int nSpellID = 0)
|
||||
{
|
||||
|
||||
@@ -1281,6 +1284,9 @@ These dont work as IPs since they are hardcoded */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Returns TRUE if the player used the last spell to craft a rod
|
||||
// -----------------------------------------------------------------------------
|
||||
int CICraftCheckCraftRod(object oSpellTarget, object oCaster, int nSpellID = 0)
|
||||
{
|
||||
|
||||
@@ -1427,6 +1433,7 @@ These dont work as IPs since they are hardcoded */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, int nSpell = 0)
|
||||
{
|
||||
if(!GetIsObjectValid(oCaster)) oCaster = OBJECT_SELF;
|
||||
@@ -1478,8 +1485,9 @@ int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALI
|
||||
if(!GetIsObjectValid(oTarget)) oTarget = PRCGetSpellTargetObject();
|
||||
int nCaster = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster));
|
||||
|
||||
//:: [TO DO] make Inscribe Epic Rune.
|
||||
if(nCaster > 20) nCaster = 20;
|
||||
//:: Check for Inscribe Epic Runes and cap CL at 20 if it doesn't exist.
|
||||
int bEpicRunes = GetHasFeat(EPIC_FEAT_INSCRIBE_EPIC_RUNES, oCaster);
|
||||
if (!bEpicRunes) { if(nCaster > 20) nCaster = 20; }
|
||||
|
||||
int nDC = PRCGetSaveDC(oTarget, oCaster);
|
||||
if(!nSpell) nSpell = PRCGetSpellId();
|
||||
|
||||
@@ -75,6 +75,15 @@ int PsiPrePowerCastCode()
|
||||
|
||||
int nContinue = !ExecuteScriptAndReturnInt("prespellcode", oManifester);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Forsakers can't use psionics
|
||||
//---------------------------------------------------------------------------
|
||||
if (nContinue && GetLevelByClass(CLASS_TYPE_FORSAKER, oManifester))
|
||||
{
|
||||
FloatingTextStringOnCreature("Forsakers cannot manifest psionic powers!", oManifester, FALSE);
|
||||
nContinue = FALSE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Break any spell require maintaining concentration
|
||||
//---------------------------------------------------------------------------
|
||||
@@ -209,3 +218,4 @@ int PsiPrePowerCastCode()
|
||||
return nContinue;
|
||||
}
|
||||
|
||||
//:: void main (){}
|
||||
@@ -132,6 +132,15 @@ int ShadPreMystCastCode()
|
||||
|
||||
int nContinue = !ExecuteScriptAndReturnInt("prespellcode",oShadow);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Block forsakers from using shadowcasting
|
||||
//---------------------------------------------------------------------------
|
||||
if(GetLevelByClass(CLASS_TYPE_FORSAKER, oShadow) > 0)
|
||||
{
|
||||
SendMessageToPC(oShadow, "Forsakers cannot use the power of shadowcasting.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Break any spell require maintaining concentration
|
||||
//---------------------------------------------------------------------------
|
||||
@@ -277,4 +286,6 @@ int ShadPreMystCastCode()
|
||||
|
||||
if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #6: " + IntToString(nContinue));
|
||||
return nContinue;
|
||||
}
|
||||
}
|
||||
|
||||
//:: void main (){}
|
||||
|
||||
@@ -78,7 +78,14 @@ int PreManeuverCastCode()
|
||||
//---------------------------------------------------------------------------
|
||||
if(nContinue)
|
||||
nContinue = !GetLocalInt(oInitiator, "CrusaderBreak");
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Forsakers can't use supernatural maneuvers
|
||||
//---------------------------------------------------------------------------
|
||||
if (nContinue && GetIsManeuverSupernatural(nMoveId) && GetLevelByClass(CLASS_TYPE_FORSAKER, oInitiator))
|
||||
{
|
||||
FloatingTextStringOnCreature("Forsakers cannot use supernatural maneuvers!", oInitiator, FALSE);
|
||||
nContinue = FALSE;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
// Run NullPsionicsField Check
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -42,6 +42,16 @@ int TruePreUtterCastCode()
|
||||
|
||||
int nContinue = !ExecuteScriptAndReturnInt("prespellcode",oTrueSpeaker);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Block forsakers from using truenaming
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
if(GetLevelByClass(CLASS_TYPE_FORSAKER, oTrueSpeaker) > 0)
|
||||
{
|
||||
SendMessageToPC(oTrueSpeaker, "Forsakers cannot use the power of truenaming.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Break any spell require maintaining concentration
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user