Updated Release Archive. Fixed Mage-killer prereqs. Removed old LETO & ConvoCC related files. Added organized spell scroll store. Fixed Gloura spellbook. Various TLK fixes. Reorganized Repo. Removed invalid user folders. Added DocGen back in.
922 lines
44 KiB
Plaintext
922 lines
44 KiB
Plaintext
//::///////////////////////////////////////////////
|
|
//:: Tome of Battle feats
|
|
//:: tob_feats
|
|
//::///////////////////////////////////////////////
|
|
/** @file
|
|
Does all ToB feats that require event scripting.
|
|
|
|
@author Stratovarius
|
|
@date Created - 2018.9.22
|
|
*/
|
|
//:://////////////////////////////////////////////
|
|
//:://////////////////////////////////////////////
|
|
|
|
#include "tob_inc_tobfunc"
|
|
#include "psi_inc_psifunc"
|
|
#include "prc_inc_combmove"
|
|
#include "x0_i0_modes"
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Feat Functions */
|
|
//////////////////////////////////////////////////
|
|
|
|
// Called on Heartbeat
|
|
void ShadowBlade(object oInitiator)
|
|
{
|
|
// Needs an active Shadow Hands stance
|
|
int nStance = GetHasActiveStance(oInitiator);
|
|
int nDisc = GetDisciplineByManeuver(nStance);
|
|
int nDex = GetAbilityModifier(ABILITY_DEXTERITY, oInitiator);
|
|
object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator);
|
|
int nWeap = GetIsDisciplineWeapon(oWeapon, DISCIPLINE_SHADOW_HAND);
|
|
|
|
// Must be a shadow hand weapon while a stance is active
|
|
if (nDisc == DISCIPLINE_SHADOW_HAND && nWeap)
|
|
{
|
|
int nDamageType = GetWeaponDamageType(oWeapon);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectDamageIncrease(IPGetDamageBonusConstantFromNumber(nDex), nDamageType)), oInitiator, 6.0);
|
|
SetLocalInt(oInitiator, "ShadowBladeDam", nDex);
|
|
DelayCommand(6.0, DeleteLocalInt(oInitiator, "ShadowBladeDam"));
|
|
}
|
|
}
|
|
|
|
// Called on Heartbeat
|
|
void RapidAssault(object oInitiator)
|
|
{
|
|
int nCombat = GetIsInCombat(oInitiator);
|
|
int nLast = GetLocalInt(oInitiator, "RapidAssault");
|
|
|
|
// Must must be in combat this round but not last round
|
|
if (nCombat == TRUE && nLast == FALSE)
|
|
{
|
|
object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator);
|
|
int nDamageType = GetWeaponDamageType(oWeapon);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectDamageIncrease(DAMAGE_BONUS_1d6, nDamageType)), oInitiator, 6.0);
|
|
}
|
|
SetLocalInt(oInitiator, "RapidAssault", nCombat);
|
|
}
|
|
|
|
// Called on heartbeat
|
|
void DesertWindDodge(object oInitiator)
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACIncrease(1, AC_DODGE_BONUS)), oInitiator, 6.0);
|
|
object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator);
|
|
int nWeap = GetIsDisciplineWeapon(oWeapon, DISCIPLINE_DESERT_WIND);
|
|
if (nWeap)
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectDamageIncrease(DAMAGE_BONUS_1, DAMAGE_TYPE_FIRE)), oInitiator, 6.0);
|
|
}
|
|
|
|
// Called on heartbeat
|
|
void DesertFire(object oInitiator)
|
|
{
|
|
SetLocalInt(oInitiator, "DesertFire", TRUE);
|
|
DelayCommand(5.9, DeleteLocalInt(oInitiator, "DesertFire"));
|
|
}
|
|
|
|
// Called on heartbeat
|
|
void DesertWind(object oInitiator)
|
|
{
|
|
// Check to see if the WP is valid
|
|
string sWPTag = "PRC_DWDWP_" + GetName(oInitiator);
|
|
object oTestWP = GetWaypointByTag(sWPTag);
|
|
if (!GetIsObjectValid(oTestWP))
|
|
{
|
|
// Create waypoint for the movement
|
|
CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oInitiator), FALSE, sWPTag);
|
|
if(DEBUG) DoDebug("tob_feats: Desert Wind Dodge WP for " + DebugObject2Str(oInitiator) + " didn't exist, creating. Tag: " + sWPTag);
|
|
}
|
|
else // We have a test waypoint, now to check the distance
|
|
{
|
|
// Distance moved in the last round
|
|
float fDist = GetDistanceBetween(oInitiator, oTestWP);
|
|
// Distance needed to move
|
|
float fCheck = FeetToMeters(10.0);
|
|
|
|
// Now clean up the WP and create a new one for next round's check
|
|
DestroyObject(oTestWP);
|
|
CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oInitiator), FALSE, sWPTag);
|
|
|
|
if(DEBUG) DoDebug("tob_feats: Moved enough: " + DebugBool2String(fDist >= fCheck));
|
|
|
|
// Moved the distance
|
|
if (fDist >= fCheck)
|
|
{
|
|
if(GetHasFeat(FEAT_DESERT_WIND_DODGE, oInitiator)) DesertWindDodge(oInitiator);
|
|
if(GetHasFeat(FEAT_DESERT_FIRE, oInitiator)) DesertFire(oInitiator);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called on False
|
|
void BladeMeditation(object oInitiator)
|
|
{
|
|
int nDisc = BladeMeditationFeat(oInitiator);
|
|
object oSkin = GetPCSkin(oInitiator);
|
|
int nSkill = GetDisciplineSkill(nDisc);
|
|
SetCompositeBonus(oSkin, "BladeMeditation", 2, ITEM_PROPERTY_SKILL_BONUS, nSkill);
|
|
}
|
|
|
|
// Called on Heartbeat
|
|
void IronheartAura(object oInitiator)
|
|
{
|
|
int nDisc = GetDisciplineByManeuver(GetHasActiveStance(oInitiator));
|
|
if (nDisc == DISCIPLINE_IRON_HEART)
|
|
{
|
|
location lTarget = GetLocation(oInitiator);
|
|
// Use the function to get the closest creature as a target
|
|
object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
while(GetIsObjectValid(oAreaTarget))
|
|
{
|
|
if(oAreaTarget != oInitiator && // Not you
|
|
GetIsInMeleeRange(oInitiator, oAreaTarget) && // They must be in melee range
|
|
GetIsFriend(oAreaTarget, oInitiator)) // Only allies
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectSavingThrowIncrease(SAVING_THROW_ALL, 2, SAVING_THROW_TYPE_ALL)), oAreaTarget, 6.0);
|
|
}
|
|
//Select the next target within the spell shape.
|
|
oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called on heartbeat
|
|
void ShadowTrickster(object oInitiator)
|
|
{
|
|
// Needs an active Shadow Hands stance
|
|
int nStance = GetHasActiveStance(oInitiator);
|
|
int nDisc = GetDisciplineByManeuver(nStance);
|
|
if (nDisc == DISCIPLINE_SHADOW_HAND)
|
|
{
|
|
SetLocalInt(oInitiator, "ShadowTrickster", TRUE);
|
|
}
|
|
else
|
|
DeleteLocalInt(oInitiator, "ShadowTrickster");
|
|
}
|
|
|
|
// Called on Heartbeat
|
|
void WhiteRavenDefense(object oInitiator)
|
|
{
|
|
int nDisc = GetDisciplineByManeuver(GetHasActiveStance(oInitiator));
|
|
object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator);
|
|
int nWeap = GetIsDisciplineWeapon(oWeapon, DISCIPLINE_WHITE_RAVEN);
|
|
int nAlly = FALSE;
|
|
if (nDisc == DISCIPLINE_WHITE_RAVEN)
|
|
{
|
|
location lTarget = GetLocation(oInitiator);
|
|
|
|
// Use the function to get the closest creature as a target
|
|
object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
while(GetIsObjectValid(oAreaTarget))
|
|
{
|
|
if(oAreaTarget != oInitiator && // Not you
|
|
GetIsInMeleeRange(oInitiator, oAreaTarget) && // They must be in melee range
|
|
GetIsFriend(oAreaTarget, oInitiator)) // Only allies
|
|
{
|
|
if (nWeap) ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACIncrease(1)), oAreaTarget, 6.0);
|
|
nAlly = TRUE;
|
|
}
|
|
//Select the next target within the spell shape.
|
|
oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
}
|
|
}
|
|
|
|
if (nAlly)
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACIncrease(1)), oInitiator, 6.0);
|
|
}
|
|
}
|
|
|
|
void DevotedBulwark(object oInitiator, object oTarget)
|
|
{
|
|
//only trigger if it was a melee weapon
|
|
if(GetIsInMeleeRange(oInitiator, oTarget))
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectACIncrease(1), oInitiator, 6.0f);
|
|
}
|
|
}
|
|
|
|
void SnapKick(object oInitiator, object oTarget)
|
|
{
|
|
if (GetLocalInt(oInitiator, "SnapKickDelay") == TRUE) return; //Once a round only
|
|
else
|
|
{
|
|
SetLocalInt(oInitiator, "SnapKickDelay", TRUE);
|
|
DelayCommand(6.0, DeleteLocalInt(oInitiator, "SnapKickDelay"));
|
|
}
|
|
|
|
object oWeap = GetItemInSlot(INVENTORY_SLOT_ARMS, oInitiator); // It's an unarmed attack, so we get the gloves
|
|
int nAttack = GetAttackRoll(oTarget, oInitiator, oWeap);
|
|
if (nAttack > 0) // We hit
|
|
{
|
|
FloatingTextStringOnCreature("Snap Kick Hit", oInitiator, FALSE);
|
|
int nCrit = FALSE;
|
|
if (nAttack == 2) nCrit = TRUE;
|
|
effect eDam = GetAttackDamage(oTarget, oInitiator, oWeap, GetWeaponBonusDamage(oWeap, oTarget), GetMagicalBonusDamage(oInitiator, oTarget), 1, 0, nCrit); //off-hand attack to get 1/2 strength bonus
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget);
|
|
}
|
|
else
|
|
FloatingTextStringOnCreature("Snap Kick Miss", oInitiator, FALSE);
|
|
}
|
|
|
|
void ThreeMountains(object oInitiator, object oTarget)
|
|
{
|
|
// Check for a first hit
|
|
int nHit = GetLocalInt(oTarget, "ThreeMountains");
|
|
|
|
if (!nHit)
|
|
{
|
|
SetLocalInt(oTarget, "ThreeMountains", TRUE);
|
|
DelayCommand(6.0, DeleteLocalInt(oTarget, "ThreeMountains"));
|
|
}
|
|
else // Hit them twice in six seconds
|
|
{
|
|
int nDC = 10 + GetHitDice(oInitiator)/2 + GetAbilityModifier(ABILITY_STRENGTH, oInitiator);
|
|
if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_NONE, oInitiator))
|
|
{
|
|
effect eLink = EffectLinkEffects(EffectSpellFailure(), EffectAttackDecrease(20));
|
|
SetBaseAttackBonus(1, oTarget);
|
|
effect eLink2 = EffectLinkEffects(EffectVisualEffect(VFX_IMP_DISEASE_S), EffectVisualEffect(VFX_IMP_REDUCE_ABILITY_SCORE_RED));
|
|
DelayCommand(6.0, RestoreBaseAttackBonus(oTarget));
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(eLink), oTarget, RoundsToSeconds(1));
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink2, oTarget);
|
|
}
|
|
DeleteLocalInt(oTarget, "ThreeMountains");
|
|
}
|
|
}
|
|
|
|
void VaeSchool(object oInitiator, object oTarget)
|
|
{
|
|
if (GetLocalInt(oInitiator, "VaeSchool") == TRUE) return; //Once a round only
|
|
else
|
|
{
|
|
SetLocalInt(oInitiator, "VaeSchool", TRUE);
|
|
DelayCommand(6.0, DeleteLocalInt(oInitiator, "VaeSchool"));
|
|
}
|
|
|
|
DoTrip(oInitiator, oTarget, 0, FALSE, FALSE, TRUE);
|
|
}
|
|
|
|
void InlindlSchool(object oInitiator)
|
|
{
|
|
if (GetIsLightWeapon(oInitiator) && GetLocalInt(oInitiator, "InlindlSchool"))
|
|
{
|
|
int nShield = GetItemACValue(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oInitiator));
|
|
effect eLink = EffectLinkEffects(EffectACDecrease(nShield), EffectAttackIncrease(nShield/2));
|
|
eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_ARMOR_OF_DARKNESS));
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(eLink), oInitiator, 6.0);
|
|
}
|
|
}
|
|
|
|
void XaniqosSchool(object oInitiator, object oTarget, object oItem, int nEvent)
|
|
{
|
|
int nType = GetBaseItemType(oItem);
|
|
if(nEvent == EVENT_ITEM_ONHIT)
|
|
{
|
|
if(GetLocalInt(oInitiator, "XaniqosSchool") && IPGetIsProjectile(oItem)) // oItem is the ammo, since it's only applies to crossbows
|
|
{
|
|
effect eDam = EffectDamage(d6(), DAMAGE_TYPE_PIERCING);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget);
|
|
}
|
|
}
|
|
else if(nEvent == EVENT_ONPLAYEREQUIPITEM)
|
|
{
|
|
if (nType == BASE_ITEM_LIGHTCROSSBOW || nType == BASE_ITEM_HEAVYCROSSBOW)
|
|
{
|
|
object oAmmo = GetItemInSlot(INVENTORY_SLOT_BOLTS, oInitiator);
|
|
IPSafeAddItemProperty(oAmmo, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
AddEventScript(oAmmo, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
}
|
|
}
|
|
else if(nEvent == EVENT_ONPLAYERUNEQUIPITEM)
|
|
{
|
|
if (nType == BASE_ITEM_LIGHTCROSSBOW || nType == BASE_ITEM_HEAVYCROSSBOW)
|
|
{
|
|
object oAmmo = GetItemInSlot(INVENTORY_SLOT_BOLTS, oInitiator);
|
|
RemoveSpecificProperty(oAmmo, ITEM_PROPERTY_ONHITCASTSPELL, IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", -1, DURATION_TYPE_TEMPORARY);
|
|
RemoveEventScript(oAmmo, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
}
|
|
}
|
|
else if(nEvent == EVENT_ONHEARTBEAT)
|
|
{
|
|
// Check to see if the WP is valid
|
|
string sWPTag = "XaniqosWP_" + GetName(oInitiator);
|
|
object oTestWP = GetWaypointByTag(sWPTag);
|
|
if (!GetIsObjectValid(oTestWP))
|
|
{
|
|
// Create waypoint for the movement
|
|
CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oInitiator), FALSE, sWPTag);
|
|
if(DEBUG) DoDebug("tob_feats: Xaniqos WP for " + DebugObject2Str(oInitiator) + " didn't exist, creating. Tag: " + sWPTag);
|
|
}
|
|
else // We have a test waypoint, now to check the distance
|
|
{
|
|
// Distance moved in the last round
|
|
float fDist = GetDistanceBetween(oInitiator, oTestWP);
|
|
// Distance needed to move
|
|
float fCheck = FeetToMeters(10.0);
|
|
|
|
// Now clean up the WP and create a new one for next round's check
|
|
DestroyObject(oTestWP);
|
|
CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oInitiator), FALSE, sWPTag);
|
|
|
|
if(DEBUG) DoDebug("tob_feats: Moved enough: " + DebugBool2String(fDist >= fCheck));
|
|
|
|
// Moved the distance
|
|
if (fDist >= fCheck)
|
|
{
|
|
// We have moved far enough
|
|
SetLocalInt(oInitiator, "XaniqosSchool", TRUE);
|
|
// Only lasts for a round
|
|
DelayCommand(6.0, DeleteLocalInt(oInitiator, "XaniqosSchool"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShieldWall(object oInitiator)
|
|
{
|
|
int nBase = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oInitiator));
|
|
if (nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD)
|
|
{
|
|
location lTarget = GetLocation(oInitiator);
|
|
// Use the function to get the closest creature as a target
|
|
object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
while(GetIsObjectValid(oAreaTarget))
|
|
{
|
|
if(oAreaTarget != oInitiator && // Not you
|
|
GetIsInMeleeRange(oInitiator, oAreaTarget) && // They must be in melee range
|
|
GetIsFriend(oAreaTarget, oInitiator)) // Only allies
|
|
{
|
|
int nBase2 = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oAreaTarget));
|
|
if (nBase2 == BASE_ITEM_SMALLSHIELD || nBase2 == BASE_ITEM_LARGESHIELD || nBase2 == BASE_ITEM_TOWERSHIELD) //Ally must have a shield
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACIncrease(2, AC_SHIELD_ENCHANTMENT_BONUS)), oAreaTarget, 6.0);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACIncrease(2, AC_SHIELD_ENCHANTMENT_BONUS)), oInitiator, 6.0);
|
|
}
|
|
}
|
|
//Select the next target within the spell shape.
|
|
oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CrossbowSniper(object oInitiator, object oItem, int nEvent)
|
|
{
|
|
int nType = GetBaseItemType(oItem);
|
|
int nDex = GetAbilityModifier(ABILITY_DEXTERITY, oInitiator);
|
|
if(nEvent == EVENT_ONPLAYEREQUIPITEM)
|
|
{
|
|
if ((nType == BASE_ITEM_LIGHTCROSSBOW && GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW, oInitiator)) ||
|
|
(nType == BASE_ITEM_HEAVYCROSSBOW && GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW, oInitiator)))
|
|
{
|
|
object oAmmo = GetItemInSlot(INVENTORY_SLOT_BOLTS, oInitiator);
|
|
IPSafeAddItemProperty(oAmmo, ItemPropertyDamageBonus(DAMAGE_TYPE_PIERCING, nDex/2), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
}
|
|
}
|
|
else if(nEvent == EVENT_ONPLAYERUNEQUIPITEM)
|
|
{
|
|
if ((nType == BASE_ITEM_LIGHTCROSSBOW && GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW, oInitiator)) ||
|
|
(nType == BASE_ITEM_HEAVYCROSSBOW && GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW, oInitiator)))
|
|
{
|
|
object oAmmo = GetItemInSlot(INVENTORY_SLOT_BOLTS, oInitiator);
|
|
RemoveSpecificProperty(oAmmo, ITEM_PROPERTY_DAMAGE_BONUS, DAMAGE_TYPE_PIERCING, nDex/2, 1, "", -1, DURATION_TYPE_TEMPORARY);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExpeditiousDodge(object oInitiator)
|
|
{
|
|
// Check to see if the WP is valid
|
|
string sWPTag = "ExpeditiousWP_" + GetName(oInitiator);
|
|
object oTestWP = GetWaypointByTag(sWPTag);
|
|
if (!GetIsObjectValid(oTestWP))
|
|
{
|
|
// Create waypoint for the movement
|
|
CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oInitiator), FALSE, sWPTag);
|
|
if(DEBUG) DoDebug("tob_feats: Expeditious WP for " + DebugObject2Str(oInitiator) + " didn't exist, creating. Tag: " + sWPTag);
|
|
}
|
|
else // We have a test waypoint, now to check the distance
|
|
{
|
|
// Distance moved in the last round
|
|
float fDist = GetDistanceBetween(oInitiator, oTestWP);
|
|
// Distance needed to move
|
|
float fCheck = FeetToMeters(40.0);
|
|
|
|
// Now clean up the WP and create a new one for next round's check
|
|
DestroyObject(oTestWP);
|
|
CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", GetLocation(oInitiator), FALSE, sWPTag);
|
|
|
|
if(DEBUG) DoDebug("tob_feats: Moved enough: " + DebugBool2String(fDist >= fCheck));
|
|
|
|
// Moved the distance
|
|
if (fDist >= fCheck)
|
|
{
|
|
// We have moved far enough
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACIncrease(2, AC_DODGE_BONUS)), oInitiator, 6.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CrescentMoon(object oInitiator, object oTarget)
|
|
{
|
|
int nRight = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator));
|
|
int nLeft = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oInitiator));
|
|
if (nLeft == BASE_ITEM_DAGGER && (nRight == BASE_ITEM_SCIMITAR || nRight == BASE_ITEM_LONGSWORD || nRight == BASE_ITEM_BASTARDSWORD || nRight == BASE_ITEM_SHORTSWORD))
|
|
{
|
|
int nHits = GetLocalInt(oTarget, "CrescentMoon"); // Check hits
|
|
if (nHits == 2)
|
|
{
|
|
nHits = 0;
|
|
// Free disarm attempt, once a round, per target
|
|
if (!GetLocalInt(oTarget, "CrescentMoonDelay"))
|
|
{
|
|
DoDisarm(oInitiator, oTarget);
|
|
SetLocalInt(oTarget, "CrescentMoonDelay", TRUE);
|
|
DelayCommand(6.0, DeleteLocalInt(oTarget, "CrescentMoonDelay"));
|
|
}
|
|
}
|
|
else
|
|
nHits++;
|
|
|
|
SetLocalInt(oTarget, "CrescentMoon", nHits); // Store the hits
|
|
}
|
|
}
|
|
|
|
void QuickStaff(object oInitiator)
|
|
{
|
|
// Only applies when using expertise
|
|
if(GetModeActive(ACTION_MODE_EXPERTISE) || GetActionMode(oInitiator, ACTION_MODE_EXPERTISE) || GetLastAttackMode(oInitiator) == COMBAT_MODE_EXPERTISE ||
|
|
GetModeActive(ACTION_MODE_IMPROVED_EXPERTISE) || GetActionMode(oInitiator, ACTION_MODE_IMPROVED_EXPERTISE) || GetLastAttackMode(oInitiator) == COMBAT_MODE_IMPROVED_EXPERTISE)
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectACIncrease(2), oInitiator, 6.0);
|
|
}
|
|
}
|
|
|
|
void BearFangGrapple(object oInitiator, object oTarget)
|
|
{
|
|
DoGrapple(oInitiator, oTarget, 0, FALSE, TRUE);
|
|
}
|
|
|
|
void BearFang(object oInitiator, object oTarget)
|
|
{
|
|
int nRight = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator));
|
|
int nLeft = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oInitiator));
|
|
if (nLeft == BASE_ITEM_DAGGER && (nRight == BASE_ITEM_BATTLEAXE || nRight == BASE_ITEM_HANDAXE || nRight == BASE_ITEM_DWARVENWARAXE))
|
|
{
|
|
int nHits = GetLocalInt(oTarget, "BearFang"); // Check hits
|
|
if (nHits == 2)
|
|
{
|
|
SetLocalInt(oInitiator, "BearFangGrapple", TRUE);
|
|
ForceUnequip(oInitiator, GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator), INVENTORY_SLOT_RIGHTHAND);
|
|
DelayCommand(0.6, BearFangGrapple(oInitiator, oTarget));
|
|
DeleteLocalInt(oInitiator, "BearFang");
|
|
}
|
|
else
|
|
nHits++;
|
|
|
|
SetLocalInt(oTarget, "BearFang", nHits); // Store the hits
|
|
}
|
|
}
|
|
|
|
void BearFangHB(object oInitiator)
|
|
{
|
|
// Resets every round
|
|
DeleteLocalInt(oInitiator, "BearFang");
|
|
}
|
|
|
|
void ImprovedRapidShot(object oInitiator)
|
|
{
|
|
// Only applies when using Rapid Shot
|
|
if(GetModeActive(ACTION_MODE_RAPID_SHOT) || GetActionMode(oInitiator, ACTION_MODE_RAPID_SHOT) || GetLastAttackMode(oInitiator) == COMBAT_MODE_RAPID_SHOT)
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectAttackIncrease(2), oInitiator, 6.0);
|
|
}
|
|
}
|
|
|
|
void DireFlailSmash(object oInitiator, object oTarget)
|
|
{
|
|
// Check for a first hit
|
|
int nHit = GetLocalInt(oTarget, "DireFlailSmash");
|
|
|
|
if (!nHit)
|
|
{
|
|
SetLocalInt(oTarget, "DireFlailSmash", TRUE);
|
|
DelayCommand(6.0, DeleteLocalInt(oTarget, "DireFlailSmash"));
|
|
}
|
|
else // Hit them twice in six seconds
|
|
{
|
|
int nDC = 10 + GetHitDice(oInitiator)/2 + GetAbilityModifier(ABILITY_STRENGTH, oInitiator);
|
|
if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_NONE, oInitiator))
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectDazed()), oTarget, RoundsToSeconds(1));
|
|
}
|
|
DeleteLocalInt(oTarget, "DireFlailSmash");
|
|
}
|
|
}
|
|
|
|
void ShieldSpecialization(object oInitiator)
|
|
{
|
|
object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oInitiator);
|
|
// Shield specialization
|
|
if((GetBaseItemType(oItem) == BASE_ITEM_SMALLSHIELD && GetHasFeat(FEAT_SHIELD_SPECIALIZATION_LIGHT, oInitiator)) || (GetBaseItemType(oItem) == BASE_ITEM_LARGESHIELD && GetHasFeat(FEAT_SHIELD_SPECIALIZATION_HEAVY, oInitiator)))
|
|
{
|
|
itemproperty ip = GetFirstItemProperty(oItem);
|
|
int iTemp;
|
|
while(GetIsItemPropertyValid(ip))
|
|
{
|
|
int iIpType = GetItemPropertyType(ip);
|
|
if (iIpType == ITEM_PROPERTY_AC_BONUS)
|
|
{
|
|
iTemp = GetItemPropertyCostTableValue(ip);
|
|
break;
|
|
}
|
|
|
|
ip = GetNextItemProperty(oItem);
|
|
}
|
|
//FloatingTextStringOnCreature("Adding Shield Specialization bonus of "+IntToString(iTemp+1), oInitiator, FALSE);
|
|
IPSafeAddItemProperty(oItem, ItemPropertyACBonus(iTemp+1), 5.95, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
|
|
}
|
|
}
|
|
|
|
void FocusedShield(object oInitiator)
|
|
{
|
|
object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oInitiator);
|
|
int nBase = GetBaseItemType(oItem);
|
|
if ((nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD) && GetIsPsionicallyFocused(oInitiator))
|
|
{
|
|
itemproperty ip = GetFirstItemProperty(oItem);
|
|
int iTemp;
|
|
while(GetIsItemPropertyValid(ip))
|
|
{
|
|
int iIpType = GetItemPropertyType(ip);
|
|
if (iIpType == ITEM_PROPERTY_AC_BONUS)
|
|
{
|
|
iTemp = GetItemPropertyCostTableValue(ip);
|
|
break;
|
|
}
|
|
|
|
ip = GetNextItemProperty(oItem);
|
|
}
|
|
//FloatingTextStringOnCreature("Adding Focused Shield bonus of "+IntToString(iTemp+1), oInitiator, FALSE);
|
|
IPSafeAddItemProperty(oItem, ItemPropertyACBonus(iTemp+1), 5.95, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
|
|
}
|
|
}
|
|
|
|
void Shieldmate(object oInitiator)
|
|
{
|
|
// Have to be awake and aware to count
|
|
if (PRCGetHasEffect(EFFECT_TYPE_CONFUSED, oInitiator) || PRCGetHasEffect(EFFECT_TYPE_DOMINATED, oInitiator) || PRCGetHasEffect(EFFECT_TYPE_DAZED, oInitiator) ||
|
|
PRCGetHasEffect(EFFECT_TYPE_PETRIFY, oInitiator) || PRCGetHasEffect(EFFECT_TYPE_PARALYZE, oInitiator) || PRCGetHasEffect(EFFECT_TYPE_SLEEP, oInitiator) ||
|
|
PRCGetHasEffect(EFFECT_TYPE_STUNNED, oInitiator) || PRCGetHasEffect(EFFECT_TYPE_FRIGHTENED, oInitiator) || GetGrapple(oInitiator))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int nBase = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oInitiator));
|
|
if (nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD)
|
|
{
|
|
location lTarget = GetLocation(oInitiator);
|
|
// Use the function to get the closest creature as a target
|
|
object oAreaTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
while(GetIsObjectValid(oAreaTarget))
|
|
{
|
|
if(oAreaTarget != oInitiator && // Not you
|
|
GetIsInMeleeRange(oInitiator, oAreaTarget) && // They must be in melee range
|
|
GetIsFriend(oAreaTarget, oInitiator)) // Only allies
|
|
{
|
|
int nBonus = 1;
|
|
if (nBase == BASE_ITEM_TOWERSHIELD) nBonus += 1;
|
|
if (GetHasFeat(FEAT_IMPROVED_SHIELDMATE, oInitiator)) nBonus += 1;
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectACIncrease(nBonus, AC_SHIELD_ENCHANTMENT_BONUS)), oAreaTarget, 6.0);
|
|
}
|
|
//Select the next target within the spell shape.
|
|
oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RethDekalaAura(object oInitiator)
|
|
{
|
|
location lTarget = GetLocation(oInitiator);
|
|
// 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 != oInitiator && // Not you. Hits everyone else though, even allies.
|
|
GetIsInMeleeRange(oInitiator, oAreaTarget)) // They must be in melee range
|
|
{
|
|
int nDamage = d6();
|
|
int nDC = 10 + GetHitDice(oInitiator)/2 + GetAbilityModifier(ABILITY_CONSTITUTION, oInitiator);
|
|
if(!PRCMySavingThrow(SAVING_THROW_FORT, oAreaTarget, nDC, SAVING_THROW_TYPE_NONE))
|
|
{
|
|
// Half fire, half acid
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, SupernaturalEffect(EffectDamage(nDamage/2, DAMAGE_TYPE_FIRE)), oAreaTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FLAME_M), oAreaTarget);
|
|
// The +1 makes acid round up instead of round down, so you don't miss out on odd numbers
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, SupernaturalEffect(EffectDamage((nDamage+1)/2, DAMAGE_TYPE_ACID)), oAreaTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_ACID_L), oAreaTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(EffectSickened()), oAreaTarget, 6.0);
|
|
}
|
|
}
|
|
//Select the next target within the spell shape.
|
|
oAreaTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_MEDIUM, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
|
}
|
|
}
|
|
|
|
void HadrimoiPerfectSymmetry(object oInitiator)
|
|
{
|
|
int nSize = PRCGetCreatureSize(oInitiator);
|
|
object oSkin = GetPCSkin(oInitiator);
|
|
object oItemR = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator);
|
|
int nWeaponSizeR = GetWeaponSize(oItemR);
|
|
object oItemL = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oInitiator);
|
|
int nWeaponSizeL = GetWeaponSize(oItemL);
|
|
// is the size appropriate for a light weapon?
|
|
if (nWeaponSizeR < nSize && nWeaponSizeL < nSize)
|
|
{
|
|
SetLocalInt(oInitiator, "HadrimoiPerfectSymmetry", TRUE);
|
|
IPSafeAddItemProperty(oSkin, PRCItemPropertyBonusFeat(IP_CONST_FEAT_AMBIDEXTROUS), 9999.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
IPSafeAddItemProperty(oSkin, PRCItemPropertyBonusFeat(IP_CONST_FEAT_TWO_WEAPON_FIGHTING), 9999.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
//int nBAB = GetBaseAttackBonus(oInitiator);
|
|
SetCompositeAttackBonus(oInitiator, "HadrimoiPerfectSymmetry", 2, ATTACK_BONUS_MISC);
|
|
/*object oWP = GetObjectToApplyNewEffect("WP_HadrimoiPerfectSymmetry", oInitiator, TRUE);
|
|
AssignCommand(oWP, ActionDoCommand(ApplyEffectToObject(DURATION_TYPE_PERMANENT, SupernaturalEffect(EffectModifyAttacks(nAttackCount-1)), oInitiator)));*/
|
|
}
|
|
else
|
|
{
|
|
DeleteLocalInt(oInitiator, "HadrimoiPerfectSymmetry");
|
|
RemoveSpecificProperty(oSkin, ITEM_PROPERTY_BONUS_FEAT, IP_CONST_FEAT_AMBIDEXTROUS, -1, -1, "", -1, DURATION_TYPE_TEMPORARY);
|
|
RemoveSpecificProperty(oSkin, ITEM_PROPERTY_BONUS_FEAT, IP_CONST_FEAT_TWO_WEAPON_FIGHTING, -1, -1, "", -1, DURATION_TYPE_TEMPORARY);
|
|
SetCompositeAttackBonus(oInitiator, "HadrimoiPerfectSymmetry", 0, ATTACK_BONUS_MISC);
|
|
/*object oWP = GetObjectToApplyNewEffect("WP_HadrimoiPerfectSymmetry", oInitiator, TRUE);*/
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Void Main and Event Triggers */
|
|
//////////////////////////////////////////////////
|
|
|
|
void main()
|
|
{
|
|
int nEvent = GetRunningEvent();
|
|
if(DEBUG) DoDebug("tob_feats running, event: " + IntToString(nEvent));
|
|
|
|
// Get the PC. This is event-dependent
|
|
object oInitiator;
|
|
switch(nEvent)
|
|
{
|
|
case EVENT_ITEM_ONHIT: oInitiator = OBJECT_SELF; break;
|
|
case EVENT_ONPLAYEREQUIPITEM: oInitiator = GetItemLastEquippedBy(); break;
|
|
case EVENT_ONPLAYERUNEQUIPITEM: oInitiator = GetItemLastUnequippedBy(); break;
|
|
case EVENT_ONHEARTBEAT: oInitiator = OBJECT_SELF; break;
|
|
|
|
default:
|
|
oInitiator = OBJECT_SELF;
|
|
}
|
|
|
|
object oItem;
|
|
object oSkin = GetPCSkin(oInitiator);
|
|
int nSnap = GetLocalInt(oInitiator, "SnapKick");
|
|
|
|
// We aren't being called from any event, instead from EvalPRCFeats
|
|
if(nEvent == FALSE)
|
|
{
|
|
if (BladeMeditationFeat(oInitiator) >= 1) BladeMeditation(oInitiator);
|
|
|
|
// Hook in the events
|
|
if(DEBUG) DoDebug("tob_feats: Adding eventhooks");
|
|
AddEventScript(oInitiator, EVENT_ONPLAYEREQUIPITEM, "tob_feats", TRUE, FALSE);
|
|
AddEventScript(oInitiator, EVENT_ONPLAYERUNEQUIPITEM, "tob_feats", TRUE, FALSE);
|
|
AddEventScript(oInitiator, EVENT_ONHEARTBEAT, "tob_feats", TRUE, FALSE);
|
|
}
|
|
else if(nEvent == EVENT_ITEM_ONHIT)
|
|
{
|
|
oItem = GetSpellCastItem();
|
|
object oTarget = PRCGetSpellTargetObject();
|
|
if(DEBUG) DoDebug("tob_feats: OnHit:\n"
|
|
+ "oInitiator = " + DebugObject2Str(oInitiator) + "\n"
|
|
+ "oItem = " + DebugObject2Str(oItem) + "\n"
|
|
+ "oTarget = " + DebugObject2Str(oTarget) + "\n"
|
|
);
|
|
if (GetHasFeat(FEAT_DEVOTED_BULWARK, oInitiator)) DevotedBulwark(oInitiator, oTarget);
|
|
if (nSnap == TRUE) SnapKick(oInitiator, oTarget);
|
|
if (GetHasFeat(FEAT_THREE_MOUNTAINS, oInitiator)) ThreeMountains(oInitiator, oTarget);
|
|
if (GetHasFeat(FEAT_VAE_SCHOOL, oInitiator) && GetCanSneakAttack(oTarget, oInitiator)) VaeSchool(oInitiator, oTarget);
|
|
if (GetHasFeat(FEAT_XANIQOS_SCHOOL, oInitiator)) XaniqosSchool(oInitiator, oTarget, oItem, nEvent);
|
|
if (GetHasFeat(FEAT_CRESCENT_MOON, oInitiator)) CrescentMoon(oInitiator, oTarget);
|
|
if (GetHasFeat(FEAT_BEAR_FANG, oInitiator)) BearFang(oInitiator, oTarget);
|
|
if (GetHasFeat(FEAT_DIRE_FLAIL_SMASH, oInitiator)) DireFlailSmash(oInitiator, oTarget);
|
|
}// end if - Running OnHit event
|
|
else if(nEvent == EVENT_ONPLAYEREQUIPITEM)
|
|
{
|
|
oInitiator = GetItemLastEquippedBy();
|
|
oItem = GetItemLastEquipped();
|
|
if(DEBUG) DoDebug("tob_feats - OnEquip\n"
|
|
+ "oInitiator = " + DebugObject2Str(oInitiator) + "\n"
|
|
+ "oItem = " + DebugObject2Str(oItem) + "\n"
|
|
);
|
|
// Armor checks
|
|
if(GetBaseItemType(oItem) == BASE_ITEM_ARMOR)
|
|
{
|
|
if (GetHasFeat(FEAT_DEVOTED_BULWARK, oInitiator))
|
|
{
|
|
// Add eventhook to the armor
|
|
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
}
|
|
}
|
|
// Weapons for Three Mountains
|
|
if (GetHasFeat(FEAT_THREE_MOUNTAINS, oInitiator))
|
|
{
|
|
if (oItem == GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator) && (GetBaseItemType(oItem) == BASE_ITEM_MORNINGSTAR || GetBaseItemType(oItem) == BASE_ITEM_HEAVYFLAIL || GetBaseItemType(oItem) == BASE_ITEM_DIREMACE) || GetBaseItemType(oItem) == BASE_ITEM_HEAVY_MACE)
|
|
{
|
|
// Add eventhook to the weapon
|
|
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
}
|
|
}
|
|
// Weapons for Snap Kick
|
|
if (oItem == GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator) && IPGetIsMeleeWeapon(oItem) && nSnap == TRUE)
|
|
{
|
|
// Add eventhook to the weapon
|
|
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
}
|
|
// Weapons for Vae School
|
|
if (oItem == GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator) && GetBaseItemType(oItem) == BASE_ITEM_WHIP && GetHasFeat(FEAT_VAE_SCHOOL, oInitiator))
|
|
{
|
|
// Add eventhook to the weapon
|
|
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
}
|
|
if (GetHasFeat(FEAT_XANIQOS_SCHOOL, oInitiator)) XaniqosSchool(oInitiator, OBJECT_INVALID, oItem, nEvent);
|
|
if (GetHasFeat(FEAT_CROSSBOW_SNIPER, oInitiator)) CrossbowSniper(oInitiator, oItem, nEvent);
|
|
// Weapons for Crescent Moon
|
|
if (GetHasFeat(FEAT_CRESCENT_MOON, oInitiator))
|
|
{
|
|
if (GetBaseItemType(oItem) == BASE_ITEM_SCIMITAR || GetBaseItemType(oItem) == BASE_ITEM_LONGSWORD || GetBaseItemType(oItem) == BASE_ITEM_BASTARDSWORD || GetBaseItemType(oItem) == BASE_ITEM_DAGGER || GetBaseItemType(oItem) == BASE_ITEM_SHORTSWORD)
|
|
{
|
|
// Add eventhook to the weapon
|
|
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
}
|
|
}
|
|
// Weapons for Bear Fang
|
|
if (GetHasFeat(FEAT_BEAR_FANG, oInitiator))
|
|
{
|
|
if (GetBaseItemType(oItem) == BASE_ITEM_BATTLEAXE || GetBaseItemType(oItem) == BASE_ITEM_HANDAXE || GetBaseItemType(oItem) == BASE_ITEM_DWARVENWARAXE || GetBaseItemType(oItem) == BASE_ITEM_DAGGER)
|
|
{
|
|
// Add eventhook to the weapon
|
|
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
}
|
|
}
|
|
// Weapons for Dire Flail Smash
|
|
if (GetHasFeat(FEAT_DIRE_FLAIL_SMASH, oInitiator))
|
|
{
|
|
if (oItem == GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator) && GetBaseItemType(oItem) == BASE_ITEM_DIREMACE)
|
|
{
|
|
// Add eventhook to the weapon
|
|
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
}
|
|
}
|
|
if (GetHasFeat(FEAT_SHIELDED_CASTING, oInitiator))
|
|
{
|
|
int nBase = GetBaseItemType(oItem);
|
|
if (nBase == BASE_ITEM_SMALLSHIELD || nBase == BASE_ITEM_LARGESHIELD || nBase == BASE_ITEM_TOWERSHIELD)
|
|
{
|
|
IPSafeAddItemProperty(oSkin, PRCItemPropertyBonusFeat(IP_CONST_IMP_CC), 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
|
}
|
|
}
|
|
if (GetRacialType(oInitiator) == RACIAL_TYPE_RETH_DEKALA && GetIsWeapon(oItem))
|
|
{
|
|
IPSafeAddItemProperty(oItem, ItemPropertyAttackBonus(4), HoursToSeconds(24), X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
|
|
IPSafeAddItemProperty(oItem, ItemPropertyAttackPenalty(4), HoursToSeconds(24), X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
|
|
}
|
|
if (GetRacialType(oInitiator) == RACIAL_TYPE_HADRIMOI) HadrimoiPerfectSymmetry(oInitiator);
|
|
}
|
|
// We are called from the OnPlayerUnEquipItem eventhook. Remove OnHitCast: Unique Power from oInitiator's weapon
|
|
else if(nEvent == EVENT_ONPLAYERUNEQUIPITEM)
|
|
{
|
|
oInitiator = GetItemLastUnequippedBy();
|
|
oItem = GetItemLastUnequipped();
|
|
if(DEBUG) DoDebug("tob_feats - OnUnEquip\n"
|
|
+ "oInitiator = " + DebugObject2Str(oInitiator) + "\n"
|
|
+ "oItem = " + DebugObject2Str(oItem) + "\n"
|
|
);
|
|
// Only applies to armours
|
|
if(GetBaseItemType(oItem) == BASE_ITEM_ARMOR)
|
|
{
|
|
// Add eventhook to the item
|
|
RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
|
|
// Remove the temporary OnHitCastSpell: Unique
|
|
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ONHITCASTSPELL, IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", 1, DURATION_TYPE_TEMPORARY);
|
|
}
|
|
// Weapons for Snap Kick
|
|
if (IPGetIsMeleeWeapon(oItem) && nSnap)
|
|
{
|
|
// Remove eventhook to the item
|
|
RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
|
|
// Remove the temporary OnHitCastSpell: Unique
|
|
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ONHITCASTSPELL, IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", 1, DURATION_TYPE_TEMPORARY);
|
|
}
|
|
// Weapons for Three Mountains
|
|
if (GetHasFeat(FEAT_THREE_MOUNTAINS, oInitiator))
|
|
{
|
|
if (GetBaseItemType(oItem) == BASE_ITEM_MORNINGSTAR || GetBaseItemType(oItem) == BASE_ITEM_HEAVYFLAIL || GetBaseItemType(oItem) == BASE_ITEM_DIREMACE || GetBaseItemType(oItem) == BASE_ITEM_HEAVY_MACE)
|
|
{
|
|
// Remove eventhook to the item
|
|
RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
|
|
// Remove the temporary OnHitCastSpell: Unique
|
|
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ONHITCASTSPELL, IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", 1, DURATION_TYPE_TEMPORARY);
|
|
}
|
|
}
|
|
// Weapons for Vae School
|
|
if (GetBaseItemType(oItem) == BASE_ITEM_WHIP && GetHasFeat(FEAT_VAE_SCHOOL, oInitiator))
|
|
{
|
|
// Remove eventhook to the item
|
|
RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
|
|
// Remove the temporary OnHitCastSpell: Unique
|
|
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ONHITCASTSPELL, IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", 1, DURATION_TYPE_TEMPORARY);
|
|
}
|
|
if (GetHasFeat(FEAT_XANIQOS_SCHOOL, oInitiator)) XaniqosSchool(oInitiator, OBJECT_INVALID, oItem, nEvent);
|
|
if (GetHasFeat(FEAT_CROSSBOW_SNIPER, oInitiator)) CrossbowSniper(oInitiator, oItem, nEvent);
|
|
// Weapons for Crescent Moon
|
|
if (GetHasFeat(FEAT_CRESCENT_MOON, oInitiator))
|
|
{
|
|
if (GetBaseItemType(oItem) == BASE_ITEM_SCIMITAR || GetBaseItemType(oItem) == BASE_ITEM_LONGSWORD || GetBaseItemType(oItem) == BASE_ITEM_BASTARDSWORD || GetBaseItemType(oItem) == BASE_ITEM_DAGGER || GetBaseItemType(oItem) == BASE_ITEM_SHORTSWORD)
|
|
{
|
|
// Remove eventhook to the item
|
|
RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
|
|
// Remove the temporary OnHitCastSpell: Unique
|
|
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ONHITCASTSPELL, IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", 1, DURATION_TYPE_TEMPORARY);
|
|
}
|
|
}
|
|
// Weapons for Bear Fang
|
|
if (GetHasFeat(FEAT_BEAR_FANG, oInitiator))
|
|
{
|
|
if (GetBaseItemType(oItem) == BASE_ITEM_BATTLEAXE || GetBaseItemType(oItem) == BASE_ITEM_HANDAXE || GetBaseItemType(oItem) == BASE_ITEM_DWARVENWARAXE || GetBaseItemType(oItem) == BASE_ITEM_DAGGER)
|
|
{
|
|
// Remove eventhook to the item
|
|
RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
|
|
// Remove the temporary OnHitCastSpell: Unique
|
|
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ONHITCASTSPELL, IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", 1, DURATION_TYPE_TEMPORARY);
|
|
}
|
|
}
|
|
// Weapons for Dire Flail Smash
|
|
if (GetHasFeat(FEAT_DIRE_FLAIL_SMASH, oInitiator))
|
|
{
|
|
if (GetBaseItemType(oItem) == BASE_ITEM_DIREMACE)
|
|
{
|
|
// Remove eventhook to the item
|
|
RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_feats", TRUE, FALSE);
|
|
|
|
// Remove the temporary OnHitCastSpell: Unique
|
|
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ONHITCASTSPELL, IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", 1, DURATION_TYPE_TEMPORARY);
|
|
}
|
|
}
|
|
if (GetHasFeat(FEAT_SHIELDED_CASTING, oInitiator))
|
|
{
|
|
// If you don't have a shield in your left hand, no benefit
|
|
int nBase = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oInitiator));
|
|
if (nBase != BASE_ITEM_SMALLSHIELD && nBase != BASE_ITEM_LARGESHIELD && nBase != BASE_ITEM_TOWERSHIELD)
|
|
{
|
|
RemoveSpecificProperty(oSkin, ITEM_PROPERTY_BONUS_FEAT, IP_CONST_IMP_CC);
|
|
}
|
|
}
|
|
if (GetRacialType(oInitiator) == RACIAL_TYPE_RETH_DEKALA && GetIsWeapon(oItem))
|
|
{
|
|
// Remove the attack bonus
|
|
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ATTACK_BONUS, -1, -1, 1, "", -1, DURATION_TYPE_TEMPORARY);
|
|
RemoveSpecificProperty(oItem, ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER, -1, -1, 1, "", -1, DURATION_TYPE_TEMPORARY);
|
|
}
|
|
if (GetRacialType(oInitiator) == RACIAL_TYPE_HADRIMOI) HadrimoiPerfectSymmetry(oInitiator);
|
|
}
|
|
else if(nEvent == EVENT_ONHEARTBEAT)
|
|
{
|
|
if(GetHasFeat(FEAT_SHADOW_BLADE, oInitiator)) ShadowBlade(oInitiator);
|
|
if(GetHasFeat(FEAT_RAPID_ASSAULT, oInitiator)) RapidAssault(oInitiator);
|
|
if(GetHasFeat(FEAT_DESERT_WIND_DODGE, oInitiator) || GetHasFeat(FEAT_DESERT_FIRE, oInitiator)) DesertWind(oInitiator);
|
|
if(GetHasFeat(FEAT_IRONHEART_AURA, oInitiator)) IronheartAura(oInitiator);
|
|
if(GetHasFeat(FEAT_SHADOW_TRICKSTER, oInitiator)) ShadowTrickster(oInitiator);
|
|
if(GetHasFeat(FEAT_WHITE_RAVEN_DEFENSE, oInitiator)) WhiteRavenDefense(oInitiator);
|
|
if(GetHasFeat(FEAT_INLINDL_SCHOOL, oInitiator)) InlindlSchool(oInitiator);
|
|
if(GetHasFeat(FEAT_XANIQOS_SCHOOL, oInitiator)) XaniqosSchool(oInitiator, OBJECT_INVALID, oItem, nEvent);
|
|
if(GetHasFeat(FEAT_EXPEDITIOUS_DODGE, oInitiator)) ExpeditiousDodge(oInitiator);
|
|
if(GetHasFeat(FEAT_SHIELD_WALL, oInitiator)) ShieldWall(oInitiator);
|
|
if(GetHasFeat(FEAT_QUICK_STAFF, oInitiator)) QuickStaff(oInitiator);
|
|
if(GetHasFeat(FEAT_IMPROVED_RAPID_SHOT, oInitiator)) ImprovedRapidShot(oInitiator);
|
|
if(GetHasFeat(FEAT_BEAR_FANG, oInitiator)) BearFangHB(oInitiator);
|
|
if(GetHasFeat(FEAT_SHIELD_SPECIALIZATION_LIGHT, oInitiator) || GetHasFeat(FEAT_SHIELD_SPECIALIZATION_HEAVY, oInitiator)) ShieldSpecialization(oInitiator);
|
|
if(GetHasFeat(FEAT_FOCUSED_SHIELD, oInitiator)) FocusedShield(oInitiator);
|
|
if(GetHasFeat(FEAT_SHIELDMATE, oInitiator)) Shieldmate(oInitiator);
|
|
if(GetRacialType(oInitiator) == RACIAL_TYPE_RETH_DEKALA) RethDekalaAura(oInitiator);
|
|
}
|
|
|
|
} |