//::///////////////////////////////////////////////
//:: [Item Property Function]
//:: [inc_item_props.nss]
//:://////////////////////////////////////////////
/** @file
    This file defines several functions used to
    manipulate item properties.  In particular,
    It defines functions used in the prc_* files
    to apply passive feat bonuses.

    Take special note of SetCompositeBonus.  This
    function is crucial for allowing bonuses of the
    same type from different PRCs to stack.
*/
//:://////////////////////////////////////////////
//:: Created By: Aaon Graywolf
//:: Created On: Dec 19, 2003
//:://////////////////////////////////////////////
//:: Update: Jan 4 2002
//::    - Extended Composite bonus function to handle pretty much
//::      every property that can possibly be composited.

//////////////////////////////
// Function Prototypes      //
//////////////////////////////

#include "prc_feat_const"
#include "prc_misc_const"

//:: Test void
//:: void main (){}

/**
 * Checks oItem for all properties matching iType and iSubType. Removes all
 * these properties and returns their total CostTableVal.
 * This function should only be used for Item Properties that have simple
 * integer CostTableVals, such as AC, Save/Skill Bonuses, etc.
 *
 * @param oItem    The item on which to look for the itemproperties. Usually a
 *                 skin object.
 * @param iType    ITEM_PROPERTY_* constant of the itemproperty to look for.
 * @param iSubType IP_CONST_* constant of itemproperty SubType if applicable.
 * @return         The total CostTableValue of the itemproperties found
 *                 matching iType and iSubType.
 */
int TotalAndRemoveProperty(object oItem, int iType, int iSubType = -1);

/**
 * Used to roll bonuses from multiple sources into a single property.
 * Only supports properties which have simple integer CostTableVals.
 * See the code for a list of supported types.  Some important properties
 * that CANNOT be composited are SpellResistance, DamageBonus, DamageReduction
 * DamageResistance and MassiveCritical, as these use 2da-referencing constants
 * instead of integers for CostTableVals.
 *
 * <DEPRECATED>
 * LocalInts from SetCompositeBonus() when applied to the skin need to be
 * added to DeletePRCLocalInts() in prc_inc_function in order for the system to
 * properly clear the properties when the itemproperties are removed using
 * ScrubPCSkin().
 * When applied to equipment, the LocalInts need to be added to
 * DeletePRCLocalIntsT() in inc_item_props.
 * </DEPRECATED>
 *
 * Use SetCompositeBonus() for the skin, SetCompositeBonusT() for other equipment.
 *
 *
 * @param oPC      Object wearing / using the item
 * @param oItem    Object to apply bonus to
 * @param sBonus   String name of the source for this bonus
 * @param iVal     Integer value to set this bonus to
 * @param iType    ITEM_PROPERTY_* constant of itemproperty to apply
 * @param iSubType IP_CONST_* constant of itemproperty SubType if applicable
 */
void SetCompositeBonus(object oItem, string sBonus, int iVal, int iType, int iSubType = -1);

/**
 * See SetCompositeBonus(). This is an equivalent, except it applies the itemproperties as
 * temporary ones with duration of 9999 seconds.
 */
void SetCompositeBonusT(object oItem, string sBonus, int iVal, int iType, int iSubType = -1);

/**
 * Calculates the total Bonus AC granted by itemproperties on an item.
 *
 * @param oItem The item to calculate AC bonus given by.
 * @return      The total of all AC bonus itemproperties on oItem.
 */
int GetACBonus(object oItem);

/**
 * Calculates the Base AC (i.e. AC without bonuses) of an item
 *
 * @param oItem The item to calculate base AC for.
 * @return      The base AC, as calculated by removing the value returned by
 *              GetACBonus() on the item from the value returned by GetItemACValue().
 */
int GetBaseAC(object oItem);

/**
 * Gets the itemproperty number of a specific SR value
 */
int GetSRByValue(int nValue);

/**
 * Returns the opposite element from iElem or -1 if iElem is not valid
 * Can be useful for determining elemental strengths and weaknesses
 *
 * @param iElem IP_CONST_DAMAGETYPE_* constant.
 * @return      IP_CONST_DAMAGETYPE_* constant of the opposing damage type.
 */
int GetOppositeElement(int iElem);

/**
 * Used to find the damage type done by any given weapon using 2da lookups.
 * Only usable on weapon items.
 *
 * @param oWeapon The weapon whose damage type to examine.
 * @return        One of IP_CONST_DAMAGETYPE_BLUDGEONING,
 *                       IP_CONST_DAMAGETYPE_PIERCING,
 *                       IP_CONST_DAMAGETYPE_SLASHING
 *                if used on a weapon item. Otherwise -1.
 */
int GetItemPropertyDamageType(object oWeapon);

/**
 * Used to find the damage type done by any given weapon using 2da lookups.
 * Only usable on weapon items.
 *
 * @param oWeapon The weapon whose damage type to examine.
 * @return        One of DAMAGE_TYPE_BLUDGEONING,
 *                       DAMAGE_TYPE_PIERCING,
 *                       DAMAGE_TYPE_SLASHING
 *                if used on a weapon item. Otherwise -1.
 */
int GetItemDamageType(object oWeapon);

/**
 * To ensure a damage bonus stacks with any existing enhancement bonus,
 * create a temporary damage bonus on the weapon.  You do not want to do this
 * if the weapon is of the "slashing and piercing" type, because the
 * enhancement bonus is considered "physical", not "slashing" or "piercing".
 *
 * Because of this strange Bioware behavior, you'll want to only call this code as such:
 *
 * if (StringToInt(Get2DACache("baseitems","WeaponType",GetBaseItemType(oWeapon))) != 4)
 * {
 *     IPEnhancementBonusToDamageBonus(oWeapon);
 * }
 *
 *
 * @param oWeap The weapon to perform the operation on.
 */
void IPEnhancementBonusToDamageBonus(object oWeap);

/**
 * Used to roll bonuses from multiple sources into a single property
 * Only supports damage bonuses in a linear fashion - +1 through +20.
 *
 * Note: If you do not define iSubType, the damage applied will most likely not
 * stack with any enhancement bonus.  See IPEnhancementBonusToDamageBonus() above.
 *
 * <DEPRECATED>
 * LocalInts from SetCompositeDamageBonus() need to be added to
 * DeletePRCLocalInts() in prc_inc_function.
 * LocalInts from SetCompositeDamageBonusT() need to be added to
 * DeletePRCLocalIntsT() in inc_item_props.
 * </DEPRECATED>
 *
 *
 * @param oItem    Object to apply bonus to
 * @param sBonus   String name of the source for this bonus
 * @param iVal     Integer value to set this bonus to (damage +1 through +20)
 * @param iSubType IP_CONST_DAMAGETYPE* constant -- leave blank to use the weapon's damage type.
 */
void SetCompositeDamageBonusT(object oItem, string sBonus, int iVal, int iSubType = -1); // for temporary bonuses

/**
 * Removes a number of itemproperties matching the parameters.
 *
 * @param oItem     The item to remove itemproperties from.
 * @param iType     ITEM_PROPERTY_* constant.
 * @param iSubType  IP_CONST_* constant of the itemproperty subtype or -1 to
 *                  match all possible subtypes. Also use -1 if the itemproperty
 *                  has no subtypes.
 * @param iCostVal  CostTableValue of the itemproperty to remove. Again, -1 for
 *                  any.
 * @param iNum      How many matching itemproperties to remove. -1 for all. Defaults
 *                  to 1.
 * @param sFlag     Name of a local integer on the item to set to 0 when this is run.
 *                  If anyone knows why the fuck this is done, please write here - Ornedan
 * @param iParam1   Param1 value of the itemproperty to remove. Again, -1 for any.
 * @param iDuration DURATION_TYPE_* constant. The duration type of the itemproperty.
 *                  Again, -1 for any.
 */
