Updated AMS marker feats. Removed arcane & divine marker feats. Updated Dread Necromancer for epic progression. Updated weapon baseitem models. Updated new weapons for crafting & npc equip. Updated prefix. Updated release archive.
734 lines
32 KiB
Plaintext
734 lines
32 KiB
Plaintext
//::///////////////////////////////////////////////
|
|
//:: User Defined OnHitCastSpell code
|
|
//:: prc_onhitcast
|
|
//:://////////////////////////////////////////////
|
|
/*
|
|
This file holds all the OnHitCastSpell events
|
|
used by PRC.
|
|
It was created to replace x2_s3_onhitcast so that
|
|
it wouldn't override module-specific onhitcast events.
|
|
|
|
Add your own entries after the previous ones. Try to
|
|
keep variable scope as little as possible. ie, no top-
|
|
level variables if you just can avoid it.
|
|
If your entry is long (over 20 lines), consider placing
|
|
the guts of it outside the main to improve readability
|
|
for the rest of us :D
|
|
|
|
|
|
Please remember to comment your entry.
|
|
At least mention what class ability / spell / whatever
|
|
it is part of.
|
|
*/
|
|
//:://////////////////////////////////////////////
|
|
//:: Created By: Various people
|
|
//:://////////////////////////////////////////////
|
|
|
|
#include "psi_inc_onhit"
|
|
#include "inc_rend"
|
|
#include "psi_inc_ac_const"
|
|
#include "prc_inc_onhit"
|
|
#include "tob_inc_tobfunc"
|
|
#include "prc_inc_sp_tch"
|
|
|
|
void SetRancorVar(object oPC);
|
|
void SetImprovedRicochetVar(object oPC);
|
|
void DoImprovedRicochet(object oPC, object oTarget);
|
|
|
|
object GetProperTarget(object oPC, object oTarget)
|
|
{
|
|
location lTarget = GetLocation(oPC);
|
|
// Use the function to get the closest creature as a target
|
|
object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
while(GetIsObjectValid(oAreaTarget))
|
|
{
|
|
if(oAreaTarget != oPC && // Not you.
|
|
GetIsEnemy(oPC, oAreaTarget) && // Enemies only, please
|
|
GetIsInMeleeRange(oPC, oAreaTarget)) // They must be in melee range
|
|
{
|
|
return oAreaTarget;
|
|
}
|
|
//Select the next target within the spell shape.
|
|
oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
}
|
|
|
|
return oTarget;
|
|
}
|
|
|
|
void main()
|
|
{
|
|
object oSpellOrigin = OBJECT_SELF; // On a weapon: The one wielding the weapon. On an armor: The one wearing an armor
|
|
|
|
// Call the normal OnHitCastSpell: Unique script
|
|
if (DEBUG) DoDebug("prc_onhitcast: entered, executing normal onhitcastspell unique power script x2_s3_onhitcast for "+GetName(oSpellOrigin));
|
|
ExecuteScript("x2_s3_onhitcast", oSpellOrigin);
|
|
|
|
// motu99: setting a local int, so that onhitcast impact spell scripts can find out, whether they were called from prc_onhitcast
|
|
// or from somewhere else (presumably the aurora engine). This local int will be deleted just before we exit prc_onhitcast
|
|
// Note that any scripts that are called by a DelayCommand (or AssignCommand) from prc_onhitcast will not find this local int
|
|
SetLocalInt(oSpellOrigin, "prc_ohc", TRUE);
|
|
|
|
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. Some idiot didn't check that.
|
|
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
|
|
object oItem = PRCGetSpellCastItem(oSpellOrigin); // The item casting triggering this spellscript
|
|
int iItemBaseType = GetBaseItemType(oItem);
|
|
|
|
|
|
// DEBUG code, remove
|
|
if (DEBUG)
|
|
{
|
|
if(!GetIsOnHitCastSpell(oSpellTarget, oItem, oSpellOrigin))
|
|
DoDebug("prc_onhitcast: Warning, the currently running instance of prc_onhitcast was not recognized as an onhitcastspell");
|
|
}
|
|
// DEBUG code, remove
|
|
|
|
if (DEBUG) DoDebug("prc_onhitcast: now executing prc specific routines with item = "+ GetName(oItem)+", target = "+GetName(oSpellTarget)+", caller = "+GetName(oSpellOrigin)+", item type = "+IntToString(iItemBaseType));
|
|
|
|
// int nBArcher; // Blood Archer level
|
|
int nFoeHunter = GetLevelByClass(CLASS_TYPE_FOE_HUNTER, oSpellOrigin); // Foe Hunter Level
|
|
//int bItemIsWeapon;
|
|
|
|
//nBArcher = GetLevelByClass(CLASS_TYPE_BLARCHER, oSpellOrigin);
|
|
|
|
//// Swashbuckler Weakening and Wounding Criticals
|
|
if(GetHasFeat(INSIGHTFUL_STRIKE, oSpellOrigin))
|
|
ExecuteScript("prc_swashweak", oSpellOrigin);
|
|
|
|
//// Champion of Corellon damage healing for sneak/critical immune creatures
|
|
if(GetHasFeat(FEAT_COC_ELEGANT_STRIKE, oSpellOrigin))
|
|
ExecuteScript("prc_coc_heal", oSpellOrigin);
|
|
|
|
//// Stormlord Shocking & Thundering Spear
|
|
|
|
if(GetHasFeat(FEAT_THUNDER_WEAPON, oSpellOrigin))
|
|
ExecuteScript("ft_shockweap", oSpellOrigin);
|
|
|
|
if(GetHasSpellEffect(TEMPUS_ENCHANT_WEAPON, oItem))
|
|
{
|
|
int nRace = MyPRCGetRacialType(oSpellTarget);
|
|
if((GetLocalInt(oSpellOrigin, "WeapEchant1") == TEMPUS_ABILITY_VICIOUS &&
|
|
nRace == GetLocalInt(oSpellOrigin,"WeapEchantRace1")) ||
|
|
(GetLocalInt(oSpellOrigin,"WeapEchant2")==TEMPUS_ABILITY_VICIOUS &&
|
|
nRace == GetLocalInt(oSpellOrigin,"WeapEchantRace2")) ||
|
|
(GetLocalInt(oSpellOrigin,"WeapEchant3")==TEMPUS_ABILITY_VICIOUS &&
|
|
nRace == GetLocalInt(oSpellOrigin,"WeapEchantRace3"))
|
|
)
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectDamage(d6()),oSpellOrigin);
|
|
}
|
|
|
|
if (GetIsPC(oSpellOrigin))
|
|
SetLocalInt(oSpellOrigin,"DmgDealt",GetTotalDamageDealt());
|
|
|
|
/// Vassal of Bahamut Dragonwrack
|
|
if(GetLevelByClass(CLASS_TYPE_VASSAL, oSpellOrigin) > 3)
|
|
{
|
|
if (iItemBaseType == BASE_ITEM_ARMOR)
|
|
ExecuteScript("prc_vb_dw_armor", oSpellOrigin);
|
|
else
|
|
ExecuteScript("prc_vb_dw_weap", oSpellOrigin);
|
|
}
|
|
/* /// Blood Archer Acidic Blood
|
|
if (nBArcher >= 2)
|
|
{
|
|
if(iItemBaseType == BASE_ITEM_ARMOR)
|
|
ExecuteScript("prc_bldarch_ab", oSpellOrigin);
|
|
}
|
|
|
|
// Blood Archer Poison Blood
|
|
if (nBArcher > 0 && iItemBaseType != BASE_ITEM_ARMOR)
|
|
{
|
|
// poison number based on archer level
|
|
// gives proper DC for poison
|
|
int iPoison = 104 + nBArcher;
|
|
effect ePoison = EffectPoison(iPoison);
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoison, oSpellTarget);
|
|
}*/
|
|
|
|
// Frenzied Berserker Auto Frenzy
|
|
if(iItemBaseType == BASE_ITEM_ARMOR && GetHasFeat(FEAT_FRENZY, oSpellOrigin))
|
|
{
|
|
if(!GetHasFeatEffect(FEAT_FRENZY, oSpellOrigin))
|
|
{
|
|
DelayCommand(0.01, ExecuteScript("prc_fb_auto_fre", oSpellOrigin) );
|
|
}
|
|
else if(GetCurrentHitPoints(oSpellOrigin) == 1 && GetHasFeat(FEAT_DEATHLESS_FRENZY, oSpellOrigin))
|
|
{
|
|
DelayCommand(0.01, ExecuteScript("prc_fb_deathless", oSpellOrigin) );
|
|
}
|
|
}
|
|
|
|
// Warsling Sniper Improved Ricochet
|
|
if (iItemBaseType == BASE_ITEM_BULLET
|
|
&& GetLevelByClass(CLASS_TYPE_HALFLING_WARSLINGER, oSpellOrigin) > 5
|
|
&& GetLocalInt(oSpellOrigin, "CanRicochet") != 2)
|
|
{
|
|
// Deactivates Ability
|
|
SetLocalInt(oSpellOrigin, "CanRicochet", 2);
|
|
|
|
DoImprovedRicochet(oSpellOrigin, oSpellTarget);
|
|
|
|
// Prevents the heartbeat script from running multiple times
|
|
if(GetLocalInt(oSpellOrigin, "ImpRicochetVarRunning") != 1)
|
|
{
|
|
DelayCommand(6.0, SetImprovedRicochetVar(oSpellOrigin) );
|
|
SetLocalInt(oSpellOrigin, "ImpRicochetVarRunning", 1);
|
|
}
|
|
}
|
|
|
|
// Warchief Devoted Bodyguards
|
|
if(iItemBaseType == BASE_ITEM_ARMOR && GetLevelByClass(CLASS_TYPE_WARCHIEF, oSpellOrigin) > 7)
|
|
{
|
|
// Warchief must make a reflex save
|
|
if(!GetLocalInt(oSpellOrigin, "WarChiefDelay"))
|
|
{
|
|
// Done this way for formatting reasons in game
|
|
if(ReflexSave(oSpellOrigin, 15))
|
|
DelayCommand(0.01, ExecuteScript("prc_wchf_bodygrd", oSpellOrigin));
|
|
|
|
// He can only do this once a round, so put a limit on it
|
|
SetLocalInt(oSpellOrigin, "WarChiefDelay", TRUE);
|
|
DelayCommand(6.0, DeleteLocalInt(oSpellOrigin, "WarChiefDelay"));
|
|
}
|
|
}
|
|
|
|
// Foe Hunter Damage Resistance
|
|
if(iItemBaseType == BASE_ITEM_ARMOR && nFoeHunter > 1)
|
|
{
|
|
DelayCommand(0.01, ExecuteScript("prc_fh_dr",oSpellOrigin) );
|
|
}
|
|
|
|
// Foe Hunter Rancor Attack
|
|
if(iItemBaseType != BASE_ITEM_ARMOR && nFoeHunter > 0)
|
|
{
|
|
if( GetLocalInt(oSpellOrigin, "PRC_CanUseRancor") != 2
|
|
&& GetLocalInt(oSpellOrigin, "HatedFoe") == MyPRCGetRacialType(oSpellTarget) )
|
|
{
|
|
int iFHLevel = GetLevelByClass(CLASS_TYPE_FOE_HUNTER, oSpellOrigin);
|
|
int iRancorDice = FloatToInt( (( iFHLevel + 1.0 ) /2) );
|
|
|
|
int iDamage = d6(iRancorDice);
|
|
int iDamType = GetWeaponDamageType(oItem);
|
|
int iDamPower = GetDamagePowerConstant(oItem, oSpellTarget, oSpellOrigin);
|
|
|
|
effect eDam = EffectDamage(iDamage, iDamType, iDamPower);
|
|
|
|
string sMess = "*Rancor Attack*";
|
|
FloatingTextStringOnCreature(sMess, oSpellOrigin, FALSE);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oSpellTarget);
|
|
|
|
// Deactivates Ability
|
|
SetLocalInt(oSpellOrigin, "PRC_CanUseRancor", 2);
|
|
|
|
// Prevents the heartbeat script from running multiple times
|
|
if(GetLocalInt(oSpellOrigin, "PRC_RancorVarRunning") != 1)
|
|
{
|
|
DelayCommand(6.0, SetRancorVar(oSpellOrigin) );
|
|
SetLocalInt(oSpellOrigin, "PRC_RancorVarRunning", 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Creatures with a necrotic cyst take +1d6 damage from natural attacks of undead
|
|
if(GetHasNecroticCyst(oSpellOrigin) && iItemBaseType == BASE_ITEM_ARMOR)
|
|
{
|
|
//if enemy is undead
|
|
if(MyPRCGetRacialType(oSpellTarget) == RACIAL_TYPE_UNDEAD)
|
|
{
|
|
//and unarmed
|
|
if (!GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oSpellTarget)) &&
|
|
!GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oSpellTarget)))
|
|
|
|
{
|
|
effect eDam = EffectDamage(d6(1), DAMAGE_TYPE_MAGICAL);
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oSpellOrigin);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*//////////////////////////////////////////////////
|
|
//////////////// PSIONICS //////////////////////////
|
|
//////////////////////////////////////////////////*/
|
|
|
|
// SweepingStrike OnHit
|
|
if(iItemBaseType != BASE_ITEM_ARMOR && GetLocalInt(oItem, "SweepingStrike"))
|
|
{
|
|
SweepingStrike(oSpellOrigin, oSpellTarget);
|
|
}
|
|
|
|
// Mind Stab OnHit
|
|
if(iItemBaseType != BASE_ITEM_ARMOR && GetLocalInt(oSpellOrigin, "ShadowMindStab") && !GetLocalInt(oSpellOrigin, "MindStabDelay"))
|
|
{
|
|
//FloatingTextStringOnCreature("MindStab Stage 1", oSpellOrigin, FALSE);
|
|
// Only works when able to sneak attack a target
|
|
if (GetCanSneakAttack(oSpellTarget, oSpellOrigin))
|
|
{
|
|
//FloatingTextStringOnCreature("MindStab Stage 2", oSpellOrigin, FALSE);
|
|
MindStab(oSpellOrigin, oSpellTarget);
|
|
// Only once per round
|
|
SetLocalInt(oSpellOrigin, "MindStabDelay", TRUE);
|
|
DelayCommand(6.0, DeleteLocalInt(oSpellOrigin, "MindStabDelay"));
|
|
}
|
|
}
|
|
|
|
// Astral Construct's Poison Touch special ability
|
|
if(GetLocalInt(oSpellOrigin, ASTRAL_CONSTRUCT_POISON_TOUCH))
|
|
{
|
|
ExecuteScript("psi_ast_con_ptch", oSpellOrigin);
|
|
}
|
|
|
|
// Pyrokineticist bonus damage
|
|
int nPyroLevel = GetLevelByClass(CLASS_TYPE_PYROKINETICIST, oSpellOrigin);
|
|
if(nPyroLevel)
|
|
{
|
|
int iElement = GetPersistantLocalInt(oSpellOrigin, "PyroDamageType");
|
|
int nDam1 = 0;
|
|
int nDam2 = 0;
|
|
int nDam = 0;
|
|
int nDice = 0;
|
|
|
|
if(iItemBaseType == BASE_ITEM_ARMOR)
|
|
{ //Nimbus Onhit
|
|
if(nPyroLevel >= 5 && GetHasSpellEffect(SPELL_NIMBUS, oSpellOrigin))
|
|
{
|
|
nDice = 2;
|
|
nDam = (iElement == DAMAGE_TYPE_SONIC) ? d4(nDice) : d6(nDice); //reduced damage dice
|
|
if((iElement == DAMAGE_TYPE_COLD) || (iElement == DAMAGE_TYPE_ELECTRICAL) || (iElement == DAMAGE_TYPE_ACID))
|
|
nDam = max(nDice, nDam - nDice); //minimum of 1 per die
|
|
}
|
|
}
|
|
else
|
|
{ //Assume unarmed/weapon damage
|
|
if(nPyroLevel >= 2)
|
|
{
|
|
nDice = (GetLevelByClass(CLASS_TYPE_PYROKINETICIST, oSpellOrigin) >= 8) ? 4 : 2;
|
|
nDam1 = (iElement == DAMAGE_TYPE_SONIC) ? d4(nDice) : d6(nDice); //reduced damage dice
|
|
if((iElement == DAMAGE_TYPE_COLD) || (iElement == DAMAGE_TYPE_ELECTRICAL) || (iElement == DAMAGE_TYPE_ACID))
|
|
nDam1 = max(nDice, nDam1 - nDice); //minimum of 1 per die
|
|
}
|
|
if(GetTag(oItem) == "PRC_PYRO_LASH_WHIP")
|
|
{ //Extra damage from whip
|
|
nDam2 += (iElement == DAMAGE_TYPE_SONIC) ? d6(1) : d8(1); //reduced damage dice
|
|
if((iElement == DAMAGE_TYPE_COLD) || (iElement == DAMAGE_TYPE_ELECTRICAL) || (iElement == DAMAGE_TYPE_ACID))
|
|
nDam2 = max(1, nDam2 - 1); //minimum of 1
|
|
}
|
|
nDam = nDam1 + nDam2;
|
|
}
|
|
if(nDam > 0)
|
|
{
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDam, iElement), oSpellTarget);
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(GetPersistantLocalInt(oSpellOrigin, "PyroImpactVFX")), oSpellTarget);
|
|
}
|
|
}
|
|
|
|
/*//////////////////////////////////////////////////
|
|
//////////////// END PSIONICS //////////////////////
|
|
//////////////////////////////////////////////////*/
|
|
|
|
/*//////////////////////////////////////////////////
|
|
//////////////// Blade Magic ///////////////////////
|
|
//////////////////////////////////////////////////*/
|
|
|
|
// Martial Spirit
|
|
if(GetHasSpellEffect(MOVE_DS_MARTIAL_SPIRIT, oSpellOrigin) && GetBaseItemType(oItem) != BASE_ITEM_ARMOR && GetObjectType(oSpellTarget) == OBJECT_TYPE_CREATURE && !GetIsDead(oSpellTarget))
|
|
{
|
|
object oHealTarget = GetCrusaderHealTarget(oSpellOrigin, 30.0);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(2), oHealTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HEALING_L_LAW), oHealTarget);
|
|
}
|
|
|
|
// Blood in the Water
|
|
if(GetHasSpellEffect(MOVE_TC_BLOOD_WATER, oSpellOrigin) && GetBaseItemType(oItem) != BASE_ITEM_ARMOR && !GetIsImmune(oSpellTarget, IMMUNITY_TYPE_CRITICAL_HIT))
|
|
{
|
|
// Fake critical hit check
|
|
if(d20() >= GetWeaponCriticalRange(oSpellOrigin, oItem))
|
|
{
|
|
effect eBuff = EffectLinkEffects(EffectAttackIncrease(1), EffectDamageIncrease(DAMAGE_BONUS_1, DAMAGE_TYPE_SLASHING));
|
|
DelayCommand(0.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBuff, oSpellOrigin, TurnsToSeconds(1)));
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_COM_BLOOD_CRT_YELLOW_HEAD), oSpellOrigin);
|
|
}
|
|
}
|
|
|
|
// Fire Riposte
|
|
if(GetHasSpellEffect(MOVE_DW_FIRE_RIPOSTE, oSpellOrigin) && GetBaseItemType(oItem) == BASE_ITEM_ARMOR)
|
|
{
|
|
int nTouchAttack = PRCDoMeleeTouchAttack(oSpellTarget);
|
|
if(nTouchAttack > 0)
|
|
{
|
|
// Apply the damage and VFX
|
|
ApplyTouchAttackDamage(oSpellOrigin, oSpellTarget, nTouchAttack, d6(4), DAMAGE_TYPE_FIRE);
|
|
effect eVis = EffectVisualEffect(VFX_COM_HIT_FIRE);
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oSpellTarget);
|
|
FloatingTextStringOnCreature("Fire Riposte Hit", oSpellOrigin, FALSE);
|
|
// Clean up
|
|
PRCRemoveSpellEffects(MOVE_DW_FIRE_RIPOSTE, oSpellOrigin, oSpellOrigin);
|
|
}
|
|
}
|
|
|
|
// Holocaust Cloak
|
|
if(GetHasSpellEffect(MOVE_DW_HOLOCAUST_CLOAK, oSpellOrigin) && GetBaseItemType(oItem) == BASE_ITEM_ARMOR)
|
|
{
|
|
if(GetIsInMeleeRange(oSpellOrigin, oSpellTarget))
|
|
{
|
|
// Apply the damage and VFX
|
|
effect eVis = EffectVisualEffect(VFX_COM_HIT_FIRE);
|
|
effect eDam = EffectDamage(5, DAMAGE_TYPE_FIRE);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oSpellTarget);
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oSpellTarget);
|
|
FloatingTextStringOnCreature("Holocaust Cloak Hit", oSpellOrigin, FALSE);
|
|
}
|
|
}
|
|
// Defensive Rebuke
|
|
if(GetHasSpellEffect(MOVE_DS_DEFENSIVE_REBUKE, oSpellOrigin) && GetBaseItemType(oItem) != BASE_ITEM_ARMOR)
|
|
{
|
|
SetLocalObject(oSpellTarget, "DefensiveRebuke", oSpellOrigin);
|
|
DelayCommand(3.0, ExecuteScript("tob_dvsp_defrbka", oSpellTarget));
|
|
}
|
|
// Defensive Rebuke
|
|
if(GetHasSpellEffect(MOVE_DM_PEARL_BLACK_DOUBT, oSpellOrigin) && GetBaseItemType(oItem) == BASE_ITEM_ARMOR)
|
|
{
|
|
// Will reset to 0.
|
|
DeleteLocalInt(oSpellOrigin, "PearlOfBlackDoubtBonus");
|
|
}
|
|
// Tactics of the Wolf
|
|
if(GetHasSpellEffect(MOVE_WR_TACTICS_WOLF, oSpellOrigin) && GetBaseItemType(oItem) != BASE_ITEM_ARMOR)
|
|
{
|
|
if (GetIsFlanked(oSpellTarget, oSpellOrigin))
|
|
{
|
|
int nWolfDam = GetLocalInt(oSpellOrigin, "TacticsWolf");
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nWolfDam), oSpellTarget);
|
|
}
|
|
}
|
|
|
|
// Aura of Triumph
|
|
if(GetHasSpellEffect(MOVE_DS_AURA_TRIUMPH, oSpellOrigin) && GetBaseItemType(oItem) != BASE_ITEM_ARMOR)
|
|
{
|
|
// Heal both
|
|
object oHealTarget = GetLocalObject(oSpellOrigin, "DSTriumph");
|
|
// Must be within 10 feet
|
|
if (10.0 >= FeetToMeters(GetDistanceBetween(oSpellOrigin, oSpellOrigin)))
|
|
{
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(4), oHealTarget);
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HEALING_L_LAW), oHealTarget);
|
|
}
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(4), oSpellOrigin);
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HEALING_L_LAW), oSpellOrigin);
|
|
}
|
|
|
|
// Immortal Fortitude
|
|
if(GetHasSpellEffect(MOVE_DS_IMMORTAL_FORTITUDE, oSpellOrigin) && GetBaseItemType(oItem) == BASE_ITEM_ARMOR)
|
|
{
|
|
// He's immortal, so now we run a script to see if he should/would have died (Saving, etc)
|
|
if(GetCurrentHitPoints(oSpellOrigin) == 1)
|
|
{
|
|
DelayCommand(0.01, ExecuteScript("tob_dvsp_imfrtoh", oSpellOrigin));
|
|
}
|
|
}
|
|
// Immortal Fortitude
|
|
if(GetHasSpellEffect(MOVE_WR_CLARION_CALL, oSpellOrigin) && GetBaseItemType(oItem) != BASE_ITEM_ARMOR)
|
|
{
|
|
// Check to see if the target is dead, triggers each time
|
|
if(GetIsDead(oSpellTarget))
|
|
{
|
|
location lTarget = GetLocation(oSpellOrigin);
|
|
object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, FeetToMeters(60.0), lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
while(GetIsObjectValid(oAreaTarget))
|
|
{
|
|
if(GetIsFriend(oAreaTarget, oSpellOrigin))
|
|
{
|
|
// Apply extra attack for one round
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectModifyAttacks(1)), oAreaTarget, 6.0);
|
|
}
|
|
oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, FeetToMeters(60.0), lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*//////////////////////////////////////////////////
|
|
//////////////// End Blade Magic ///////////////////
|
|
//////////////////////////////////////////////////*/
|
|
|
|
if(iItemBaseType != BASE_ITEM_ARMOR && GetLocalInt(oSpellOrigin,"doarcstrike"))
|
|
{
|
|
int nDice = GetLocalInt(oSpellOrigin,"curentspell");
|
|
int nDamage = d4(nDice);
|
|
effect eDam = EffectDamage(nDamage);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eDam,oSpellTarget);
|
|
}
|
|
|
|
//spellsword & arcane archer
|
|
//will also fire for other OnHit:UniquePower items that have a SpellSequencer property (such as Duskblade)
|
|
if(GetLocalInt(oItem, "X2_L_NUMCHANNELTRIGGERS"))
|
|
{
|
|
if (DEBUG) DoDebug("prc_onhitcast: Triggering Sequencer Discharge");
|
|
SetLocalObject(oSpellOrigin, "ChannelSpellTarget", oSpellTarget);
|
|
ExecuteScript("x2_s3_sequencer", oSpellOrigin);
|
|
}
|
|
|
|
// Handle Rend. Creature weapon damage + 1.5x STR bonus.
|
|
// Only happens when attacking with a creature weapon
|
|
if(GetIsCreatureWeaponType(iItemBaseType) && (GetHasFeat(FEAT_REND, oSpellOrigin) || GetLevelByClass(CLASS_TYPE_BLACK_BLOOD_CULTIST, oSpellOrigin) >= 6))
|
|
{
|
|
DoRend(oSpellTarget, oSpellOrigin, oItem);
|
|
}
|
|
|
|
// Handle Frostrager Rend. 2d8 damage + 1.5x STR bonus.
|
|
// Only happens when attacking with a creature weapon
|
|
if(GetIsCreatureWeaponType(iItemBaseType) && (GetLevelByClass(CLASS_TYPE_FROSTRAGER, oSpellOrigin) > 4))
|
|
{
|
|
DoFrostRend(oSpellTarget, oSpellOrigin, oItem);
|
|
}
|
|
|
|
// Handle Spine Rend. 2d6 damage + 1.5x STR bonus.
|
|
// Only happens when attacking with a skarn spine
|
|
if(GetTag(oItem) == "skarn_spine" && (GetLevelByClass(CLASS_TYPE_SPINEMELD_WARRIOR, oSpellOrigin) >= 5))
|
|
{
|
|
DoSpineRend(oSpellTarget, oSpellOrigin, oItem);
|
|
}
|
|
|
|
// now cycle through all onhitcast spells on the item
|
|
// we must exclude unique power (which is associated with prc_onhitcast), because otherwise we would get infinite recursions
|
|
// it is of utmost importance to devise a *safe* way to cycle through all onhitcast spells on the item. The safe way is provided
|
|
// by the function ApplyAllOnHitCastSpellsOnItemExcludingSubType defined in prc_inc_spells
|
|
|
|
// There are two ways to call this function: Either with all necessary parameters passed explicitly to the function
|
|
// or with no parameters passed to the function (in this case default values are used, which also works, at least in prc_onhitcast)
|
|
|
|
// VERSION 1:
|
|
// generally it is more efficient to call ApplyAllOnHitCastSpellsOnItemExcludingSubType by explicitly passing the parameters to the function
|
|
// this will set up the overrides in the PRC-wrappers for the spell information functions, which generally is much faster
|
|
// ApplyAllOnHitCastSpellsOnItemExcludingSubType(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, oSpellTarget, oItem, oSpellOrigin);
|
|
|
|
// VERSION 2:
|
|
// motu99: It might be safer to call this only with defaults in order disallow overrides being set.
|
|
// (they could have been set beforehand, though - in fact they *are* if we were called from prc_inc_combat)
|
|
// VERSION 2 has also been tested to work; however, if Bioware changes its implementation the code below is more likely to break
|
|
ApplyAllOnHitCastSpellsOnItemExcludingSubType(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER);
|
|
|
|
// ONLY FOR TESTING
|
|
// if(GetPRCSwitch(PRC_TIMESTOP_LOCAL)) CastSpellAtLocation(SPELL_FIREBALL, GetLocation(oSpellTarget) /*, METAMAGIC_ANY, GetLevelByTypeArcane(oSpellOrigin), CLASS_TYPE_WIZARD */);
|
|
// if(GetPRCSwitch(PRC_PNP_TRUESEEING)) CastSpellAtObject(SPELL_FIREBALL, oSpellTarget /*, METAMAGIC_ANY, GetLevelByTypeArcane(oSpellOrigin), CLASS_TYPE_WIZARD */);
|
|
/*
|
|
// motu99: This is the old (unsafe) way to cycle through the onhitcast spells.
|
|
// This method fails, whenever one of the called impact spell script cycles through the item properties of the SpellCastItem on its own
|
|
// (or calls a function that cycles through the item properties - such as PRCGetMetaMagicFeat)
|
|
// The safe way to do things is to use the functions ApplyAllOnHitCastSpells* to be found in prc_inc_spells
|
|
// Left the piece of code here as an example and a warning, how perfectly reasonable code can break without the fault of the scripter
|
|
// Such an "error" can easily happen to any of us. It is quite difficult to spot an error, caused by nested loops
|
|
// over item properties in *different* scripts. Runtime behavior is erratic. If you are lucky, you get an
|
|
// infinite recursion (then you will notice that something is wrong). If you are not lucky, the loop will just skip over
|
|
// some item properties. And this simply because you put a completely harmless looking function like PRCGetMetaMagicFeat
|
|
// into your Spell script. How could you possibly know that you just broke your script, because of an unsafe implementation
|
|
// in a *different* script (here: prc_onhitcast)? You might not even know, that this different scripts exists.
|
|
|
|
//handle other OnHit:CastSpell properties
|
|
DoDebug("prc_onhitcast: now doing other OnHitCastSpell properties on item = "+GetName(oItem));
|
|
itemproperty ipTest = GetFirstItemProperty(oItem);
|
|
while(GetIsItemPropertyValid(ipTest))
|
|
{
|
|
if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_ONHITCASTSPELL)
|
|
{
|
|
int nIPSpell = GetItemPropertySubType(ipTest);
|
|
if(nIPSpell == 125)
|
|
{
|
|
ipTest = GetNextItemProperty(oItem);
|
|
continue; //abort if its OnHit:CastSpell:UniquePower otherwise it would TMI.
|
|
}
|
|
int nSpell = StringToInt(Get2DACache("iprp_onhitspell", "SpellIndex", nIPSpell));
|
|
// int nLevel = GetItemPropertyCostTableValue(ipTest);
|
|
string sScript = Get2DACache("spells", "ImpactScript", nSpell);
|
|
DoDebug("prc_onhitcast: Now executing Impact spell script "+sScript);
|
|
// motu99: Never execute complicated scripts within an GetFirst* / GetNext* loop !!!
|
|
// The code will break, whenever the script does a loop over item properties (or effects) on its own
|
|
// rather store all found scripts in a local array, and execute the scripts in a separate loop
|
|
ExecuteScript(sScript,oSpellOrigin);
|
|
}
|
|
ipTest = GetNextItemProperty(oItem);
|
|
}
|
|
*/
|
|
|
|
/*//////////////////////////////////////////////////
|
|
/////////////////// SPELLFIRE ////////////////////
|
|
//////////////////////////////////////////////////*/
|
|
|
|
int nSpellfire = GetLevelByClass(CLASS_TYPE_SPELLFIRE, oSpellOrigin);
|
|
if(nSpellfire && (iItemBaseType == BASE_ITEM_ARMOR))
|
|
{
|
|
int nStored = GetPersistantLocalInt(oSpellOrigin, "SpellfireLevelStored");
|
|
int nCON = GetAbilityScore(oSpellOrigin, ABILITY_CONSTITUTION);
|
|
int nFlare = 0;
|
|
int bFlare = FALSE;
|
|
if(nStored > 4 * nCON)
|
|
{
|
|
nFlare = d6(2);
|
|
bFlare = TRUE;
|
|
}
|
|
else if(nStored > 3 * nCON)
|
|
{
|
|
nFlare = d6();
|
|
bFlare = TRUE;
|
|
}
|
|
else if(nStored > 2 * nCON)
|
|
nFlare = d4();
|
|
else if(nStored > nCON)
|
|
nFlare = 1;
|
|
if(nFlare)
|
|
{
|
|
nStored -= nFlare;
|
|
if(nStored < 0) nStored = 0;
|
|
SetPersistantLocalInt(oSpellOrigin, "SpellfireLevelStored", nStored);
|
|
}
|
|
if(bFlare)
|
|
{
|
|
int nDC = 10 + nFlare;
|
|
location lTarget = GetLocation(oSpellOrigin);
|
|
object oFlareTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
while(GetIsObjectValid(oFlareTarget))
|
|
{
|
|
if(spellsIsTarget(oFlareTarget, SPELL_TARGET_STANDARDHOSTILE, oSpellOrigin))
|
|
{
|
|
if(!PRCDoResistSpell(oSpellOrigin, oFlareTarget, nSpellfire))
|
|
{
|
|
if (PRCMySavingThrow(SAVING_THROW_FORT, oFlareTarget, nDC))
|
|
{
|
|
if (GetHasMettle(oFlareTarget, SAVING_THROW_FORT))
|
|
// This script does nothing if it has Mettle, bail
|
|
return;
|
|
SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_S), oFlareTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDazzle(), oFlareTarget, 60.0);
|
|
}
|
|
}
|
|
}
|
|
oFlareTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
}
|
|
}
|
|
if(GetLocalInt(oSpellOrigin, "SpellfireCrown")) //melts non-magical melee weapons
|
|
{ //can't really get which weapon hit you, so...
|
|
object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oSpellTarget);
|
|
if(GetIsObjectValid(oWeapon))
|
|
{
|
|
if(IPGetIsMeleeWeapon(oWeapon) && !GetIsMagicItem(oWeapon))
|
|
{
|
|
DestroyObject(oWeapon);
|
|
FloatingTextStringOnCreature("*Your weapon has melted!*", oSpellTarget);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
oWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oSpellTarget);
|
|
if(GetIsObjectValid(oWeapon))
|
|
{
|
|
if(IPGetIsMeleeWeapon(oWeapon) && !GetIsMagicItem(oWeapon))
|
|
{
|
|
DestroyObject(oWeapon);
|
|
FloatingTextStringOnCreature("*Your weapon has melted!*", oSpellTarget);
|
|
}
|
|
}
|
|
else //You're putting your arms and legs through something that melts weapons?
|
|
{ //Silly monk/brawler/fool with molten weapons!
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d20()), oSpellTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d20(), DAMAGE_TYPE_FIRE), oSpellTarget);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*//////////////////////////////////////////////////
|
|
////////////////// END SPELLFIRE ///////////////////
|
|
//////////////////////////////////////////////////*/
|
|
|
|
// Handle poisoned weapons
|
|
/*
|
|
if(GetLocalInt(oItem, "pois_wpn_uses"))
|
|
{
|
|
ExecuteScript("poison_wpn_onhit", OBJECT_SELF);
|
|
}
|
|
*/
|
|
// Execute scripts hooked to this event for the player triggering it
|
|
if (DEBUG) DoDebug("prc_onhitcast: executing all scripts hooked to onhit events of attacker and item");
|
|
ExecuteAllScriptsHookedToEvent(oSpellOrigin, EVENT_ONHIT);
|
|
ExecuteAllScriptsHookedToEvent(oItem, EVENT_ITEM_ONHIT);
|
|
|
|
DeleteLocalInt(oSpellOrigin, "prc_ohc");
|
|
|
|
}
|
|
|
|
void SetRancorVar(object oPC)
|
|
{
|
|
// Turn Rancor on
|
|
SetLocalInt(oPC, "PRC_CanUseRancor", 1);
|
|
//FloatingTextStringOnCreature("Rancor Attack Possible", oPC, FALSE);
|
|
|
|
int iMain = GetMainHandAttacks(oPC);
|
|
float fDelay = 6.0 / IntToFloat(iMain);
|
|
|
|
// Turn Rancor off after one attack is made
|
|
DelayCommand(fDelay, SetLocalInt(oPC, "PRC_CanUseRancor", 2));
|
|
//DelayCommand((fDelay + 0.01), FloatingTextStringOnCreature("Rancor Attack Not Possible", oPC, FALSE));
|
|
|
|
// Call again if the character is still in combat.
|
|
// this allows the ability to keep running even if the
|
|
// player does not score a rancor hit during the allotted time
|
|
if( PRCGetIsFighting() )
|
|
{
|
|
DelayCommand(6.0, SetRancorVar(oPC) );
|
|
}
|
|
else
|
|
{
|
|
DelayCommand(2.0, SetLocalInt(oPC, "PRC_CanUseRancor", 1));
|
|
DelayCommand(2.1, SetLocalInt(oPC, "PRC_RancorVarRunning", 2));
|
|
//DelayCommand(2.2, FloatingTextStringOnCreature("Rancor Enabled After Combat", oPC, FALSE));
|
|
}
|
|
}
|
|
|
|
void DoImprovedRicochet(object oPC, object oTarget)
|
|
{
|
|
int nTargetsLeft = 1;
|
|
effect eVis = EffectVisualEffect(VFX_IMP_DUST_EXPLOSION);
|
|
|
|
location lTarget = GetLocation(oTarget);
|
|
//Declare the spell shape, size and the location. Capture the first target object in the shape.
|
|
object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
|
|
//Cycle through the targets within the spell shape until you run out of targets.
|
|
while (GetIsObjectValid(oAreaTarget) && nTargetsLeft > 0)
|
|
{
|
|
if (spellsIsTarget(oAreaTarget, SPELL_TARGET_SELECTIVEHOSTILE, OBJECT_SELF) && oAreaTarget != OBJECT_SELF && oAreaTarget != oTarget)
|
|
{
|
|
PerformAttack(oAreaTarget, oPC, eVis, 0.0, -2, 0, 0, "*Improved Ricochet Hit*", "*Improved Ricochet Missed*");
|
|
// Use up a target slot only if we actually did something to it
|
|
nTargetsLeft -= 1;
|
|
}
|
|
|
|
//Select the next target within the spell shape.
|
|
oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_LARGE, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
}
|
|
}
|
|
|
|
void SetImprovedRicochetVar(object oPC)
|
|
{
|
|
// Turn Retort on
|
|
SetLocalInt(oPC, "CanRicochet", 1);
|
|
|
|
// Turn Retort off after one attack is made
|
|
DelayCommand(0.01, SetLocalInt(oPC, "CanRicochet", 0));
|
|
|
|
// Call again if the character is still in combat.
|
|
// this allows the ability to keep running even if the
|
|
// player does not score a retort hit during the allotted time
|
|
if( PRCGetIsFighting() )
|
|
{
|
|
DelayCommand(6.0, SetImprovedRicochetVar(oPC));
|
|
}
|
|
else
|
|
{
|
|
DelayCommand(2.0, SetLocalInt(oPC, "CanRicochet", 1));
|
|
DelayCommand(2.1, SetLocalInt(oPC, "ImpRicochetVarRunning", 2));
|
|
}
|
|
}
|