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.
322 lines
12 KiB
Plaintext
322 lines
12 KiB
Plaintext
/*
|
|
prc_virtuoso
|
|
|
|
Handles Virtuoso feats
|
|
|
|
WARNING: Many of these don't care about
|
|
faction, will affect both allies and enemies!
|
|
|
|
Designed along the lines of Bard Song
|
|
|
|
By: Flaming_Sword
|
|
Created: Jul 8, 2006
|
|
Modified: Jul 9, 2006
|
|
*/
|
|
|
|
#include "prc_inc_clsfunc"
|
|
|
|
void main()
|
|
{
|
|
object oPC = OBJECT_SELF;
|
|
if (PRCGetHasEffect(EFFECT_TYPE_SILENCE,oPC))
|
|
{
|
|
FloatingTextStrRefOnCreature(85764,oPC); // not useable when silenced
|
|
return;
|
|
}
|
|
int nSpellID = GetSpellId();
|
|
if(!VirtuosoPerformanceDecrement(oPC, nSpellID))
|
|
{
|
|
SendMessageToPC(oPC, "You do not have enough daily uses of Virtuoso Performance to use this ability.");
|
|
return;
|
|
}
|
|
object oTarget = PRCGetSpellTargetObject();
|
|
int nDuration = 10;
|
|
//Check to see if the caster has Lasting Impression and increase duration.
|
|
if(GetHasFeat(870)) nDuration *= 10;
|
|
// lingering song
|
|
if(GetHasFeat(424)) nDuration += 5;
|
|
float fDuration = RoundsToSeconds(nDuration);
|
|
|
|
object oItem;
|
|
int nTemp;
|
|
effect eLink;
|
|
int nCasterLevel = PRCGetCasterLevel(oPC);
|
|
int nPerform = GetSkillRank(SKILL_PERFORM, oPC);
|
|
//Constructing effects
|
|
switch(nSpellID)
|
|
{
|
|
case SPELL_VIRTUOSO_SUSTAINING_SONG:
|
|
{
|
|
if(nPerform < 11)
|
|
{
|
|
SendMessageToPC(oPC, "You do not have enough ranks in Perform to use this ability.");
|
|
return;
|
|
}
|
|
|
|
//insert pnp stabilisation code here
|
|
eLink = EffectVisualEffect(VFX_DUR_PROTECTION_GOOD_MINOR);
|
|
eLink = EffectLinkEffects(eLink, EffectRegenerate(1, 6.0));
|
|
eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE));
|
|
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_CALUMNY:
|
|
{
|
|
if(nPerform < 13)
|
|
{
|
|
SendMessageToPC(oPC, "You do not have enough ranks in Perform to use this ability.");
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_JARRING_SONG:
|
|
{
|
|
if(nPerform < 14)
|
|
{
|
|
SendMessageToPC(oPC, "You do not have enough ranks in Perform to use this ability.");
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_SHARP_NOTE:
|
|
{
|
|
if(nPerform < 15)
|
|
{
|
|
SendMessageToPC(oPC, "You do not have enough ranks in Perform to use this ability.");
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_MINDBENDING_MELODY:
|
|
{
|
|
if(nPerform < 16)
|
|
{
|
|
SendMessageToPC(oPC, "You do not have enough ranks in Perform to use this ability.");
|
|
return;
|
|
}
|
|
eLink = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_DOMINATED);
|
|
eLink = EffectLinkEffects(eLink, PRCGetScaledEffect(EffectDominated(), oTarget));
|
|
eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));
|
|
|
|
int nRacial = MyPRCGetRacialType(oTarget);
|
|
//Fire cast spell at event for the specified target
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_DOMINATE_PERSON, FALSE));
|
|
//Make sure the target is a humanoid
|
|
if(!GetIsReactionTypeFriendly(oTarget))
|
|
{
|
|
if ((nRacial == RACIAL_TYPE_DWARF) ||
|
|
(nRacial == RACIAL_TYPE_ELF) ||
|
|
(nRacial == RACIAL_TYPE_GNOME) ||
|
|
(nRacial == RACIAL_TYPE_HALFLING) ||
|
|
(nRacial == RACIAL_TYPE_HUMAN) ||
|
|
(nRacial == RACIAL_TYPE_HALFELF) ||
|
|
(nRacial == RACIAL_TYPE_HALFORC))
|
|
{
|
|
//Make SR Check
|
|
if (!PRCDoResistSpell(oPC, oTarget, nCasterLevel + SPGetPenetr()))
|
|
{
|
|
//Make Will Save
|
|
if (!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, 15 + GetAbilityModifier(ABILITY_CHARISMA, oPC), SAVING_THROW_TYPE_MIND_SPELLS, OBJECT_SELF, 1.0))
|
|
{
|
|
DelayCommand(1.0, SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, fDuration,TRUE,-1,nCasterLevel));
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DOMINATE_S), oTarget);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_GREATER_CALUMNY:
|
|
{
|
|
if(nPerform < 17)
|
|
{
|
|
SendMessageToPC(oPC, "You do not have enough ranks in Perform to use this ability.");
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_MAGICAL_MELODY:
|
|
{ //dummy effect, good for checking if there is a spell effect
|
|
if(nPerform < 18)
|
|
{
|
|
SendMessageToPC(oPC, "You do not have enough ranks in Perform to use this ability.");
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_SONG_OF_FURY:
|
|
{
|
|
if(nPerform < 19)
|
|
{
|
|
SendMessageToPC(oPC, "You do not have enough ranks in Perform to use this ability.");
|
|
return;
|
|
}
|
|
eLink = EffectAbilityIncrease(ABILITY_CONSTITUTION, 4);
|
|
eLink = EffectLinkEffects(eLink, EffectAbilityIncrease(ABILITY_STRENGTH, 4));
|
|
eLink = EffectLinkEffects(eLink, EffectSavingThrowIncrease(SAVING_THROW_WILL, 2));
|
|
eLink = EffectLinkEffects(eLink, EffectACDecrease(2, AC_DODGE_BONUS));
|
|
eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE));
|
|
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_REVEALING_MELODY:
|
|
{
|
|
if(nPerform < 20)
|
|
{
|
|
SendMessageToPC(oPC, "You do not have enough ranks in Perform to use this ability.");
|
|
return;
|
|
}
|
|
effect eVis = EffectVisualEffect(VFX_DUR_MAGICAL_SIGHT);
|
|
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE);
|
|
effect eSight = EffectTrueSeeing();
|
|
if(GetPRCSwitch(PRC_PNP_TRUESEEING))
|
|
{
|
|
eSight = EffectSeeInvisible();
|
|
int nSpot = GetPRCSwitch(PRC_PNP_TRUESEEING_SPOT_BONUS);
|
|
if(nSpot == 0)
|
|
nSpot = 15;
|
|
effect eSpot = EffectSkillIncrease(SKILL_SPOT, nSpot);
|
|
effect eUltra = EffectUltravision();
|
|
eSight = EffectLinkEffects(eSight, eSpot);
|
|
eSight = EffectLinkEffects(eSight, eUltra);
|
|
}
|
|
eLink = EffectLinkEffects(eVis, eSight);
|
|
eLink = EffectLinkEffects(eLink, eDur);
|
|
|
|
break;
|
|
}
|
|
}
|
|
eLink = ExtraordinaryEffect(eLink);
|
|
|
|
object oAffected = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_COLOSSAL, GetLocation(oPC));
|
|
while(GetIsObjectValid(oAffected))
|
|
{
|
|
|
|
PRCRemoveEffectsFromSpell(oAffected, nSpellID);
|
|
if(oAffected == oPC)
|
|
{
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectVisualEffect(VFX_DUR_BARD_SONG)), oAffected, fDuration,TRUE,-1,nCasterLevel);
|
|
}
|
|
switch(nSpellID)
|
|
{
|
|
case SPELL_VIRTUOSO_SUSTAINING_SONG:
|
|
{
|
|
//insert pnp stabilisation code here
|
|
if(GetCurrentHitPoints(oAffected) <= 10) //arbitrary check for now
|
|
{
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oAffected, fDuration,TRUE,-1,nCasterLevel);
|
|
}
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_CALUMNY:
|
|
{
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_JARRING_SONG:
|
|
{
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectVisualEffect(VFX_DUR_MIND_AFFECTING_NEGATIVE)), oAffected, fDuration,TRUE,-1,nCasterLevel);
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE)), oAffected, fDuration,TRUE,-1,nCasterLevel);
|
|
|
|
break;
|
|
}
|
|
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))
|
|
{
|
|
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);
|
|
}
|
|
break;
|
|
} */
|
|
case SPELL_VIRTUOSO_MINDBENDING_MELODY:
|
|
{
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_GREATER_CALUMNY:
|
|
{
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_MAGICAL_MELODY:
|
|
{
|
|
if(spellsIsTarget(oAffected, SPELL_TARGET_ALLALLIES, oPC))
|
|
{
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectVisualEffect(VFX_DUR_PROTECTION_GOOD_MINOR)), oAffected, fDuration,TRUE,-1,nCasterLevel);
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE)), oAffected, fDuration,TRUE,-1,nCasterLevel);
|
|
}
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_SONG_OF_FURY:
|
|
{
|
|
if(spellsIsTarget(oAffected, SPELL_TARGET_ALLALLIES, oPC))
|
|
{
|
|
int iVoice;
|
|
switch(d3())
|
|
{
|
|
case 1: iVoice = VOICE_CHAT_BATTLECRY1;
|
|
break;
|
|
case 2: iVoice = VOICE_CHAT_BATTLECRY2;
|
|
break;
|
|
case 3: iVoice = VOICE_CHAT_BATTLECRY3;
|
|
break;
|
|
}
|
|
PlayVoiceChat(iVoice, oAffected);
|
|
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oAffected, fDuration,TRUE,-1,nCasterLevel);
|
|
}
|
|
break;
|
|
}
|
|
case SPELL_VIRTUOSO_REVEALING_MELODY:
|
|
{
|
|
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oAffected, fDuration,TRUE,-1,17);
|
|
|
|
break;
|
|
}
|
|
}
|
|
oAffected = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_COLOSSAL, GetLocation(oPC));
|
|
}
|
|
} |