void RemoveSpecificProperty(object oItem, int iType, int iSubType = -1, int iCostVal = -1, int iNum = 1,
                            string sFlag = "", int iParam1 = -1, int iDuration = DURATION_TYPE_PERMANENT);

/**
 * Finds the first itemproperty matching the parameters.
 *   Use GetIsItemPropertyValid() to check if an itemproperty exists.
 *
 * @param oItem     The item to remove itemproperties from.
 * @param iType     ITEM_PROPERTY_* constant.
 * @param iSubType  IP_CONST_* constant of the itemproperty subtype or -1 to
 *                  match all possible subtypes. Also use -1 if the itemproperty
 *                  has no subtypes.
 * @param iCostVal  CostTableValue of the itemproperty to remove. Again, -1 for
 *                  any.
 * @param iParam1   Param1 value of the itemproperty to remove. Again, -1 for any.
 * @param iDuration DURATION_TYPE_* constant. The duration type of the itemproperty.
 *                  Again, -1 for any.
 */
itemproperty GetSpecificProperty(object oItem, int iType, int iSubType = -1, int iCostVal = -1,
                            int iParam1 = -1, int iDuration = DURATION_TYPE_PERMANENT);

/**
 * Keeps track of Attack Bonus effects and stacks them appropriately... you cannot set up
 * "special" attack bonuses against races or alignments, but it will keep seperate tabs on
 * on-hand attack bonuses and off-hand attack bonuses.
 *
 * NOTE: This attack bonus is an effect on the creature, not an item property.  Item Property
 * attacks have the downside that they pierce DR, whereas effects do not.
 *
 * NOTE: DO *NOT* USE THIS FUNCTION WITH SPELL/SLA EFFECTS.  They stack fine on their own.
 *
 * <DEPRECATED>
 * LocalInts in and finally SetCompositeAttackBonus() need to be added to
 * DeletePRCLocalInts() in prc_inc_function.
 * </DEPRECATED>
 *
 *
 * @param oPC      PC/NPC you wish to apply an attack bonus effect to
 * @param sBonus   The unique name you wish to give this attack bonus
 * @param iVal     The amount the attack bonus should be (there is a hardcoded limit of 20)
 * @param iSubType ATTACK_BONUS_MISC applies to both hands, ATTACK_BONUS_ONHAND applies to the right (main)
 *                 hand, and ATTACK_BONUS_OFFHAND applies to the left (off) hand
 */
void SetCompositeAttackBonus(object oPC, string sBonus, int iVal, int iSubType = ATTACK_BONUS_MISC);

/**
 * Internal function.
 * Handles maintaining a list of composite itemproperty names for
 * use when clearing the itemproperties away.
 *
 * @param oItem      The item a composite bonus is being set on.
 * @param sBase      The base name for the local variables used. For differentiating between
 *                   permanent and temporary lists.
 * @param sComposite The name of a composite property being set.
 */
void UpdateUsedCompositeNamesList(object oItem, string sBase, string sComposite);

/**
 * Internal function.
 * Deletes all the composite itemproperty names listed and deletes the list.
 *
 * @param oItem The item being cleaned of composite properties.
 * @param sBase The base name for the local variables used. For differentiating between
 *              permanent and temporary lists.
 */
void DeleteNamedComposites(object oItem, string sBase);

/**
 * Determines if any of the given item's itemproperties would make it a
 * magical item.
 *
 * @param oItem The item to test
 * @return      TRUE if the item is a magical item, FALSE otherwise
 */
int GetIsMagicItem(object oItem);

/**
 * Special function for adding ip feats to creatures hide.
 * The hide is always equipped so it easier to use GetHasFeat()
 * than to loop over all ips to check if given skin was already
 * added.
 * @param nFeat  The ID of real feat we want to add (row number in feat.2da)
 * @param IPFeat The ID of ip feat (row number in iprp_feats.2da)
 * @param oSkin  Target item to which we add the feat - should be an equipped
 *               creature skin (the script doesn't check it it's valid)
 * @param oPC    Owner of oSkin
 */
void AddSkinFeat(int nFeat, int IPFeat, object oSkin, object oPC = OBJECT_SELF, float fDuration = 0.0f);

//////////////////////////////////////////////////
/* Include section                              */
//////////////////////////////////////////////////

#include "inc_2dacache"
#include "inc_persist_loca"
#include "inc_prc_npc"
//#include "inc_utility"
#include "prc_ipfeat_const"
#include "inc_array"

//////////////////////////////
// Function Definitions     //
//////////////////////////////

int TotalAndRemoveProperty(object oItem, int iType, int iSubType = -1)
{
    itemproperty ip = GetFirstItemProperty(oItem);
    int total = 0;

    while(GetIsItemPropertyValid(ip)){
        if(GetItemPropertyType(ip) == iType && (GetItemPropertySubType(ip) == iSubType || iSubType == -1)){
            total += GetItemPropertyCostTableValue(ip);
            RemoveItemProperty(oItem, ip);
        }
        ip = GetNextItemProperty(oItem);
    }
    return total;
}

itemproperty GetSpecificProperty(object oItem, int iType, int iSubType = -1, int iCostVal = -1,
                            int iParam1 = -1, int iDuration = DURATION_TYPE_PERMANENT)
{
    itemproperty ip = GetFirstItemProperty(oItem);
    while(GetIsItemPropertyValid(ip)){
        if(GetItemPropertyType(ip)            == iType                          &&
           (GetItemPropertyDurationType(ip)   == iDuration || iDuration == -1)  &&
           (GetItemPropertySubType(ip)        == iSubType  || iSubType  == -1)  &&
           (GetItemPropertyCostTableValue(ip) == iCostVal  || iCostVal  == -1)  &&
           (GetItemPropertyParam1Value(ip)    == iParam1   || iParam1   == -1)
          )
        {
            return ip;
        }
        ip = GetNextItemProperty(oItem);
    }
    return ip;
}

void RemoveSpecificProperty(object oItem, int iType, int iSubType = -1, int iCostVal = -1, int iNum = 1,
                            string sFlag = "", int iParam1 = -1, int iDuration = DURATION_TYPE_PERMANENT)
{
    int iRemoved = 0;
    itemproperty ip = GetFirstItemProperty(oItem);
    while(GetIsItemPropertyValid(ip) && (iRemoved < iNum || iNum == -1)){
        if(GetItemPropertyType(ip)            == iType                          &&
           (GetItemPropertyDurationType(ip)   == iDuration || iDuration == -1)  &&
           (GetItemPropertySubType(ip)        == iSubType  || iSubType  == -1)  &&
           (GetItemPropertyCostTableValue(ip) == iCostVal  || iCostVal  == -1)  &&
           (GetItemPropertyParam1Value(ip)    == iParam1   || iParam1   == -1)
          )
        {
            RemoveItemProperty(oItem, ip);
            iRemoved++;
        }
        ip = GetNextItemProperty(oItem);
    }
    SetLocalInt(oItem, sFlag, 0);
}

