Author Topic: How-to: Mastering item properties  (Read 356 times)

Legacy_Kato -

  • Hero Member
  • *****
  • Posts: 747
  • Karma: +0/-0
How-to: Mastering item properties
« on: February 21, 2015, 09:36:00 am »


               

Hi everyone,


 


Quite often I see some posts regarding item properties manipulation, and for the cost, it looks like most servers simply copy the item to modify, apply the modifs to the copy, retrieve its gold value, compare it to the original and destroy the copy. That's perfectly understandable since the item property cost formula (displayed in the item format document) is flawed. Actually, there are several other problems in the vanilla ip system, so in case it can be helpful, here is a description of some of them, with a workaround for each. Also included is the correct item property cost formula.


 


First and foremost, several of the vanilla ip functions are flawed, at least if we believe the Lexicon regarding the return values, which are supposed to be -1 if no usable data exists. Here are the functions, their return values and the workarounds:


 


GetItemPropertySubType(itemproperty ip) // Should return -1 if prop has no subtype but often returns 65535.


GetItemPropertyCostTableValue(itemproperty ip) // same as above


GetItemPropertyCostTable() // Should return -1 if prop has no cost table but often returns 255.


 


The workaround is very simple, using the first function in the following example:


 


int GetIPSubType(itemproperty ip)


{


    int nSub = GetItemPropertySubType(ip);


    if(nSub == 65535) return -1;


    return nSub;


}


 


