Added default level up package for Warblade. Moment of Prescience should be an instant action. Truespeak should be a class skill for Factotum. Added Armor Enhancement, Lesser infusion. Added Armor Enhancement infusion. Added Armor Enhancement, Greater infusion Added Resistance Item infusion. Added Weapon Augmentation, Personal infusion. Added Weapon Augmentation infusion. Added Weapon Augmentation, Lesser infusion.
213 lines
6.8 KiB
Plaintext
213 lines
6.8 KiB
Plaintext
// inf_resist_item.nss
|
|
|
|
/*
|
|
Resistance Item
|
|
Abjuration
|
|
Level: Artificer 1
|
|
Components: S, M
|
|
Casting Time: 1 round
|
|
Range: Touch
|
|
Target: Item touched
|
|
Duration: 10 min./level
|
|
Saving Throw: None
|
|
Spell Resistance: No
|
|
|
|
A nonmagical item imbued with this infusion grants a + 1 resistance
|
|
bonus on saving throws to a character who wears or wields it. This
|
|
bonus increases by 1 for every four caster levels (to +2 at 4th level,
|
|
+3 at 8th level, +4 at 12th level, and so forth).
|
|
Material Component: Oil mixed with various spices and minerals.
|
|
*/
|
|
|
|
#include "prc_craft_inc"
|
|
#include "inc_dynconv"
|
|
|
|
// Spell constants for subradials
|
|
const int RESISTANCE_ITEM_FORTITUDE = 6001;
|
|
const int RESISTANCE_ITEM_REFLEX = 6002;
|
|
const int RESISTANCE_ITEM_WILL = 6003;
|
|
|
|
// Check if item is magical
|
|
int GetIsItemMagical(object oItem)
|
|
{
|
|
if(!GetIsObjectValid(oItem)) return FALSE;
|
|
|
|
// Skip plot items
|
|
if(GetPlotFlag(oItem)) return TRUE;
|
|
|
|
int bIsMagical = FALSE;
|
|
itemproperty ipCheck = GetFirstItemProperty(oItem);
|
|
while (GetIsItemPropertyValid(ipCheck))
|
|
{
|
|
string sTag = GetItemPropertyTag(ipCheck);
|
|
int nType = GetItemPropertyType(ipCheck);
|
|
|
|
// Check for protected properties
|
|
if(sTag == "Tag_PRC_OnHitKeeper" ||
|
|
sTag == "Quality_Masterwork" ||
|
|
sTag == "Material_Mithral" ||
|
|
sTag == "Material_Adamantine" ||
|
|
sTag == "Material_Darkwood" ||
|
|
sTag == "Material_ColdIron" ||
|
|
sTag == "Material_MundaneCrystal" ||
|
|
sTag == "Material_DeepCrystal" ||
|
|
nType == ITEM_PROPERTY_MATERIAL) // All material properties
|
|
{
|
|
// Protected property - skip, don't set bIsMagical
|
|
}
|
|
else
|
|
{
|
|
// Check for helmet carveout: +1 Concentration only
|
|
if(GetBaseItemType(oItem) == BASE_ITEM_HELMET &&
|
|
GetItemPropertyType(ipCheck) == ITEM_PROPERTY_SKILL_BONUS &&
|
|
GetItemPropertySubType(ipCheck) == SKILL_CONCENTRATION &&
|
|
GetItemPropertyCostTableValue(ipCheck) == 1)
|
|
{
|
|
// This is a +1 Concentration helmet with no other properties, allow it
|
|
bIsMagical = FALSE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
bIsMagical = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
ipCheck = GetNextItemProperty(oItem);
|
|
}
|
|
|
|
return bIsMagical;
|
|
}
|
|
|
|
// Find first non-magical item on creature
|
|
object GetFirstNonMagicalItem(object oCreature)
|
|
{
|
|
// Check equipped items first
|
|
int nSlot;
|
|
for(nSlot = 0; nSlot < NUM_INVENTORY_SLOTS; nSlot++)
|
|
{
|
|
object oItem = GetItemInSlot(nSlot, oCreature);
|
|
if(GetIsObjectValid(oItem) && !GetIsItemMagical(oItem))
|
|
return oItem;
|
|
}
|
|
|
|
// Check inventory
|
|
object oItem = GetFirstItemInInventory(oCreature);
|
|
while(GetIsObjectValid(oItem))
|
|
{
|
|
if(!GetIsItemMagical(oItem))
|
|
return oItem;
|
|
oItem = GetNextItemInInventory(oCreature);
|
|
}
|
|
|
|
return OBJECT_INVALID;
|
|
}
|
|
|
|
void main()
|
|
{
|
|
PRCSetSchool(SPELL_SCHOOL_ABJURATION);
|
|
if (!X2PreSpellCastCode()) return;
|
|
|
|
object oCaster = OBJECT_SELF;
|
|
//object oTarget = PRCGetSpellTargetObject();
|
|
object oTarget = OBJECT_SELF; //:: for testing
|
|
|
|
int nCasterLevel = PRCGetCasterLevel(oCaster);
|
|
//int nSpellID = PRCGetSpellId();
|
|
int nSpellID = RESISTANCE_ITEM_FORTITUDE;
|
|
|
|
// Calculate resistance bonus: +1 +1 per 4 caster levels
|
|
int nBonus = 1 + (nCasterLevel / 4);
|
|
|
|
// Calculate duration: 10 minutes per level
|
|
float fDuration = TurnsToSeconds(nCasterLevel * 10);
|
|
int nMetaMagic = PRCGetMetaMagicFeat();
|
|
if(nMetaMagic & METAMAGIC_EXTEND)
|
|
fDuration *= 2;
|
|
|
|
// Add fallback for very small durations
|
|
if (fDuration <= 1.0) fDuration = 30.0f; // 30 seconds is fine for testing
|
|
|
|
// Determine save type based on infusion spellID
|
|
int nSaveType;
|
|
string sSaveName;
|
|
|
|
switch(nSpellID)
|
|
{
|
|
case RESISTANCE_ITEM_FORTITUDE:
|
|
nSaveType = SAVING_THROW_FORT;
|
|
sSaveName = "Fortitude";
|
|
break;
|
|
case RESISTANCE_ITEM_REFLEX:
|
|
nSaveType = SAVING_THROW_REFLEX;
|
|
sSaveName = "Reflex";
|
|
break;
|
|
case RESISTANCE_ITEM_WILL:
|
|
nSaveType = SAVING_THROW_WILL;
|
|
sSaveName = "Will";
|
|
break;
|
|
default:
|
|
FloatingTextStringOnCreature("Invalid resistance spell type.", oCaster, FALSE);
|
|
return;
|
|
}
|
|
|
|
object oItem;
|
|
|
|
// Handle targeting
|
|
if(GetObjectType(oTarget) == OBJECT_TYPE_ITEM)
|
|
{
|
|
oItem = oTarget;
|
|
}
|
|
else if(GetObjectType(oTarget) == OBJECT_TYPE_CREATURE)
|
|
{
|
|
oItem = GetFirstNonMagicalItem(oTarget);
|
|
if(!GetIsObjectValid(oItem))
|
|
{
|
|
FloatingTextStringOnCreature(GetName(oTarget) + " has no non-magical items to infuse.", oCaster, FALSE);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FloatingTextStringOnCreature("Invalid target. Must target an item or creature.", oCaster, FALSE);
|
|
return;
|
|
}
|
|
|
|
// Validate item
|
|
if(!GetIsObjectValid(oItem))
|
|
{
|
|
FloatingTextStringOnCreature("Invalid item target.", oCaster, FALSE);
|
|
return;
|
|
}
|
|
|
|
if(GetPlotFlag(oItem))
|
|
{
|
|
FloatingTextStringOnCreature("Cannot infuse plot items.", oCaster, FALSE);
|
|
return;
|
|
}
|
|
|
|
if(GetIsItemMagical(oItem))
|
|
{
|
|
FloatingTextStringOnCreature(GetName(oItem) + " is already magical. Can only infuse non-magical items.", oCaster, FALSE);
|
|
return;
|
|
}
|
|
|
|
// Check for existing resistance infusion to prevent stacking
|
|
itemproperty ipExisting = GetFirstItemProperty(oItem);
|
|
while(GetIsItemPropertyValid(ipExisting))
|
|
{
|
|
if(GetItemPropertyTag(ipExisting) == "ResistanceInfusion")
|
|
{
|
|
FloatingTextStringOnCreature(GetName(oItem) + " already has a resistance infusion.", oCaster, FALSE);
|
|
return;
|
|
}
|
|
ipExisting = GetNextItemProperty(oItem);
|
|
}
|
|
|
|
// Create and apply the resistance bonus property
|
|
itemproperty ip = ItemPropertyBonusSavingThrow(nSaveType, nBonus);
|
|
ip = TagItemProperty(ip, "ResistanceInfusion");
|
|
IPSafeAddItemProperty(oItem, ip, fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE);
|
|
|
|
FloatingTextStringOnCreature(sSaveName + " resistance +" + IntToString(nBonus) + " applied to " + GetName(oItem) + ".", oCaster, FALSE);
|
|
} |