//SetCompositeBonus is a special case that doesn't need to use DelayAddItemProperty because
//the property that is added is always different from the one that was deleted by TotalAndRemoveProperty.
void SetCompositeBonus(object oItem, string sBonus, int iVal, int iType, int iSubType = -1)
{
    int iOldVal = GetLocalInt(oItem, sBonus);
    int iChange = iVal - iOldVal;
    int iCurVal = 0;

    if(iChange == 0) return;

    // Store the bonus name for use during cleanup
    UpdateUsedCompositeNamesList(oItem, "PRC_CBon", sBonus);

    //Moved TotalAndRemoveProperty into switch to prevent
    //accidental deletion of unsupported property types
    switch(iType)
    {
        case ITEM_PROPERTY_ABILITY_BONUS:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            iCurVal -= GetPersistantLocalInt(GetItemPossessor(oItem), "LetoAbility_"+IntToString(iSubType));
            if (DEBUG) DoDebug("Ability Decrease #1: oItem "+GetName(oItem)+" sBonus "+sBonus+" iChange "+IntToString(iChange)+" iCurVal "+IntToString(iCurVal)+" iSubType "+IntToString(iSubType));
            /*if ((iCurVal + iChange) > 12)
            {
                iVal -= iCurVal + iChange - 12;
                iCurVal = 12;
                iChange = 0;
            }*/
            if(iCurVal+iChange > 0)
            {
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAbilityBonus(iSubType, iCurVal + iChange), oItem);
                if (DEBUG) DoDebug("Ability Increase");
            }    
            else if(iCurVal+iChange < 0)
            {
            	if (DEBUG) DoDebug("Ability Decrease #2: oItem "+GetName(oItem)+" sBonus "+sBonus+" iChange "+IntToString(iChange)+" iSubType "+IntToString(iSubType));
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseAbility(iSubType, -1*(iCurVal + iChange)), oItem);
            }    
            break;
        case ITEM_PROPERTY_AC_BONUS:
            iCurVal = TotalAndRemoveProperty(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonus(iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonusVsAlign(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonusVsDmgType(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonusVsRace(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyACBonusVsSAlign(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_ATTACK_BONUS:
            iCurVal = TotalAndRemoveProperty(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonus(iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonusVsAlign(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonusVsRace(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackBonusVsSAlign(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDamageBonusVsRace(iSubType, DAMAGE_TYPE_SLASHING, iCurVal + iChange), oItem);
            break;            
        case ITEM_PROPERTY_DECREASED_ABILITY_SCORE:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 10)
            {
                iVal -= iCurVal + iChange - 10;
                iCurVal = 10;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseAbility(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_DECREASED_AC:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 5)
            {
                iVal -= iCurVal + iChange - 5;
                iCurVal = 5;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseAC(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER:
            iCurVal = TotalAndRemoveProperty(oItem, iType);
            if ((iCurVal + iChange)  > 5)
            {
                iVal -= iCurVal + iChange - 5;
                iCurVal = 5;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyAttackPenalty(iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER:
            iCurVal = TotalAndRemoveProperty(oItem, iType);
            if ((iCurVal + iChange)  > 5)
            {
                iVal -= iCurVal + iChange - 5;
                iCurVal = 5;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyEnhancementPenalty(iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_DECREASED_SAVING_THROWS:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyReducedSavingThrow(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyReducedSavingThrowVsX(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_DECREASED_SKILL_MODIFIER:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 10)
            {
                iVal -= iCurVal + iChange - 10;
                iCurVal = 10;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDecreaseSkill(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_ENHANCEMENT_BONUS:
            iCurVal = TotalAndRemoveProperty(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyEnhancementBonus(iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyEnhancementBonusVsAlign(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP:
            iCurVal = TotalAndRemoveProperty(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyEnhancementBonusVsRace(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyEnhancementBonusVsSAlign(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_MIGHTY:
            iCurVal = TotalAndRemoveProperty(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyMaxRangeStrengthMod(iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_REGENERATION:
            iCurVal = TotalAndRemoveProperty(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyRegeneration(iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_REGENERATION_VAMPIRIC:
            iCurVal = TotalAndRemoveProperty(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyVampiricRegeneration(iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_SAVING_THROW_BONUS:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusSavingThrowVsX(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusSavingThrow(iSubType, iCurVal + iChange), oItem);
            break;
        case ITEM_PROPERTY_SKILL_BONUS:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 50)
            {
                iVal -= iCurVal + iChange - 50;
                iCurVal = 50;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertySkillBonus(iSubType, iCurVal + iChange), oItem);
            break;
    }
    SetLocalInt(oItem, sBonus, iVal);
}

int GetACBonus(object oItem)
{
    if(!GetIsObjectValid(oItem)) return 0;

    itemproperty ip = GetFirstItemProperty(oItem);
    int iTotal = 0;

    while(GetIsItemPropertyValid(ip)){
        if(GetItemPropertyType(ip) == ITEM_PROPERTY_AC_BONUS)
            iTotal += GetItemPropertyCostTableValue(ip);
        ip = GetNextItemProperty(oItem);
    }
    return iTotal;
}

int GetBaseAC(object oItem){ return GetItemACValue(oItem) - GetACBonus(oItem); }

int GetOppositeElement(int iElem)
{
    switch(iElem){
        case IP_CONST_DAMAGETYPE_ACID:
            return DAMAGE_TYPE_ELECTRICAL;
        case IP_CONST_DAMAGETYPE_COLD:
            return IP_CONST_DAMAGETYPE_FIRE;
        case IP_CONST_DAMAGETYPE_DIVINE:
            return IP_CONST_DAMAGETYPE_NEGATIVE;
        case IP_CONST_DAMAGETYPE_ELECTRICAL:
            return IP_CONST_DAMAGETYPE_ACID;
        case IP_CONST_DAMAGETYPE_FIRE:
            return IP_CONST_DAMAGETYPE_COLD;
        case IP_CONST_DAMAGETYPE_NEGATIVE:
            return IP_CONST_DAMAGETYPE_POSITIVE;
     }
     return -1;
}

int TotalAndRemovePropertyT(object oItem, int iType, int iSubType = -1)
{
    itemproperty ip = GetFirstItemProperty(oItem);
    int total = 0;
    int iTemp;

    while(GetIsItemPropertyValid(ip)){
        if(GetItemPropertyType(ip) == iType && (GetItemPropertySubType(ip) == iSubType || iSubType == -1)){
             iTemp = GetItemPropertyCostTableValue(ip);
            total = iTemp > total ? iTemp : total;
            if (GetItemPropertyDurationType(ip)== DURATION_TYPE_TEMPORARY) RemoveItemProperty(oItem, ip);
        }
        ip = GetNextItemProperty(oItem);
    }
    return total;
}

//SetCompositeBonusT is a special case that doesn't need to use DelayAddItemProperty because
//the property that is added is always different from the one that was deleted by TotalAndRemovePropertyT.
void SetCompositeBonusT(object oItem, string sBonus, int iVal, int iType, int iSubType = -1)
{
    int iOldVal = GetLocalInt(oItem, sBonus);
    if (GetLocalInt(GetItemPossessor(oItem),"ONREST")) iOldVal =0;
    int iChange = iVal - iOldVal;
    int iCurVal = 0;

    if(iChange == 0) return;

    // Store the bonus name for use during cleanup
    UpdateUsedCompositeNamesList(oItem, "PRC_CBonT", sBonus);

    //Moved TotalAndRemoveProperty into switch to prevent
    //accidental deletion of unsupported property types
    switch(iType)
    {
        case ITEM_PROPERTY_ABILITY_BONUS:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 12)
            {
                iVal -= iCurVal + iChange - 12;
                iCurVal = 12;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAbilityBonus(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_AC_BONUS:
            iCurVal = TotalAndRemovePropertyT(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyACBonus(iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyACBonusVsAlign(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyACBonusVsDmgType(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyACBonusVsRace(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyACBonusVsSAlign(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_ATTACK_BONUS:
            iCurVal = TotalAndRemovePropertyT(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackBonus(iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackBonusVsAlign(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackBonusVsRace(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackBonusVsSAlign(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP:
            iCurVal = TotalAndRemoveProperty(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            if(iCurVal+iChange > 0)
                AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyDamageBonusVsRace(iSubType,  DAMAGE_TYPE_SLASHING, iCurVal + iChange), oItem);
            break;              
        case ITEM_PROPERTY_DECREASED_ABILITY_SCORE:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDecreaseAbility(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_DECREASED_AC:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 5)
            {
                iVal -= iCurVal + iChange - 5;
                iCurVal = 5;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDecreaseAC(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER:
            iCurVal = TotalAndRemovePropertyT(oItem, iType);
            if ((iCurVal + iChange)  > 5)
            {
                iVal -= iCurVal + iChange - 5;
                iCurVal = 5;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyAttackPenalty(iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER:
            iCurVal = TotalAndRemovePropertyT(oItem, iType);
            if ((iCurVal + iChange)  > 5)
            {
                iVal -= iCurVal + iChange - 5;
                iCurVal = 5;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementPenalty(iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_DECREASED_SAVING_THROWS:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyReducedSavingThrowVsX(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyReducedSavingThrow(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_DECREASED_SKILL_MODIFIER:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 10)
            {
                iVal -= iCurVal + iChange - 10;
                iCurVal = 10;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDecreaseSkill(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_ENHANCEMENT_BONUS:
            iCurVal = TotalAndRemovePropertyT(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonus(iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonusVsAlign(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP:
            iCurVal = TotalAndRemovePropertyT(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonusVsRace(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyEnhancementBonusVsSAlign(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_MIGHTY:
            iCurVal = TotalAndRemovePropertyT(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyMaxRangeStrengthMod(iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_REGENERATION:
            iCurVal = TotalAndRemovePropertyT(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyRegeneration(iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_REGENERATION_VAMPIRIC:
            iCurVal = TotalAndRemovePropertyT(oItem, iType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyVampiricRegeneration(iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_SAVING_THROW_BONUS:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyBonusSavingThrowVsX(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 20)
            {
                iVal -= iCurVal + iChange - 20;
                iCurVal = 20;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyBonusSavingThrow(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
        case ITEM_PROPERTY_SKILL_BONUS:
            iCurVal = TotalAndRemovePropertyT(oItem, iType, iSubType);
            if ((iCurVal + iChange)  > 50)
            {
                iVal -= iCurVal + iChange - 50;
                iCurVal = 50;
                iChange = 0;
            }
            AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertySkillBonus(iSubType, iCurVal + iChange), oItem,9999.0);
            break;
    }
    SetLocalInt(oItem, sBonus, iVal);
}

int GetItemPropertyDamageType(object oWeapon)
{
    if(GetObjectType(oWeapon) != OBJECT_TYPE_ITEM)
        return -1;

    int iWeaponType = GetBaseItemType(oWeapon);
    int iDamageType = StringToInt(Get2DACache("baseitems","WeaponType",iWeaponType));
    switch(iDamageType)
    {
        case 1: return IP_CONST_DAMAGETYPE_PIERCING;    break;
        case 2: return IP_CONST_DAMAGETYPE_BLUDGEONING; break;
        case 3: return IP_CONST_DAMAGETYPE_SLASHING;    break;
        case 4: return IP_CONST_DAMAGETYPE_SLASHING;    break; // slashing & piercing... slashing bonus.

        default: return -1;
    }
    return -1;
}

int GetItemDamageType(object oWeapon)
{
    if(GetObjectType(oWeapon) != OBJECT_TYPE_ITEM)
        return -1;

    int iWeaponType = GetBaseItemType(oWeapon);
    int iDamageType = StringToInt( Get2DACache("baseitems","WeaponType",iWeaponType) );
    switch(iDamageType)
    {
        case 1: return DAMAGE_TYPE_PIERCING;    break;
        case 2: return DAMAGE_TYPE_BLUDGEONING; break;
        case 3: return DAMAGE_TYPE_SLASHING;    break;
        case 4: return DAMAGE_TYPE_SLASHING;    break; // slashing & piercing... slashing bonus.

        default: return -1;
    }
    return -1;
}

// To ensure the damage bonus stacks with any existing enhancement bonus,
// we create a temporary damage bonus on the weapon.  We do not want to do this
// if the weapon is of the "slashing and piercing" type, because the
// enhancement bonus is considered "physical", not "slashing" or "piercing".
// If you borrow this code, make sure to keep the "IPEnh" and realize that
// "slashing and piercing" weapons need a special case.
void IPEnhancementBonusToDamageBonus(object oWeap)
{
    int iBonus = 0;
    int iTemp;

    if (GetLocalInt(oWeap, "IPEnh") || !GetIsObjectValid(oWeap)) return;

    itemproperty ip = GetFirstItemProperty(oWeap);
    while(GetIsItemPropertyValid(ip))
    {
        if(GetItemPropertyType(ip) == ITEM_PROPERTY_ENHANCEMENT_BONUS)
            iTemp = GetItemPropertyCostTableValue(ip);
            iBonus = iTemp > iBonus ? iTemp : iBonus;
        ip = GetNextItemProperty(oWeap);
    }

    SetCompositeDamageBonusT(oWeap,"IPEnh",iBonus);
}

int TotalAndRemoveDamagePropertyT(object oItem, int iSubType)
{
    itemproperty ip = GetFirstItemProperty(oItem);
    int iPropertyValue;
    int total = 0;
    int iTemp;

    while(GetIsItemPropertyValid(ip))
    {
        iPropertyValue = GetItemPropertyCostTableValue(ip);

        if((GetItemPropertyType(ip) == ITEM_PROPERTY_DAMAGE_BONUS) &&
           (GetItemPropertySubType(ip) == iSubType) &&
           ((iPropertyValue < 6) || (iPropertyValue > 15)))
        {
            total = iPropertyValue > total ? iPropertyValue : total;
            if (GetItemPropertyDurationType(ip)== DURATION_TYPE_TEMPORARY) RemoveItemProperty(oItem, ip);
       }
        ip = GetNextItemProperty(oItem);

    }
    return total;
}

void SetCompositeDamageBonusT(object oItem, string sBonus, int iVal, int iSubType = -1)
{
    int iOldVal = GetLocalInt(oItem, sBonus);
    int iChange = iVal - iOldVal;
    int iLinearDamage = 0;
    int iCurVal = 0;

    if(iChange == 0) return;

    // Store the bonus name for use during cleanup
    UpdateUsedCompositeNamesList(oItem, "PRC_CBonT", sBonus);

    if (iSubType == -1) iSubType = GetItemPropertyDamageType(oItem);
    if (iSubType == -1) return;  // if it's still -1 we're not dealing with a weapon.

    iCurVal = TotalAndRemoveDamagePropertyT(oItem, iSubType);

    if (iCurVal > 15) iCurVal -= 10; // values 6-20 are in the 2da as lines 16-30
    iLinearDamage = iCurVal + iChange;
    if (iLinearDamage > 20)
    {
        iVal = iLinearDamage - 20; // Change the stored value to reflect the fact that we overflowed
        iLinearDamage = 20; // This is prior to adjustment due to non-linear values
    }
    if (iLinearDamage > 5) iLinearDamage += 10; // values 6-20 are in the 2da as lines 16-30
    AddItemProperty(DURATION_TYPE_TEMPORARY, ItemPropertyDamageBonus(iSubType, iLinearDamage), oItem,9999.0);

    SetLocalInt(oItem, sBonus, iVal);
}

void TotalRemovePropertyT(object oItem)
{
    itemproperty ip = GetFirstItemProperty(oItem);
    while(GetIsItemPropertyValid(ip))
    {
        if(GetItemPropertyDurationType(ip)== DURATION_TYPE_TEMPORARY)
            RemoveItemProperty(oItem, ip);
        ip = GetNextItemProperty(oItem);
    }
}

void DeletePRCLocalIntsT(object oPC, object oItem = OBJECT_INVALID)
{
    // See if we were given a valid item as parameter. If we were, we
    // will be removing ints from it.
    // Otherwise, we will take the item in each slot and removing the
    // ints that should be on it.
    int bGivenObject = GetIsObjectValid(oItem);

    // RIGHT HAND
    if(!bGivenObject)
        oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oPC);

    if(bGivenObject || GetIsObjectValid(oItem))
    {
        // Clear composite bonuses
        TotalRemovePropertyT(oItem);
        DeleteNamedComposites(oItem, "PRC_CBonT");

        /* Clear other stuff
         *
         * All the commented out values are the ones that didn't seem to be used anywhere anymore - Ornedan
         */
        //Stormlord
        DeleteLocalInt(oItem,"STShock"); /// @todo Rewrite the Stormlord not to directly manipulate values
        DeleteLocalInt(oItem,"STThund");
        DeleteLocalInt(oItem,"ManArmsCore");
        //Vile/Sanctify & Un/Holy Martial Strike
        DeleteLocalInt(oItem,"SanctMar");
        DeleteLocalInt(oItem,"MartialStrik");
        DeleteLocalInt(oItem,"UnholyStrik");
        DeleteLocalInt(oItem,"USanctMar");
        //Duelist Precise Strike
        DeleteLocalInt(oItem,"DuelistPreciseSlash");
        //DeleteLocalInt(oItem,"DuelistPreciseSmash");
        // Dispater
        DeleteLocalInt(oItem,"DispIronPowerA");
        DeleteLocalInt(oItem,"DispIronPowerD");
        // Dragonwrack
        DeleteLocalInt(oItem,"DWright");
    }

    // LEFT HAND
    if(!bGivenObject)
        oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC);

    if(bGivenObject || GetIsObjectValid(oItem))
    {
        if(!bGivenObject)
        {
            // Clear composite bonuses
            TotalRemovePropertyT(oItem);
            DeleteNamedComposites(oItem, "PRC_CBonT");
        }

        //Vile/Sanctify & Un/Holy Martial Strike
        DeleteLocalInt(oItem,"SanctMar");
        DeleteLocalInt(oItem,"MartialStrik");
        DeleteLocalInt(oItem,"UnholyStrik");
        DeleteLocalInt(oItem,"USanctMar");
        // Dragonwrack
        DeleteLocalInt(oItem,"DWleft");
        // Dispater
        DeleteLocalInt(oItem,"DispIronPowerA");
        DeleteLocalInt(oItem,"DispIronPowerD");
    }

    // CHEST
    if(!bGivenObject)
        oItem = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC);

    if(bGivenObject || GetIsObjectValid(oItem))
    {
        if(!bGivenObject)
        {
            // Clear composite bonuses
            TotalRemovePropertyT(oItem);
            DeleteNamedComposites(oItem, "PRC_CBonT");
        }
        // Frenzied Berzerker
        DeleteLocalInt(oItem,"AFrenzy");
        // Shadowlord
        DeleteLocalInt(oItem,"ShaDiscorp");
        // Dragonwrack
        DeleteLocalInt(oItem,"Dragonwrack");
    }

    // LEFT RING
    if(!bGivenObject)
        oItem = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC);

    if(bGivenObject || GetIsObjectValid(oItem))
    {
        if(!bGivenObject)
        {
            // Clear composite bonuses
            TotalRemovePropertyT(oItem);
            DeleteNamedComposites(oItem, "PRC_CBonT");
        }
    }

    // ARMS
    if(!bGivenObject)
        oItem = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC);

    if(bGivenObject || GetIsObjectValid(oItem))
    {
        if(!bGivenObject)
        {
            // Clear composite bonuses
            TotalRemovePropertyT(oItem);
            DeleteNamedComposites(oItem, "PRC_CBonT");
        }

        // Disciple of Mephistopheles
        DeleteLocalInt(oItem,"DiscMephGlove");
    }
}

void SetCompositeAttackBonus(object oPC, string sBonus, int iVal, int iSubType = ATTACK_BONUS_MISC)
{
    object oCastingObject = CreateObject(OBJECT_TYPE_PLACEABLE, "x0_rodwonder", GetLocation(oPC));

    int iSpl = 2732; //SPELL_SET_COMPOSITE_ATTACK_BONUS;

    SetLocalString(oCastingObject, "SET_COMPOSITE_STRING", sBonus);
    SetLocalInt(oCastingObject, "SET_COMPOSITE_VALUE", iVal);
    SetLocalInt(oCastingObject, "SET_COMPOSITE_SUBTYPE", iSubType);

    DelayCommand(0.1, AssignCommand(oCastingObject, ActionCastSpellAtObject(iSpl, oPC, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE)));

    DestroyObject(oCastingObject, 6.0);
}

int GetSRByValue(int nValue)
{
    switch(nValue)
    {
        case  1: return 52;
        case  2: return 53;
        case  3: return 54;
        case  4: return 55;
        case  5: return 56;
        case  6: return 57;
        case  7: return 58;
        case  8: return 59;
        case  9: return 60;
        case 10: return  0;
        case 12: return  1;
        case 14: return  2;
        case 16: return  3;
        case 18: return  4;
        case 20: return  5;
        case 22: return  6;
        case 24: return  7;
        case 26: return  8;
        case 28: return  9;
        case 30: return 10;
        case 32: return 11;
        case 34: return 12;
        case 36: return 13;
        case 38: return 14;
        case 40: return 15;
        case 42: return 16;
        case 44: return 17;
        case 46: return 18;
        case 48: return 19;
        case 50: return 20;
        case 52: return 21;
        case 54: return 22;
        case 56: return 23;
        case 58: return 24;
        case 60: return 25;
        case 11: return 26;
        case 13: return 27;
        case 15: return 28;
        case 17: return 29;
        case 19: return 30;
        case 21: return 31;
        case 23: return 32;
        case 25: return 33;
        case 27: return 34;
        case 29: return 35;
        case 31: return 36;
        case 33: return 37;
        case 35: return 38;
        case 37: return 39;
        case 39: return 40;
        case 41: return 41;
        case 43: return 42;
        case 45: return 43;
        case 47: return 44;
        case 49: return 45;
        case 51: return 46;
        case 53: return 47;
        case 55: return 48;
        case 57: return 49;
        case 59: return 50;
        case 61: return 51;
    }
    if(nValue < 1)
        return -1;
    if(nValue > 98)
        return 61;//99 max
    if(nValue > 61)
        return 51;//61 flat cap
    return -1;
}

void UpdateUsedCompositeNamesList(object oItem, string sBase, string sComposite)
{
    // Add the bonus name to the list if it isn't there already
    if(!GetLocalInt(oItem, sBase + "_Exist_" + sComposite))
    {
        if(DEBUG) DoDebug("Storing the composite name '" + sComposite + "' for later deletion");

        string sArrayName = sBase + "_Names";
        // Create the array if it doesn't exist already
        if(!array_exists(oItem, sArrayName))
            array_create(oItem, sArrayName);

        // Store the bonus name in a list
        array_set_string(oItem, sArrayName, array_get_size(oItem, sArrayName), sComposite);

        // Store a marker so we don't need to loop over the list to find out if the name has been stored already
        SetLocalInt(oItem, sBase + "_Exist_" + sComposite, TRUE);
    }
}

void DeleteNamedComposites(object oItem, string sBase)
{
    if(DEBUG) DoDebug("Deleting composite bonus list '" + sBase + "' on " + DebugObject2Str(oItem));

    string sArrayName = sBase + "_Names";
    string sComposite;
    int nMax = array_get_size(oItem, sArrayName);
    int i = 0;

    // Delete all composite values and markers
    for(; i < nMax; i++)
    {
        sComposite = array_get_string(oItem, sArrayName, i);
        if(DEBUG) DoDebug("Deleting bonus marker '" + sComposite + "'");
        DeleteLocalInt(oItem, sComposite);
        DeleteLocalInt(oItem, sBase + "_Exist_" + sComposite);
    }

    // Delete the array
    array_delete(oItem, sArrayName);
}

int GetIsMagicItem(object oItem)
{
    itemproperty ip = GetFirstItemProperty(oItem);
    int nType;
    int nSubtype;
    while(GetIsItemPropertyValid(ip))   //loop through item properties looking for a magical one
    {
        if(GetItemPropertyDurationType(ip) == DURATION_TYPE_PERMANENT)
        {   //ignore temporary properties
            nType = GetItemPropertyType(ip);
            if((nType >= 0 && nType <= 9) ||    //read from itempropdef.2da
                (nType >= 13 && nType <= 20) ||
                (nType == 26) ||
                (nType >= 32 && nType <= 44) ||
                (nType == 46) ||
                (nType >= 51 && nType <= 59) ||
                (nType == 61) ||
                (nType >= 67 && nType <= 69) ||
                (nType >= 71 && nType <= 80) ||
                (nType == 82) ||
                (nType == 84) ||
                (nType >= 100 && nType <= 101) ||
                (nType >= 133 && nType <= 134))
            {
                return 1;   //magical property
            }
            nSubtype = GetItemPropertySubType(ip);

            if(nType == ITEM_PROPERTY_BONUS_FEAT)
            {
                if(GetBaseItemType(oItem) == BASE_ITEM_WHIP)
                {
                    if(nSubtype != IP_CONST_FEAT_DISARM_WHIP)
                        return 1;
                }
                else
                    return 1;
            }
            ip = GetNextItemProperty(oItem);
        }
    }
    return 0;
}

int FeatToIprop(int nFeat)
{
            switch(nFeat)
            {
            case FEAT_WEAPON_FOCUS_BASTARD_SWORD: 	return IP_CONST_FEAT_WEAPON_FOCUS_BASTARD_SWORD;
            case FEAT_WEAPON_FOCUS_BATTLE_AXE: 		return IP_CONST_FEAT_WEAPON_FOCUS_BATTLE_AXE;
            case FEAT_WEAPON_FOCUS_CLUB: 			return IP_CONST_FEAT_WEAPON_FOCUS_CLUB;
            case FEAT_WEAPON_FOCUS_DAGGER: 			return IP_CONST_FEAT_WEAPON_FOCUS_DAGGER;
            case FEAT_WEAPON_FOCUS_DART: 			return IP_CONST_FEAT_WEAPON_FOCUS_DART;
            case FEAT_WEAPON_FOCUS_DIRE_MACE: 		return IP_CONST_FEAT_WEAPON_FOCUS_DIRE_MACE;
            case FEAT_WEAPON_FOCUS_DOUBLE_AXE: 		return IP_CONST_FEAT_WEAPON_FOCUS_DOUBLE_AXE;
            case FEAT_WEAPON_FOCUS_DWAXE: 			return IP_CONST_FEAT_WEAPON_FOCUS_DWAXE;
            case FEAT_WEAPON_FOCUS_GREAT_AXE: 		return IP_CONST_FEAT_WEAPON_FOCUS_GREAT_AXE;
            case FEAT_WEAPON_FOCUS_GREAT_SWORD: 	return IP_CONST_FEAT_WEAPON_FOCUS_GREAT_SWORD;
            case FEAT_WEAPON_FOCUS_HALBERD: 		return IP_CONST_FEAT_WEAPON_FOCUS_HALBERD;
            case FEAT_WEAPON_FOCUS_HAND_AXE: 		return IP_CONST_FEAT_WEAPON_FOCUS_HAND_AXE;
            case FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW: 	return IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW;
            case FEAT_WEAPON_FOCUS_HEAVY_FLAIL: 	return IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_FLAIL;
            case FEAT_WEAPON_FOCUS_KAMA: 			return IP_CONST_FEAT_WEAPON_FOCUS_KAMA;
            case FEAT_WEAPON_FOCUS_KATANA: 			return IP_CONST_FEAT_WEAPON_FOCUS_KATANA;
            case FEAT_WEAPON_FOCUS_KUKRI: 			return IP_CONST_FEAT_WEAPON_FOCUS_KUKRI;
            case FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW: 	return IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW;
            case FEAT_WEAPON_FOCUS_LIGHT_FLAIL: 	return IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_FLAIL;
            case FEAT_WEAPON_FOCUS_LIGHT_HAMMER: 	return IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_HAMMER;
            case FEAT_WEAPON_FOCUS_LIGHT_MACE: 		return IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_MACE;
            case FEAT_WEAPON_FOCUS_LONG_SWORD: 		return IP_CONST_FEAT_WEAPON_FOCUS_LONG_SWORD;
            case FEAT_WEAPON_FOCUS_LONGBOW: 		return IP_CONST_FEAT_WEAPON_FOCUS_LONGBOW;
            case FEAT_WEAPON_FOCUS_MORNING_STAR: 	return IP_CONST_FEAT_WEAPON_FOCUS_MORNING_STAR;
            case FEAT_WEAPON_FOCUS_STAFF: 			return IP_CONST_FEAT_WEAPON_FOCUS_STAFF;
            case FEAT_WEAPON_FOCUS_RAPIER: 			return IP_CONST_FEAT_WEAPON_FOCUS_RAPIER;
            case FEAT_WEAPON_FOCUS_SCIMITAR: 		return IP_CONST_FEAT_WEAPON_FOCUS_SCIMITAR;
            case FEAT_WEAPON_FOCUS_SCYTHE: 			return IP_CONST_FEAT_WEAPON_FOCUS_SCYTHE;
            case FEAT_WEAPON_FOCUS_SHORTBOW: 		return IP_CONST_FEAT_WEAPON_FOCUS_SHORTBOW;
            case FEAT_WEAPON_FOCUS_SPEAR: 			return IP_CONST_FEAT_WEAPON_FOCUS_SPEAR;
            case FEAT_WEAPON_FOCUS_SHORT_SWORD: 	return IP_CONST_FEAT_WEAPON_FOCUS_SHORT_SWORD;
            case FEAT_WEAPON_FOCUS_SHURIKEN: 		return IP_CONST_FEAT_WEAPON_FOCUS_SHURIKEN;
            case FEAT_WEAPON_FOCUS_SICKLE: 			return IP_CONST_FEAT_WEAPON_FOCUS_SICKLE;
            case FEAT_WEAPON_FOCUS_SLING: 			 return IP_CONST_FEAT_WEAPON_FOCUS_SLING;
            case FEAT_WEAPON_FOCUS_THROWING_AXE: 	 return IP_CONST_FEAT_WEAPON_FOCUS_THROWING_AXE;
            case FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD: return IP_CONST_FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD;
            case FEAT_WEAPON_FOCUS_WAR_HAMMER: 		 return IP_CONST_FEAT_WEAPON_FOCUS_WAR_HAMMER;
            case FEAT_WEAPON_FOCUS_WHIP:			 return IP_CONST_FEAT_WEAPON_FOCUS_WHIP;
			case FEAT_WEAPON_FOCUS_EAGLE_CLAW:		 return IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW;
			case FEAT_WEAPON_FOCUS_FALCHION:		 return IP_CONST_FEAT_WEAPON_FOCUS_FALCHION;
			case FEAT_WEAPON_FOCUS_HEAVY_MACE:	   	 return IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_MACE;
			case FEAT_WEAPON_FOCUS_LIGHT_PICK:		 return IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_PICK;
			case FEAT_WEAPON_FOCUS_HEAVY_PICK:		 return IP_CONST_FEAT_WEAPON_FOCUS_HEAVY_PICK;
			case FEAT_WEAPON_FOCUS_KATAR:			 return IP_CONST_FEAT_WEAPON_FOCUS_KATAR;
			case FEAT_WEAPON_FOCUS_SAI:			 	 return IP_CONST_FEAT_WEAPON_FOCUS_SAI;	
			case FEAT_WEAPON_FOCUS_NUNCHAKU:		 return IP_CONST_FEAT_WEAPON_FOCUS_NUNCHAKU;	
			case FEAT_WEAPON_FOCUS_MAUL:			 return IP_CONST_FEAT_WEAPON_FOCUS_MAUL;	
			case FEAT_WEAPON_FOCUS_SAP:			 	 return IP_CONST_FEAT_WEAPON_FOCUS_SAP;	
			case FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR:  return IP_CONST_FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR;
			case FEAT_WEAPON_FOCUS_GOAD:			 return IP_CONST_FEAT_WEAPON_FOCUS_GOAD;		
			case FEAT_WEAPON_FOCUS_TRIDENT:			 return IP_CONST_FEAT_WEAPON_FOCUS_TRIDENT;			
			case FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE: return IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE;
			case FEAT_WEAPON_FOCUS_ELVEN_THINBLADE:  return IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_THINBLADE;
			case FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE: return IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE;   
		

        case FEAT_WEAPON_SPECIALIZATION_CLUB: return              IP_CONST_FEAT_WEAPON_SPECIALIZATION_CLUB              ;
        case FEAT_WEAPON_SPECIALIZATION_DAGGER: return            IP_CONST_FEAT_WEAPON_SPECIALIZATION_DAGGER            ;
        case FEAT_WEAPON_SPECIALIZATION_DART: return              IP_CONST_FEAT_WEAPON_SPECIALIZATION_DART              ;
        case FEAT_WEAPON_SPECIALIZATION_HEAVY_CROSSBOW: return    IP_CONST_FEAT_WEAPON_SPECIALIZATION_HEAVY_CROSSBOW    ;
        case FEAT_WEAPON_SPECIALIZATION_LIGHT_CROSSBOW: return    IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_CROSSBOW    ;
        case FEAT_WEAPON_SPECIALIZATION_LIGHT_MACE: return        IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_MACE        ;
        case FEAT_WEAPON_SPECIALIZATION_MORNING_STAR: return      IP_CONST_FEAT_WEAPON_SPECIALIZATION_MORNING_STAR      ;
        case FEAT_WEAPON_SPECIALIZATION_STAFF: return             IP_CONST_FEAT_WEAPON_SPECIALIZATION_STAFF             ;
        case FEAT_WEAPON_SPECIALIZATION_SPEAR: return             IP_CONST_FEAT_WEAPON_SPECIALIZATION_SPEAR             ;
        case FEAT_WEAPON_SPECIALIZATION_SICKLE: return            IP_CONST_FEAT_WEAPON_SPECIALIZATION_SICKLE            ;
        case FEAT_WEAPON_SPECIALIZATION_SLING: return             IP_CONST_FEAT_WEAPON_SPECIALIZATION_SLING             ;
        case FEAT_WEAPON_SPECIALIZATION_UNARMED_STRIKE: return    IP_CONST_FEAT_WEAPON_SPECIALIZATION_UNARMED_STRIKE    ;
        case FEAT_WEAPON_SPECIALIZATION_LONGBOW: return           IP_CONST_FEAT_WEAPON_SPECIALIZATION_LONGBOW           ;
        case FEAT_WEAPON_SPECIALIZATION_SHORTBOW: return          IP_CONST_FEAT_WEAPON_SPECIALIZATION_SHORTBOW          ;
        case FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD: return       IP_CONST_FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD       ;
        case FEAT_WEAPON_SPECIALIZATION_RAPIER: return            IP_CONST_FEAT_WEAPON_SPECIALIZATION_RAPIER            ;
        case FEAT_WEAPON_SPECIALIZATION_SCIMITAR: return          IP_CONST_FEAT_WEAPON_SPECIALIZATION_SCIMITAR          ;
        case FEAT_WEAPON_SPECIALIZATION_LONG_SWORD: return        IP_CONST_FEAT_WEAPON_SPECIALIZATION_LONG_SWORD        ;
        case FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD: return       IP_CONST_FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD       ;
        case FEAT_WEAPON_SPECIALIZATION_HAND_AXE: return          IP_CONST_FEAT_WEAPON_SPECIALIZATION_HAND_AXE          ;
        case FEAT_WEAPON_SPECIALIZATION_THROWING_AXE: return      IP_CONST_FEAT_WEAPON_SPECIALIZATION_THROWING_AXE      ;
        case FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE: return        IP_CONST_FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE        ;
        case FEAT_WEAPON_SPECIALIZATION_GREAT_AXE: return         IP_CONST_FEAT_WEAPON_SPECIALIZATION_GREAT_AXE         ;
        case FEAT_WEAPON_SPECIALIZATION_HALBERD: return           IP_CONST_FEAT_WEAPON_SPECIALIZATION_HALBERD           ;
        case FEAT_WEAPON_SPECIALIZATION_LIGHT_HAMMER: return      IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_HAMMER      ;
        case FEAT_WEAPON_SPECIALIZATION_LIGHT_FLAIL: return       IP_CONST_FEAT_WEAPON_SPECIALIZATION_LIGHT_FLAIL       ;
        case FEAT_WEAPON_SPECIALIZATION_WAR_HAMMER: return        IP_CONST_FEAT_WEAPON_SPECIALIZATION_WAR_HAMMER        ;
        case FEAT_WEAPON_SPECIALIZATION_HEAVY_FLAIL: return       IP_CONST_FEAT_WEAPON_SPECIALIZATION_HEAVY_FLAIL       ;
        case FEAT_WEAPON_SPECIALIZATION_KAMA: return              IP_CONST_FEAT_WEAPON_SPECIALIZATION_KAMA              ;
        case FEAT_WEAPON_SPECIALIZATION_KUKRI: return             IP_CONST_FEAT_WEAPON_SPECIALIZATION_KUKRI             ;
        case FEAT_WEAPON_SPECIALIZATION_SHURIKEN: return          IP_CONST_FEAT_WEAPON_SPECIALIZATION_SHURIKEN          ;
        case FEAT_WEAPON_SPECIALIZATION_SCYTHE: return            IP_CONST_FEAT_WEAPON_SPECIALIZATION_SCYTHE            ;
        case FEAT_WEAPON_SPECIALIZATION_KATANA: return            IP_CONST_FEAT_WEAPON_SPECIALIZATION_KATANA            ;
        case FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD: return     IP_CONST_FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD     ;
        case FEAT_WEAPON_SPECIALIZATION_DIRE_MACE: return         IP_CONST_FEAT_WEAPON_SPECIALIZATION_DIRE_MACE         ;
        case FEAT_WEAPON_SPECIALIZATION_DOUBLE_AXE: return        IP_CONST_FEAT_WEAPON_SPECIALIZATION_DOUBLE_AXE        ;
        case FEAT_WEAPON_SPECIALIZATION_TWO_BLADED_SWORD: 	return IP_CONST_FEAT_WEAPON_SPECIALIZATION_TWO_BLADED_SWORD;
        case FEAT_WEAPON_SPECIALIZATION_DWAXE: 				return IP_CONST_FEAT_WEAPON_SPECIALIZATION_DWAXE;
        case FEAT_WEAPON_SPECIALIZATION_WHIP: 				return IP_CONST_FEAT_WEAPON_SPECIALIZATION_WHIP;	
		case FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE: 	return IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_LIGHTBLADE;
		case FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE: 	return IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_THINBLADE;
		case FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE: 	return IP_CONST_FEAT_WEAPON_SPECIALIZATION_ELVEN_COURTBLADE;		
            }
    return - 1;
}

int FocusToWeapProf(int nFeat)
{
    switch(nFeat)
    {
        case FEAT_WEAPON_FOCUS_CLUB:             	return BASE_ITEM_CLUB;
        case FEAT_WEAPON_FOCUS_DAGGER:           	return BASE_ITEM_DAGGER;
        case FEAT_WEAPON_FOCUS_DART:             	return BASE_ITEM_DART;
        case FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW:   	return BASE_ITEM_HEAVYCROSSBOW;
        case FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW:   	return BASE_ITEM_LIGHTCROSSBOW;
        case FEAT_WEAPON_FOCUS_LIGHT_MACE:       	return BASE_ITEM_LIGHTMACE;
        case FEAT_WEAPON_FOCUS_MORNING_STAR:     	return BASE_ITEM_MORNINGSTAR;
        case FEAT_WEAPON_FOCUS_STAFF:            	return BASE_ITEM_QUARTERSTAFF;
        case FEAT_WEAPON_FOCUS_SPEAR:            	return BASE_ITEM_SHORTSPEAR;
        case FEAT_WEAPON_FOCUS_SICKLE:           	return BASE_ITEM_SICKLE;
        case FEAT_WEAPON_FOCUS_SLING:            	return BASE_ITEM_SLING;
        case FEAT_WEAPON_FOCUS_LONGBOW:          	return BASE_ITEM_LONGBOW;
        case FEAT_WEAPON_FOCUS_SHORTBOW:         	return BASE_ITEM_SHORTBOW;
        case FEAT_WEAPON_FOCUS_SHORT_SWORD:      	return BASE_ITEM_SHORTSWORD;
        case FEAT_WEAPON_FOCUS_RAPIER:           	return BASE_ITEM_RAPIER;
        case FEAT_WEAPON_FOCUS_SCIMITAR:         	return BASE_ITEM_SCIMITAR;
        case FEAT_WEAPON_FOCUS_LONG_SWORD:       	return BASE_ITEM_LONGSWORD;
        case FEAT_WEAPON_FOCUS_GREAT_SWORD:      	return BASE_ITEM_GREATSWORD;
        case FEAT_WEAPON_FOCUS_HAND_AXE:         	return BASE_ITEM_HANDAXE;
        case FEAT_WEAPON_FOCUS_THROWING_AXE:     	return BASE_ITEM_THROWINGAXE;
        case FEAT_WEAPON_FOCUS_BATTLE_AXE:       	return BASE_ITEM_BATTLEAXE;
        case FEAT_WEAPON_FOCUS_GREAT_AXE:        	return BASE_ITEM_GREATAXE;
        case FEAT_WEAPON_FOCUS_HALBERD:          	return BASE_ITEM_HALBERD;
        case FEAT_WEAPON_FOCUS_LIGHT_HAMMER:     	return BASE_ITEM_LIGHTHAMMER;
        case FEAT_WEAPON_FOCUS_LIGHT_FLAIL:      	return BASE_ITEM_LIGHTFLAIL;
        case FEAT_WEAPON_FOCUS_WAR_HAMMER:       	return BASE_ITEM_WARHAMMER;
        case FEAT_WEAPON_FOCUS_HEAVY_FLAIL:      	return BASE_ITEM_HEAVYFLAIL;
        case FEAT_WEAPON_FOCUS_KAMA:             	return BASE_ITEM_KAMA;
        case FEAT_WEAPON_FOCUS_KUKRI:            	return BASE_ITEM_KUKRI;
        case FEAT_WEAPON_FOCUS_SHURIKEN:         	return BASE_ITEM_SHURIKEN;
        case FEAT_WEAPON_FOCUS_SCYTHE:           	return BASE_ITEM_SCYTHE;
        case FEAT_WEAPON_FOCUS_KATANA:           	return BASE_ITEM_KATANA;
        case FEAT_WEAPON_FOCUS_BASTARD_SWORD:    	return BASE_ITEM_BASTARDSWORD;
        case FEAT_WEAPON_FOCUS_DIRE_MACE:        	return BASE_ITEM_DIREMACE;
        case FEAT_WEAPON_FOCUS_DOUBLE_AXE:       	return BASE_ITEM_DOUBLEAXE;
        case FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD: 	return BASE_ITEM_TWOBLADEDSWORD;
        case FEAT_WEAPON_FOCUS_DWAXE:            	return BASE_ITEM_DWARVENWARAXE;
        case FEAT_WEAPON_FOCUS_WHIP:             	return BASE_ITEM_WHIP;
		case FEAT_WEAPON_FOCUS_EAGLE_CLAW:		 	return BASE_ITEM_EAGLE_CLAW;
		case FEAT_WEAPON_FOCUS_FALCHION:		 	return BASE_ITEM_FALCHION;
		case FEAT_WEAPON_FOCUS_GOAD:			 	return BASE_ITEM_GOAD;
		case FEAT_WEAPON_FOCUS_HEAVY_MACE:	   	 	return BASE_ITEM_HEAVY_MACE;
		case FEAT_WEAPON_FOCUS_LIGHT_PICK:		 	return BASE_ITEM_LIGHT_PICK;
		case FEAT_WEAPON_FOCUS_HEAVY_PICK:		 	return BASE_ITEM_HEAVY_PICK;
		case FEAT_WEAPON_FOCUS_KATAR:			 	return BASE_ITEM_KATAR;
		case FEAT_WEAPON_FOCUS_SAI:			 	 	return BASE_ITEM_SAI;	
		case FEAT_WEAPON_FOCUS_NUNCHAKU:		 	return BASE_ITEM_NUNCHAKU;	
		case FEAT_WEAPON_FOCUS_MAUL:			 	return BASE_ITEM_MAUL;	
		case FEAT_WEAPON_FOCUS_SAP:			 	 	return BASE_ITEM_SAP;	
		case FEAT_WEAPON_FOCUS_DOUBLE_SCIMITAR:  	return BASE_ITEM_DOUBLE_SCIMITAR;
		case FEAT_WEAPON_FOCUS_TRIDENT:			 	return BASE_ITEM_TRIDENT;
		case FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE:	return BASE_ITEM_ELVEN_LIGHTBLADE;
		case FEAT_WEAPON_FOCUS_ELVEN_THINBLADE:		return BASE_ITEM_ELVEN_THINBLADE;
		case FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE:	return BASE_ITEM_ELVEN_COURTBLADE;
				
    }
    return - 1;
}

void AddSkinFeat(int nFeat, int IPFeat, object oSkin, object oPC = OBJECT_SELF, float fDuration = 0.0f)
{
    // Already has that feat - do nothing
    if(GetHasFeat(nFeat, oPC))
        return;

    int nDurType = fDuration == 0.0f ? DURATION_TYPE_PERMANENT : DURATION_TYPE_TEMPORARY;

    AddItemProperty(nDurType, ItemPropertyBonusFeat(IPFeat), oSkin, fDuration);
}