Then there is the matter of the include x2_inc_itemprops, in case you need to use it(I'll personally never use it).


Looking at the function IPGetHasItemPropertyByConst(int nItemProp, object oItem) we see that it does exactly the same thing as GetItemHasItemProperty(object oItem, int nProperty), so it is useless in case you're wondering. Moreover, most of the time you will want to retrieve the property on an Item, not only to verify if your Item has it. Using the corrected vanilla functions, this would do the job:


 


itemproperty GetMatchingIP(object oItem, int nPropType, int nSubType = -1)

{

   itemproperty ip = GetFirstItemProperty(oItem);

   itemproperty ipRet;

 

   while(GetIsItemPropertyValid(ip))

   {

      if(GetItemPropertyType(ip) == nPropType && GetIPSubType(ip) == nSubType)

      {

         ipRet = ip;

         break;

      }

      ip = GetNextItemProperty(oItem);

   }

   return ipRet;

}

 


Then, in the same include, look at the function IPGetItemPropertyByID(int nPropID, int nParam1 = 0, int nParam2 = 0, int nParam3 = 0, int nParam4 = 0). If you need to create an item property without knowing in advance what the params will be, you must wire the parameters with a certain logic, each time you call the function. Although it is not a bug, such a counter-intuitive approach can yield undesired results unless you become really used to it. Here is a much more intuitive approach, add or remove props to your taste in the switch:


 


// nIndex is the cost table value, or magnitude

itemproperty GetIPFromConst(int nPropType, int nSubType = -1, int nIndex = -1)

{

   itemproperty ipRet;

   switch(nPropType)

   {

      case ITEM_PROPERTY_ABILITY_BONUS: return ItemPropertyAbilityBonus(nSubType, nIndex);

      case ITEM_PROPERTY_AC_BONUS: return ItemPropertyACBonus(nIndex);

      case ITEM_PROPERTY_ENHANCEMENT_BONUS: return ItemPropertyEnhancementBonus(nIndex);

      case ITEM_PROPERTY_DAMAGE_BONUS: return ItemPropertyDamageBonus(nSubType, nIndex);

      case ITEM_PROPERTY_DARKVISION: return ItemPropertyDarkvision();

      case ITEM_PROPERTY_HASTE: return ItemPropertyHaste();

      case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC: return ItemPropertyBonusSavingThrow(nSubType, nIndex);

      case ITEM_PROPERTY_KEEN: return ItemPropertyKeen();

      case ITEM_PROPERTY_MIGHTY: return ItemPropertyMaxRangeStrengthMod(nIndex);

      case ITEM_PROPERTY_REGENERATION: return ItemPropertyRegeneration(nIndex);

      case ITEM_PROPERTY_ATTACK_BONUS: return ItemPropertyAttackBonus(nIndex);

      case ITEM_PROPERTY_MASSIVE_CRITICALS: return ItemPropertyMassiveCritical(nIndex);

   }

   return ipRet;

}

 

 

Last but not least, the formula for calculating the cost of a property. As I mentioned earlier, the item format formula is WRONG, check it for yourself if you're skeptical. Instead of simply displaying the formula here, I'll paste a function using it, taken from an old post of mine. It returns the "math" cost of a single property for flexibility, so for the cost of a potentially upgraded item, please see next post.  

 

float GetIPMathCost(itemproperty ip)

{

    int nProp = GetItemPropertyType(ip);

    int nSub = GetIPSubType(ip); // using the corrected function

    int nCostTable = GetItemPropertyCostTable(ip);

    float fPropCost = StringToFloat(Get2DAString("ItemPropDef", "Cost", nProp));

    float fSubCost = 1.0;

    float fCostValue = 1.0;

    if(fPropCost == 0.0 && nSub > -1)

    {

       string sSubTable = Get2DAString("ItemPropDef", "SubTypeResRef", nProp);

       fSubCost = StringToFloat(Get2DAString(sSubTable, "Cost", nSub));

    }

    if(nCostTable > 0)

    {

       string sCostTable = Get2DAString("iprp_costtable", "Name", nCostTable);

       fCostValue = StringToFloat(Get2DAString(sCostTable, "Cost", GetItemPropertyCostTableValue(ip)));

    }

    return fPropCost * fSubCost * fCostValue;

}  

 

 

Kato


               
               

               
            

Legacy_Kato -

  • Hero Member
  • *****
  • Posts: 747
  • Karma: +0/-0
How-to: Mastering item properties
« Reply #1 on: February 21, 2015, 09:45:59 pm »


               

Continuing from post #1, here is the function returning the cost of a "virtually" upgraded or downgraded item. This is eliminating the need of using item copies in upgrade/downgrade systems, thanks to the previously defined function GetIPMathCost(). It returns the cost of oItem, as if ipAdd had been applied to it, overwriting any similar property. To retrieve the cost of the upgrade itself, you can compare the return value of the following function with the gold value of the item to modify, or you can modify this function to do it.


 


int GetModifiedItemCost(object oItem, itemproperty ipAdd)

{

   int nItemType = GetBaseItemType(oItem);

   float fItemMult = StringToFloat(Get2DAString("baseitems", "ItemMultiplier", nItemType));

   float fBaseItemCost = StringToFloat(Get2DAString("baseitems", "BaseCost", nItemType));

   int nCurrentValue = GetGoldPieceValue(oItem);

 

   float fTotalMathCost = GetIPMathCost(ipAdd); // using the function of post #1 

   itemproperty ipMatch = GetMatchingIP(oItem, GetItemPropertyType(ipAdd), GetIPSubType(ipAdd));

 

   itemproperty ip = GetFirstItemProperty(oItem);

   while(GetIsItemPropertyValid(ip))

   {

      if(ip != ipMatch) fTotalMathCost += GetIPMathCost(ip);

      ip = GetNextItemProperty(oItem);

   }

   int nUpgradedValue = FloatToInt(fItemMult * (fBaseItemCost + pow(fTotalMathCost, 2.0) * 1000)); // additional cost needs to be added here

   if(nUpgradedValue > nCurrentValue) return nUpgradedValue;

   else return nCurrentValue - (nCurrentValue - nUpgradedValue);

}

 


 


Let me know if you need assistance with this, thanks for reading


Kato 



               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
How-to: Mastering item properties
« Reply #2 on: February 22, 2015, 12:30:51 am »


               

pow(fTotalMathCost, 2.0)


 


This is where the math goes whacky.  The system does not do (x + y)^2 (I am using ^ as a shorthand for power here), rather it does x^2 + 2xy + y^2  (in other words it breaks up the square into addable entities).  This should produce the same result, but it is calculated as a float, and does not have a significant amount of precision if x and y are far off from each other.


 


As an example, if I wanted to do fTotalMath as the system does I would do the following


 


fTotalMathSquared = pow(GetMathCost(ip), 2.0);


... (then in the loop)


fTotalMathSquared += 2 *GetMathCost(ip) * sqrt(fTotalMathSquared) + pow(GetMathCost(ip), 2.0);



               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
How-to: Mastering item properties
« Reply #3 on: February 22, 2015, 01:13:15 am »


               


 


Then, in the same include, look at the function IPGetItemPropertyByID(int nPropID, int nParam1 = 0, int nParam2 = 0, int nParam3 = 0, int nParam4 = 0). If you need to create an item property without knowing in advance what the params will be, you must wire the parameters with a certain logic, each time you call the function. Although it is not a bug, such a counter-intuitive approach can yield undesired results unless you become really used to it. Here is a much more intuitive approach, add or remove props to your taste in the switch:

 


// nIndex is the cost table value, or magnitude

itemproperty GetIPFromConst(int nPropType, int nSubType = -1, int nIndex = -1)

{

   itemproperty ipRet;

   switch(nPropType)

   {

      case ITEM_PROPERTY_ABILITY_BONUS: return ItemPropertyAbilityBonus(nSubType, nIndex);

      case ITEM_PROPERTY_AC_BONUS: return ItemPropertyACBonus(nIndex);

      case ITEM_PROPERTY_ENHANCEMENT_BONUS: return ItemPropertyEnhancementBonus(nIndex);

      case ITEM_PROPERTY_DAMAGE_BONUS: return ItemPropertyDamageBonus(nSubType, nIndex);

      case ITEM_PROPERTY_DARKVISION: return ItemPropertyDarkvision();

      case ITEM_PROPERTY_HASTE: return ItemPropertyHaste();

      case ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC: return ItemPropertyBonusSavingThrow(nSubType, nIndex);

      case ITEM_PROPERTY_KEEN: return ItemPropertyKeen();

      case ITEM_PROPERTY_MIGHTY: return ItemPropertyMaxRangeStrengthMod(nIndex);

      case ITEM_PROPERTY_REGENERATION: return ItemPropertyRegeneration(nIndex);

      case ITEM_PROPERTY_ATTACK_BONUS: return ItemPropertyAttackBonus(nIndex);

      case ITEM_PROPERTY_MASSIVE_CRITICALS: return ItemPropertyMassiveCritical(nIndex);

   }

   return ipRet;

}

 



 


This function is to be used in conjunction with des_crft_props.2da.  This 2da was only defined with light gems in mind (though BioWare put in a test row for keen which works).  As such you really should include all the cases and at least 3 parameters for the properties that require 3.


               
               

               
            

Legacy_Kato -

  • Hero Member
  • *****
  • Posts: 747
  • Karma: +0/-0
How-to: Mastering item properties
« Reply #4 on: February 22, 2015, 03:11:46 am »


               

Thank you very much WhiZard '<img'>