Files
PRC8/nwn/nwnprc/trunk/spells/x2_s0_enhweap.nss
Jaysyn904 c1188ebb28 2026/02/19 Update
Eldritch Disciple should have Verminlord as an epic bonus feat.
Only spellcasters can use Craft (Alchemy).
Added Great Charisma and removed Great Wisdom as Force Missile Mage epic bonus feats.
Frenzied Berserker was missing Great STR 10 as an epic bonus feat.
Oozemaster had several epic bonus feats only grantable at 10th lvl.
Pyromancer's Great Charisma bonus feats were pointing at wrong or non-existent feats.
Corrected Frenzied Berserker's skill list.
Corrected Legendary Dreadnought's skill list.
Added placeholders for Combat Form feats.
Added Combat Forms masterfeats.
Fixed ASF issue with Eldritch Sculptor's 2nd blast.
Gated debug in CheckIfDeleveled().
Updated AddRacialRestrictions() for latest races.
Vow of Poverty & Forsaker work better together at level up.
Maybe fixed the mass ability buffs not hitting all targets issue.  Needs mulitplayer testing.
Updated some creature abilities to use PRC functions.
2026-02-19 21:10:22 -05:00

451 lines
19 KiB
Plaintext

/*
x2_s0_enhweap
Consolidation of:
Blackstaff
Blade Thirst
Bless Weapon
Darkfire
Deafening Clang
Flame Weapon
Holy Sword
Keen Edge
Magic Weapon
Greater Magic Weapon
Unholy Sword
Weapon of Impact
Shillelagh
By: Flaming_Sword
Created: Jun 29, 2006
Modified: Jun 30, 2006
Modified; Apr 7, 2007 by motu99
*/
#include "prc_sp_func"
#include "x2_inc_toollib"
/// Used simply to use up a bit less processor time on the delayed command to delete these 6 Ints.
// motu99: seems to be not used any more
void DeleteTheInts(object oTarget)
{
DeleteLocalInt(oTarget, "X2_Wep_Dam_Type");
DeleteLocalInt(oTarget, "X2_Wep_Caster_Lvl");
DeleteLocalInt(oTarget, "X2_Wep_Dam_Type_DF");
DeleteLocalInt(oTarget, "X2_Wep_Caster_Lvl_DF");
DeleteLocalInt(oTarget, "X2_Wep_Caster_Lvl_DC");
DeleteLocalInt(oTarget, "X2_Wep_Penetr_DC");
}
//------------------------------------------------------------------------------
// AN, 2003
// Returns TRUE if oItem is a slashing weapon
//------------------------------------------------------------------------------
int PRCGetSlashingWeapon(object oItem)
{
//Declare major variables
int nItem = GetBaseItemType(oItem);
//exclude creature weapons
if(nItem == BASE_ITEM_CSLASHWEAPON ||
nItem == BASE_ITEM_CSLSHPRCWEAP)
return FALSE;
int nWeapon = (StringToInt(Get2DACache("baseitems", "WeaponType", nItem)));
// 3 = slashing, 4 = piercing-slashing
return (nWeapon == 3 || nWeapon == 4);
/* if((nItem == BASE_ITEM_BASTARDSWORD) ||
(nItem == BASE_ITEM_BATTLEAXE) ||
(nItem == BASE_ITEM_DOUBLEAXE) ||
(nItem == BASE_ITEM_GREATAXE) ||
(nItem == BASE_ITEM_GREATSWORD) ||
(nItem == BASE_ITEM_HALBERD) ||
(nItem == BASE_ITEM_HANDAXE) ||
(nItem == BASE_ITEM_KAMA) ||
(nItem == BASE_ITEM_KATANA) ||
(nItem == BASE_ITEM_KUKRI) ||
(nItem == BASE_ITEM_LONGSWORD)||
(nItem == BASE_ITEM_SCIMITAR)||
(nItem == BASE_ITEM_SCYTHE)||
(nItem == BASE_ITEM_SICKLE)||
(nItem == BASE_ITEM_TWOBLADEDSWORD) ||
(nItem == BASE_ITEM_DWARVENWARAXE) ||
(nItem == BASE_ITEM_THROWINGAXE) ||
(nItem == BASE_ITEM_WHIP)
|| (nItem == 300) //CEP Trident
|| (nItem == 303) //CEP Sai
|| (nItem == 305) //CEP falchion
|| (nItem == 309) //CEP assassin dager
|| (nItem == 310) //CEP katar
|| (nItem == 313) //CEP kukri2
|| (nItem == 316) //CEP falchion
|| (nItem == 319) //CEP sh_x1_mercuryls
|| (nItem == 320) //CEP sh_x1_mercurygs
|| (nItem == 321) //CEP sh_x1_doublesc
|| (nItem == 322) //CEP goad
|| (nItem == 323) //CEP windfirewheel
|| (nItem == 324) //CEP maugdoublesword
)
{
return TRUE;
}
return FALSE;*/
}
//------------------------------------------------------------------------------
// Returns TRUE if oItem is a piercing weapon
//------------------------------------------------------------------------------
int PRCGetPiercingWeapon(object oItem)
{
int nItem = GetBaseItemType(oItem);
//exclude creature weapons
if(nItem == BASE_ITEM_CBLUDGWEAPON)
return FALSE;
int nWeapon = (StringToInt(Get2DACache("baseitems", "WeaponType", nItem)));
// 2 = bludgeoning, 5 = bludgeoning/piercing
return (nWeapon == 1 || nWeapon == 4 || nWeapon == 5);
}
//------------------------------------------------------------------------------
// Returns TRUE if oItem is a bludgeoning weapon
//------------------------------------------------------------------------------
int PRCGetBludgeoningWeapon(object oItem)
{
int nItem = GetBaseItemType(oItem);
//exclude creature weapons
if(nItem == BASE_ITEM_CBLUDGWEAPON)
return FALSE;
int nWeapon = (StringToInt(Get2DACache("baseitems", "WeaponType", nItem)));
// 2 = bludgeoning, 5 = bludgeoning/piercing
return (nWeapon == 2 || nWeapon == 5);
}
void ApplyEffectsToWeapon(object oItem, int nSpellID, float fDuration, object oCaster, int nCasterLevel, int nPenetr)
{
switch(nSpellID)
{
case SPELL_BLADE_THIRST:
{
IPSafeAddItemProperty(oItem, ItemPropertyEnhancementBonus(3), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
IPSafeAddItemProperty(oItem, ItemPropertyLight(IP_CONST_LIGHTBRIGHTNESS_LOW, IP_CONST_LIGHTCOLOR_YELLOW), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
break;
}
case SPELL_BLACKSTAFF:
{
IPSafeAddItemProperty(oItem, ItemPropertyEnhancementBonus(4), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
IPSafeAddItemProperty(oItem, ItemPropertyOnHitProps(IP_CONST_ONHIT_DISPELMAGIC, IP_CONST_ONHIT_SAVEDC_16), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING);
IPSafeAddItemProperty(oItem, ItemPropertyVisualEffect(ITEM_VISUAL_EVIL), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
break;
}
case SPELL_SHILLELAGH:
{
IPSafeAddItemProperty(oItem, ItemPropertyEnhancementBonus(1), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
IPSafeAddItemProperty(oItem, ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_BLUDGEONING, IP_CONST_DAMAGEBONUS_1d6), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
IPSafeAddItemProperty(oItem, ItemPropertyVisualEffect(ITEM_VISUAL_HOLY), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
break;
}
case SPELL_BLESS_WEAPON:
{
// If the spell is cast again, any previous enhancement boni are kept
IPSafeAddItemProperty(oItem, ItemPropertyEnhancementBonus(1), fDuration, X2_IP_ADDPROP_POLICY_KEEP_EXISTING,TRUE);
// Replace existing temporary anti undead boni
IPSafeAddItemProperty(oItem, ItemPropertyDamageBonusVsRace(IP_CONST_RACIALTYPE_UNDEAD, IP_CONST_DAMAGETYPE_DIVINE, IP_CONST_DAMAGEBONUS_2d6), fDuration,X2_IP_ADDPROP_POLICY_REPLACE_EXISTING );
IPSafeAddItemProperty(oItem, ItemPropertyVisualEffect(ITEM_VISUAL_HOLY), fDuration,X2_IP_ADDPROP_POLICY_REPLACE_EXISTING,FALSE,TRUE );
break;
}
case SPELL_DARKFIRE:
case SPELL_FLAME_WEAPON:
{
int nAppearanceType = ITEM_VISUAL_FIRE;
int nDamageType = ChangedElementalDamage(oCaster, DAMAGE_TYPE_FIRE);
int bDarkfire = (nSpellID == SPELL_DARKFIRE);
//SendMessageToPC(OBJECT_SELF, "I am the caster");
switch(nDamageType)
{
case DAMAGE_TYPE_ACID: nAppearanceType = ITEM_VISUAL_ACID; break;
case DAMAGE_TYPE_COLD: nAppearanceType = ITEM_VISUAL_COLD; break;
case DAMAGE_TYPE_ELECTRICAL: nAppearanceType = ITEM_VISUAL_ELECTRICAL; break;
case DAMAGE_TYPE_SONIC: nAppearanceType = ITEM_VISUAL_SONIC; break;
}
// motu99: added differentiation for Darkfire and flame weapon
if (bDarkfire)
{
// DeleteLocalInt(oItem, "X2_Wep_Dam_Type_DF");
SetLocalInt(oItem, "X2_Wep_Dam_Type_DF", nDamageType);
// Sets Caster Level int because it was too confusing trying to figure out caster level
// in the damage script.
// DeleteLocalInt(oItem, "X2_Wep_Caster_Lvl");
SetLocalInt(oItem, "X2_Wep_Caster_Lvl_DF", nCasterLevel);
}
else
{
// DeleteLocalInt(oItem, "X2_Wep_Dam_Type");
SetLocalInt(oItem, "X2_Wep_Dam_Type", nDamageType);
// Sets Caster Level int because it was too confusing trying to figure out caster level
// in the damage script.
// DeleteLocalInt(oItem, "X2_Wep_Caster_Lvl");
SetLocalInt(oItem, "X2_Wep_Caster_Lvl", nCasterLevel);
}
// If the spell is cast again, any previous itemproperties matching are removed.
if(!bDarkfire || (bDarkfire && !(GetBaseItemType(oItem) == BASE_ITEM_SHORTSPEAR && GetHasFeat(FEAT_THUNDER_WEAPON, oCaster))))
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(bDarkfire ? 127 : 124, nCasterLevel), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING);
IPSafeAddItemProperty(oItem, ItemPropertyVisualEffect(nAppearanceType), fDuration,X2_IP_ADDPROP_POLICY_REPLACE_EXISTING,FALSE,TRUE);
//DelayCommand(fDuration, DeleteTheInts(oItem));
break;
}
case SPELL_DEAFENING_CLANG:
{
IPSafeAddItemProperty(oItem, ItemPropertyAttackBonus(1), fDuration, X2_IP_ADDPROP_POLICY_KEEP_EXISTING ,TRUE,TRUE);
IPSafeAddItemProperty(oItem, ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_SONIC, IP_CONST_DAMAGEBONUS_3), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING ,FALSE,TRUE);
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(137, 5),fDuration, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, TRUE,FALSE);
IPSafeAddItemProperty(oItem, ItemPropertyVisualEffect(ITEM_VISUAL_SONIC), fDuration,X2_IP_ADDPROP_POLICY_REPLACE_EXISTING,FALSE,TRUE );
SetLocalInt(oItem, "X2_Wep_Caster_Lvl_DC", nCasterLevel);
SetLocalInt(oItem, "X2_Wep_Penetr_DC", nPenetr);
break;
}
case SPELL_HOLY_SWORD:
{
AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonus(2), oItem, fDuration);
AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL, 5), oItem, fDuration);
AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_EVIL, IP_CONST_DAMAGETYPE_DIVINE, IP_CONST_DAMAGEBONUS_2d6), oItem, fDuration);
AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAreaOfEffect(IP_CONST_AOE_CIRCLE_VS_EVIL, nCasterLevel), oItem, fDuration);
break;
}
case SPELL_KEEN_EDGE:
{
IPSafeAddItemProperty(oItem,ItemPropertyKeen(), fDuration, X2_IP_ADDPROP_POLICY_KEEP_EXISTING ,TRUE,TRUE);
break;
}
case SPELL_WEAPON_OF_IMPACT:
{
IPSafeAddItemProperty(oItem,ItemPropertyKeen(), fDuration, X2_IP_ADDPROP_POLICY_KEEP_EXISTING ,TRUE,TRUE);
break;
}
case SPELL_MAGIC_WEAPON:
{
IPSafeAddItemProperty(oItem,ItemPropertyEnhancementBonus(1), fDuration, X2_IP_ADDPROP_POLICY_KEEP_EXISTING ,TRUE,TRUE);
break;
}
case SPELL_GREATER_MAGIC_WEAPON:
{
int nBonus = nCasterLevel / 3;
if(nBonus > 5) nBonus = 5;
IPSafeAddItemProperty(oItem,ItemPropertyEnhancementBonus(nBonus), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING,FALSE,TRUE);
break;
}
case SPELL_UNHOLYSWORD:
{
AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonus(2), oItem, fDuration);
AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonusVsAlign(IP_CONST_ALIGNMENTGROUP_GOOD, 5), oItem, fDuration);
AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonusVsAlign(IP_CONST_ALIGNMENTGROUP_GOOD, IP_CONST_DAMAGETYPE_DIVINE, IP_CONST_DAMAGEBONUS_2d6), oItem, fDuration);
AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAreaOfEffect(IP_CONST_AOE_CIRCLE_VS_GOOD, nCasterLevel), oItem, fDuration);
break;
}
}
}
//Implements the spell impact, put code here
// if called in many places, return TRUE if
// stored charges should be decreased
// eg. touch attack hits
//
// Variables passed may be changed if necessary
int DoSpell(object oCaster, object oTarget, int nCasterLevel, int nEvent)
{
int nSpellID = PRCGetSpellId(oCaster);
int nMetaMagic = PRCGetMetaMagicFeat(oCaster);
int nPenetr = nCasterLevel + SPGetPenetr();
//float fMaxDuration = RoundsToSeconds(nCasterLevel); //modify if necessary
int nDuration = nCasterLevel;
if(CheckMetaMagic(nMetaMagic, METAMAGIC_EXTEND)) nDuration *= 2;
object oMyWeapon = IPGetTargetedOrEquippedMeleeWeapon();
object oPossessor = GetItemPossessor(oMyWeapon);
int bCondition = GetIsObjectValid(oMyWeapon);
if(!bCondition)
{
FloatingTextStrRefOnCreature(83615, oCaster);
return TRUE;
}
int nVis = VFX_IMP_SUPER_HEROISM;
int nDur = VFX_DUR_CESSATE_POSITIVE;
float fDuration = RoundsToSeconds(nDuration);
switch(nSpellID)
{
case SPELL_BLADE_THIRST:
{
bCondition = bCondition && PRCGetSlashingWeapon(oMyWeapon);
break;
}
case SPELL_BLACKSTAFF:
{
nVis = VFX_IMP_EVIL_HELP;
bCondition = bCondition && (GetBaseItemType(oMyWeapon) == BASE_ITEM_QUARTERSTAFF) ||
(GetBaseItemType(oMyWeapon) == BASE_ITEM_MAGICSTAFF) ||
(GetBaseItemType(oMyWeapon) == BASE_ITEM_CRAFTED_STAFF);
break;
}
case SPELL_BLESS_WEAPON:
{
fDuration = TurnsToSeconds(nDuration);
// ---------------- TARGETED ON BOLT -------------------
if(GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_ITEM)
{
// special handling for blessing crossbow bolts that can slay rakshasa's
if (GetBaseItemType(oTarget) == BASE_ITEM_BOLT)
{
SignalEvent(GetItemPossessor(oTarget), EventSpellCastAt(GetItemPossessor(oTarget), nSpellID, FALSE));
IPSafeAddItemProperty(oTarget, ItemPropertyOnHitCastSpell(123,1), fDuration, X2_IP_ADDPROP_POLICY_KEEP_EXISTING );
SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nVis), GetItemPossessor(oTarget));
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(nDur), GetItemPossessor(oTarget), RoundsToSeconds(nDuration));
return TRUE;
}
}
break;
}
case SPELL_DARKFIRE:
{
nVis = VFX_IMP_PULSE_FIRE;
fDuration = HoursToSeconds(nDuration);
break;
}
case SPELL_FLAME_WEAPON:
{
nVis = VFX_IMP_PULSE_FIRE;
fDuration = TurnsToSeconds(nDuration);
break;
}
case SPELL_HOLY_SWORD:
case SPELL_UNHOLYSWORD:
{
nVis = VFX_IMP_GOOD_HELP;
break;
}
case SPELL_KEEN_EDGE:
{
bCondition = bCondition && (PRCGetSlashingWeapon(oMyWeapon) || PRCGetPiercingWeapon(oMyWeapon));
fDuration = TurnsToSeconds(nDuration*10);
break;
}
case SPELL_WEAPON_OF_IMPACT:
{
bCondition = bCondition && PRCGetBludgeoningWeapon(oMyWeapon);
fDuration = TurnsToSeconds(nDuration*10);
break;
}
case SPELL_MAGIC_WEAPON:
{
fDuration = HoursToSeconds(nDuration);
break;
}
case SPELL_GREATER_MAGIC_WEAPON:
{
fDuration = HoursToSeconds(nDuration);
break;
}
case SPELL_SHILLELAGH:
{
int nBaseItemType = GetBaseItemType(oMyWeapon);
if (nBaseItemType == BASE_ITEM_QUARTERSTAFF && !GetIsMagicItem(oMyWeapon))
{
// Execute the code for non-magical quarterstaff here
bCondition = bCondition && TRUE; // Update the condition as needed
fDuration = TurnsToSeconds(nDuration);
}
else if (nBaseItemType == BASE_ITEM_CLUB && !GetIsMagicItem(oMyWeapon))
{
// Execute the code for non-magical club here
bCondition = bCondition && TRUE; // Update the condition as needed
fDuration = TurnsToSeconds(nDuration);
}
else
{
// Invalid weapon type, do nothing or handle the error here
bCondition = bCondition && FALSE; // Update the condition as needed
fDuration = 0.0; // Update the duration as needed
}
break;
}
/* case SPELL_SHILLELAGH:
{
bCondition = bCondition && ((GetBaseItemType(oMyWeapon) == BASE_ITEM_QUARTERSTAFF) ||
(GetBaseItemType(oMyWeapon) == BASE_ITEM_CLUB)) &&
!GetIsMagicItem(oMyWeapon);
fDuration = TurnsToSeconds(nDuration);
break;
} */
}
if(bCondition)
{
SignalEvent(oPossessor, EventSpellCastAt(oPossessor, nSpellID, FALSE));
if (nDuration>0)
{
effect eVis = EffectVisualEffect(nVis);
if(nSpellID == SPELL_DARKFIRE) eVis = EffectLinkEffects(EffectVisualEffect(VFX_IMP_FLAME_M), eVis);
SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPossessor);
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(nDur), oPossessor, fDuration);
ApplyEffectsToWeapon(oMyWeapon, nSpellID, fDuration, oCaster, nCasterLevel, nPenetr);
}
if(nSpellID == SPELL_UNHOLYSWORD)
{
TLVFXPillar(VFX_IMP_GOOD_HELP, GetLocation(PRCGetSpellTargetObject()), 4, 0.0f, 6.0f);
DelayCommand(1.0f, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect( VFX_IMP_SUPER_HEROISM),GetLocation(PRCGetSpellTargetObject())));
}
return TRUE;
}
else
{
if(nSpellID == SPELL_BLACKSTAFF)
FloatingTextStrRefOnCreature(83620, OBJECT_SELF); // not a qstaff
else if((nSpellID == SPELL_BLADE_THIRST) || (nSpellID == SPELL_KEEN_EDGE))
FloatingTextStrRefOnCreature(83621, OBJECT_SELF); // not a slashing weapon
else if(nSpellID == SPELL_WEAPON_OF_IMPACT)
FloatingTextStrRefOnCreature(16789910, OBJECT_SELF); // not a bludgeoning weapon
else if(nSpellID == SPELL_SHILLELAGH)
FloatingTextStrRefOnCreature(16789913, OBJECT_SELF); // not a nonmagical club or quarterstaff
}
return TRUE; //return TRUE if spell charges should be decremented
}
void main()
{
object oCaster = OBJECT_SELF;
int nCasterLevel = PRCGetCasterLevel(oCaster);
if (nCasterLevel > 40) nCasterLevel = 40;
PRCSetSchool(GetSpellSchool(PRCGetSpellId(oCaster)));
if (!X2PreSpellCastCode()) return;
object oTarget = PRCGetSpellTargetObject(oCaster);
int nEvent = GetLocalInt(oCaster, PRC_SPELL_EVENT); //use bitwise & to extract flags
if(!nEvent) //normal cast
{
if(GetLocalInt(oCaster, PRC_SPELL_HOLD) && oCaster == oTarget)
{ //holding the charge, casting spell on self
SetLocalSpellVariables(oCaster, 1); //change 1 to number of charges
return;
}
DoSpell(oCaster, oTarget, nCasterLevel, nEvent);
}
else
{
if(nEvent & PRC_SPELL_EVENT_ATTACK)
{
if(DoSpell(oCaster, oTarget, nCasterLevel, nEvent))
DecrementSpellCharges(oCaster);
}
}
PRCSetSchool();
}