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