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:
Jaysyn904
2025-12-06 22:55:23 -05:00
parent a4fd246266
commit ce7d550670
29 changed files with 641 additions and 228 deletions

View File

@@ -120,8 +120,15 @@ void main()
object oSpellTarget = PRCGetSpellTargetObject(oSpellOrigin); // On a weapon: The one being hit. On an armor: The one hitting the armor
// Make sure you don't hit yourself.
if (oSpellOrigin == oSpellTarget)
oSpellTarget = GetProperTarget(oSpellOrigin, oSpellTarget);
if (oSpellOrigin == oSpellTarget || !GetIsEnemy(oSpellOrigin, oSpellTarget))
{
if (DEBUG) DoDebug("prc_onhitcast: Preventing on-hit spell on non-enemy or self");
// Clear the local int and exit without applying on-hit effects
DeleteLocalInt(oSpellOrigin, "prc_ohc");
return;
}
/* if (oSpellOrigin == oSpellTarget)
oSpellTarget = GetProperTarget(oSpellOrigin, oSpellTarget); */
// motu99: replacing call to Bioware's GetSpellCastItem with new PRC wrapper function
// will ensure that we retrieve a valid item when we are called from scripted combat (prc_inc_combat) or

View File

@@ -1462,7 +1462,60 @@ void LichPrereq(object oPC)
SetLocalInt(oPC, "PRC_PrereqLich", 0);
}
void DragDisciple(object oPC)
void DragDisciple(object oPC)
{
int bRace = FALSE;
int bSpells = FALSE;
SetLocalInt(oPC, "PRC_PrereqDrDis", 1);
//Any nondragon (cannot already be a half-dragon)
int nRace = GetRacialType(oPC);
if(!GetHasTemplate(TEMPLATE_HALF_DRAGON, oPC)
&& nRace != RACIAL_TYPE_SPIRETOPDRAGON
&& nRace != RACIAL_TYPE_BOZAK
&& nRace != RACIAL_TYPE_KAPAK)
bRace = TRUE;
// Ability to cast arcane spells without preparation
// (dragon blooded feat eliminates that requirement)
if(GetHasFeat(DRAGON_BLOODED, oPC))
bSpells = TRUE;
else if(GetLevelByClass(CLASS_TYPE_ASSASSIN, oPC)
|| GetLevelByClass(CLASS_TYPE_BARD, oPC)
|| GetLevelByClass(CLASS_TYPE_BEGUILER, oPC)
|| GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oPC)
|| GetLevelByClass(CLASS_TYPE_DUSKBLADE, oPC)
|| GetLevelByClass(CLASS_TYPE_HEXBLADE, oPC)
|| GetLevelByClass(CLASS_TYPE_KNIGHT_WEAVE, oPC)
|| GetLevelByClass(CLASS_TYPE_SORCERER, oPC)
|| GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH, oPC)
|| GetLevelByClass(CLASS_TYPE_WARMAGE, oPC)
|| GetLevelByClass(CLASS_TYPE_WITCH, oPC))
{
if(!GetLocalInt(oPC, "PRC_ArcSpell0")
|| !GetLocalInt(oPC, "PRC_ArcSpell1"))
bSpells = TRUE;
}
// Racial spellcasters that qualify via racial hit dice
// They have innate ability to cast arcane spells without preparation
if(nRace == RACIAL_TYPE_ARANEA
|| nRace == RACIAL_TYPE_RAKSHASA
|| nRace == RACIAL_TYPE_DRIDER
|| nRace == RACIAL_TYPE_ARKAMOI
|| nRace == RACIAL_TYPE_HOBGOBLIN_WARSOUL
|| nRace == RACIAL_TYPE_REDSPAWN_ARCANISS
|| nRace == RACIAL_TYPE_MARRUTACT
|| nRace == RACIAL_TYPE_GLOURA)
{
bSpells = TRUE;
}
if(bRace && bSpells)
SetLocalInt(oPC, "PRC_PrereqDrDis", 0);
}
/* void DragDisciple(object oPC)
{
int bRace = FALSE;
int bSpells = FALSE;
@@ -1500,7 +1553,7 @@ void DragDisciple(object oPC)
if(bRace && bSpells)
SetLocalInt(oPC, "PRC_PrereqDrDis", 0);
}
*/
void WarlockPrCs(object oPC)
{
SetLocalInt(oPC, "PRC_PrereqHFWar", 1);

View File

@@ -23,6 +23,14 @@ Called Elsewhere:
void main()
{
object oPC = OBJECT_SELF;
// Block forsakers from using spellfire
if(GetLevelByClass(CLASS_TYPE_FORSAKER, oPC) > 0)
{
SendMessageToPC(oPC, "Forsakers cannot use the power of spellfire.");
return;
}
if(GetHasFeat(FEAT_SHADOWWEAVE, oPC))
{
SendMessageToPC(oPC, "You no longer have access to the weave and cannot use spellfire");

View File

@@ -221,7 +221,47 @@ void main()
break;
}
case SPELL_VIRTUOSO_SHARP_NOTE:
case SPELL_VIRTUOSO_SHARP_NOTE:
{
oItem = IPGetTargetedOrEquippedMeleeWeapon();
if(GetIsObjectValid(oItem))
{
// Check if Dragonfire Inspiration is active and preserve its properties
int bHasDragonfire = GetHasFeat(FEAT_DRAGONFIRE_INSPIRATION, oPC) &&
GetLocalInt(oPC, "DragonFireInspirationOn");
// Store existing Dragonfire properties if present
itemproperty ipDragonfire = GetFirstItemProperty(oItem);
int bDragonfireFound = FALSE;
if(bHasDragonfire)
{
ipDragonfire = GetFirstItemProperty(oItem);
while(GetIsItemPropertyValid(ipDragonfire))
{
if(GetItemPropertyType(ipDragonfire) == ITEM_PROPERTY_ONHITCASTSPELL)
{
bDragonfireFound = TRUE;
break;
}
ipDragonfire = GetNextItemProperty(oItem);
}
}
// Apply Keen property
nTemp = StringToInt(Get2DACache("baseitems", "WeaponType", GetBaseItemType(oItem)));
if(nTemp && (nTemp != 2)) //piercing and slashing weapons
IPSafeAddItemProperty(oItem,ItemPropertyKeen(), 600.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING ,TRUE,TRUE);
// Restore Dragonfire property if it was removed
if(bHasDragonfire && bDragonfireFound && !GetItemHasItemProperty(oItem, ITEM_PROPERTY_ONHITCASTSPELL))
{
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 99999.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE);
}
}
break;
}
/* case SPELL_VIRTUOSO_SHARP_NOTE:
{
oItem = IPGetTargetedOrEquippedMeleeWeapon();
if(GetIsObjectValid(oItem))
@@ -231,7 +271,7 @@ void main()
IPSafeAddItemProperty(oItem,ItemPropertyKeen(), 600.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING ,TRUE,TRUE);
}
break;
}
} */
case SPELL_VIRTUOSO_MINDBENDING_MELODY:
{
break;