diff --git a/changes/armor-rating-rounding.md b/changes/armor-rating-rounding.md new file mode 100644 index 00000000..ae4f881f --- /dev/null +++ b/changes/armor-rating-rounding.md @@ -0,0 +1,2 @@ +Full armor ratings (with the fractional part) are now displayed in item descriptions. +Fix armor ratings that end in 0.25 and 0.75 being equivalent to that armor rating minus 0.25. diff --git a/src/brogue/IO.c b/src/brogue/IO.c index 97953b8f..e087c4f3 100644 --- a/src/brogue/IO.c +++ b/src/brogue/IO.c @@ -4476,8 +4476,8 @@ void highlightScreenCell(short x, short y, const color *highlightColor, short st // Like `armorValueIfUnenchanted` for the currently-equipped armor, but takes the penalty from // donning into account. -static short estimatedArmorValue() { - short retVal = armorValueIfUnenchanted(rogue.armor) - player.status[STATUS_DONNING]; +static float estimatedArmorValue() { + float retVal = armorValueIfUnenchanted(rogue.armor) - player.status[STATUS_DONNING]; return max(0, retVal); } @@ -4683,13 +4683,13 @@ short printMonsterInfo(creature *monst, short y, boolean dim, boolean highlight) tempColorEscape, rogue.strength - player.weaknessAmount, grayColorEscape, - displayedArmorValue()); + (int) displayedArmorValue()); } else { sprintf(buf, "Str: %s%i%s Armor: %i?", tempColorEscape, rogue.strength - player.weaknessAmount, grayColorEscape, - estimatedArmorValue()); + (int) estimatedArmorValue()); } //buf[20] = '\0'; printString(" ", 0, y, &white, &black, 0); diff --git a/src/brogue/Items.c b/src/brogue/Items.c index 937f083f..e0ea1a72 100644 --- a/src/brogue/Items.c +++ b/src/brogue/Items.c @@ -21,6 +21,7 @@ * along with this program. If not, see . */ +#include #include "Rogue.h" #include "GlobalsBase.h" @@ -2086,62 +2087,74 @@ void itemDetails(char *buf, item *theItem) { } // Display the known percentage by which the armor/weapon will increase/decrease accuracy/damage/defense if not already equipped. - if (!(theItem->flags & ITEM_EQUIPPED)) { - if (theItem->category & WEAPON) { - current = player.info.accuracy; - if (rogue.weapon) { - currentDamage = (rogue.weapon->damage.lowerBound + rogue.weapon->damage.upperBound) * FP_FACTOR / 2; - if ((rogue.weapon->flags & ITEM_IDENTIFIED) || rogue.playbackOmniscience) { - current = current * accuracyFraction(netEnchant(rogue.weapon)) / FP_FACTOR; - currentDamage = currentDamage * damageFraction(netEnchant(rogue.weapon)) / FP_FACTOR; - } else { - current = current * accuracyFraction(strengthModifier(rogue.weapon)) / FP_FACTOR; - currentDamage = currentDamage * damageFraction(strengthModifier(rogue.weapon)) / FP_FACTOR; - } + if (!(theItem->flags & ITEM_EQUIPPED) && (theItem->category & WEAPON)) { + current = player.info.accuracy; + if (rogue.weapon) { + currentDamage = (rogue.weapon->damage.lowerBound + rogue.weapon->damage.upperBound) * FP_FACTOR / 2; + if ((rogue.weapon->flags & ITEM_IDENTIFIED) || rogue.playbackOmniscience) { + current = current * accuracyFraction(netEnchant(rogue.weapon)) / FP_FACTOR; + currentDamage = currentDamage * damageFraction(netEnchant(rogue.weapon)) / FP_FACTOR; } else { - currentDamage = (player.info.damage.lowerBound + player.info.damage.upperBound) * FP_FACTOR / 2; + current = current * accuracyFraction(strengthModifier(rogue.weapon)) / FP_FACTOR; + currentDamage = currentDamage * damageFraction(strengthModifier(rogue.weapon)) / FP_FACTOR; } + } else { + currentDamage = (player.info.damage.lowerBound + player.info.damage.upperBound) * FP_FACTOR / 2; + } - new = player.info.accuracy; - newDamage = (theItem->damage.lowerBound + theItem->damage.upperBound) * FP_FACTOR / 2; - if ((theItem->flags & ITEM_IDENTIFIED) || rogue.playbackOmniscience) { - new = new * accuracyFraction(netEnchant(theItem)) / FP_FACTOR; - newDamage = newDamage * damageFraction(netEnchant(theItem)) / FP_FACTOR; - } else { - new = new * accuracyFraction(strengthModifier(theItem)) / FP_FACTOR; - newDamage = newDamage * damageFraction(strengthModifier(theItem)) / FP_FACTOR; - } - accuracyChange = (new * 100 / current) - 100; - damageChange = (newDamage * 100 / currentDamage) - 100; - sprintf(buf2, "Wielding the %s%s will %s your current accuracy by %s%i%%%s, and will %s your current damage by %s%i%%%s. ", - theName, - ((theItem->flags & ITEM_IDENTIFIED) || rogue.playbackOmniscience) ? "" : ", assuming it has no hidden properties,", - (((short) accuracyChange) < 0) ? "decrease" : "increase", - (((short) accuracyChange) < 0) ? badColorEscape : (accuracyChange > 0 ? goodColorEscape : ""), - abs((short) accuracyChange), - whiteColorEscape, - (((short) damageChange) < 0) ? "decrease" : "increase", - (((short) damageChange) < 0) ? badColorEscape : (damageChange > 0 ? goodColorEscape : ""), - abs((short) damageChange), - whiteColorEscape); + new = player.info.accuracy; + newDamage = (theItem->damage.lowerBound + theItem->damage.upperBound) * FP_FACTOR / 2; + if ((theItem->flags & ITEM_IDENTIFIED) || rogue.playbackOmniscience) { + new = new * accuracyFraction(netEnchant(theItem)) / FP_FACTOR; + newDamage = newDamage * damageFraction(netEnchant(theItem)) / FP_FACTOR; } else { - new = 0; + new = new * accuracyFraction(strengthModifier(theItem)) / FP_FACTOR; + newDamage = newDamage * damageFraction(strengthModifier(theItem)) / FP_FACTOR; + } + accuracyChange = (new * 100 / current) - 100; + damageChange = (newDamage * 100 / currentDamage) - 100; + sprintf(buf2, "Wielding the %s%s will %s your current accuracy by %s%i%%%s, and will %s your current damage by %s%i%%%s. ", + theName, + ((theItem->flags & ITEM_IDENTIFIED) || rogue.playbackOmniscience) ? "" : ", assuming it has no hidden properties,", + (((short) accuracyChange) < 0) ? "decrease" : "increase", + (((short) accuracyChange) < 0) ? badColorEscape : (accuracyChange > 0 ? goodColorEscape : ""), + abs((short) accuracyChange), + whiteColorEscape, + (((short) damageChange) < 0) ? "decrease" : "increase", + (((short) damageChange) < 0) ? badColorEscape : (damageChange > 0 ? goodColorEscape : ""), + abs((short) damageChange), + whiteColorEscape); + strcat(buf, buf2); + } else if (theItem->category & ARMOR) { + float armorValue; - if ((theItem->flags & ITEM_IDENTIFIED) || rogue.playbackOmniscience) { - new = theItem->armor; - new += 10 * netEnchant(theItem) / FP_FACTOR; - new /= 10; - } else { - new = armorValueIfUnenchanted(theItem); - } + if ((theItem->flags & ITEM_IDENTIFIED) || rogue.playbackOmniscience) { + armorValue = theItem->armor; + armorValue += 10 * netEnchant(theItem) / FP_FACTOR; + armorValue /= 10; + } else { + armorValue = armorValueIfUnenchanted(theItem); + } - new = max(0, new); + armorValue = max(0, armorValue); + // If `armorValue` should end in 0.25 or 0.75, the last digit is truncated, so re-add it here. + float armorValueFractionalPart = armorValue - floor(armorValue); + if (fabs(armorValueFractionalPart - 0.2) < 1.0e-5 || fabs(armorValueFractionalPart - 0.7) < 1.0e-5) { + armorValue += 0.05; + } - sprintf(buf2, "Wearing the %s%s will result in an armor rating of %s%i%s. ", + if (theItem->flags & ITEM_EQUIPPED) { + sprintf(buf2, "%s has an armor rating of %s%g%s. ", + ((theItem->flags & ITEM_IDENTIFIED) || rogue.playbackOmniscience) ? "It" : "If it has no hidden properties, it", + armorValue > 0 ? goodColorEscape : badColorEscape, + armorValue, + whiteColorEscape); + } else { + sprintf(buf2, "Wearing the %s%s will result in an armor rating of %s%g%s. ", theName, ((theItem->flags & ITEM_IDENTIFIED) || rogue.playbackOmniscience) ? "" : ", assuming it has no hidden properties,", - (new > displayedArmorValue() ? goodColorEscape : (new < displayedArmorValue() ? badColorEscape : whiteColorEscape)), - new, whiteColorEscape); + (armorValue > displayedArmorValue() ? goodColorEscape : (armorValue < displayedArmorValue() ? badColorEscape : whiteColorEscape)), + armorValue, whiteColorEscape); } strcat(buf, buf2); } @@ -3185,16 +3198,16 @@ void updateEncumbrance() { } // Estimates the armor value of the given item, assuming the item is unenchanted. -short armorValueIfUnenchanted(item *theItem) { - short averageValue = (armorTable[theItem->kind].range.upperBound + armorTable[theItem->kind].range.lowerBound) / 2; - short strengthAdjusted = averageValue + 10 * strengthModifier(theItem) / FP_FACTOR; +float armorValueIfUnenchanted(item *theItem) { + float averageValue = (armorTable[theItem->kind].range.upperBound + armorTable[theItem->kind].range.lowerBound) / 2; + float strengthAdjusted = averageValue + 10 * strengthModifier(theItem) / FP_FACTOR; return max(0, strengthAdjusted / 10); } // Calculates the armor value to display to the player (estimated if the item is unidentified). -short displayedArmorValue() { +float displayedArmorValue() { if (!rogue.armor || (rogue.armor->flags & ITEM_IDENTIFIED)) { - return player.info.defense / 10; + return ((float) player.info.defense) / 10; } else { return armorValueIfUnenchanted(rogue.armor); } diff --git a/src/brogue/PowerTables.c b/src/brogue/PowerTables.c index 3d0ae989..eb5f68de 100644 --- a/src/brogue/PowerTables.c +++ b/src/brogue/PowerTables.c @@ -199,7 +199,21 @@ fixpt defenseFraction(fixpt netDefense) { 469, 453, 439, 425, 411, 398, 385, 373, 361, 349, 338, 327, 316, 306, 296, 287, 277, 268, 260, 251, 243, 235, 228, 221, 213, 207, 200, 193, 187, 181, 175, 170, 164, 159, 154, 149, 144, 139, 135, 130, 126, 122, 118, 114, 111, 107, 104, 100, 97, 94}; - short idx = clamp(netDefense * 4 / 10 / FP_FACTOR + 80, 0, LAST_INDEX(POW_DEFENSE_FRACTION)); + /* Examples of what this code does for different armor values: + (Using floating point instead of fixed point, and using small armor values, for clarity) + An armor value of 1.5 results in a netDefense is 15.0. + An armor value of 1.75 results in a netDefense of 17.0 (the 0.5 is lost before this function is called). + It multiplies by 4, so the last decimal place would be zero if it weren't for the 0.5 being lost (as above). + 15.0 -> 60.0; 17.0 -> 68.0 (this would be 70 if not for the truncation) + It divides by 10: 60.0 -> 6.0, 68.0 -> 6.8 + It adds 0.5 to compensate for the truncation: 6.0 -> 6.5; 6.8 -> 7.3 + This could add anywhere from 0.2 to 0.99, but 0.5 seems like the least arbitrary choice. + Without this, the step below would round both 6.0 and 6.8 down to 6. + It converts to an integer (which rounds towards zero): 6.5 -> 6; 7.3 -> 7 + It adds 80 (because some of the entries are for negative armor values) and clamps to the size of the table + This is the index into the table above. + */ + short idx = clamp((netDefense * 4 / 10 + FP_FACTOR / 2) / FP_FACTOR + 80, 0, LAST_INDEX(POW_DEFENSE_FRACTION)); return POW_DEFENSE_FRACTION[idx]; } diff --git a/src/brogue/Rogue.h b/src/brogue/Rogue.h index 442c1a9d..d1241851 100644 --- a/src/brogue/Rogue.h +++ b/src/brogue/Rogue.h @@ -3314,8 +3314,8 @@ extern "C" { boolean itemIsHeavyWeapon(const item *theItem); boolean itemIsPositivelyEnchanted(const item *theItem); void updateEncumbrance(void); - short displayedArmorValue(void); - short armorValueIfUnenchanted(item *theItem); + float displayedArmorValue(); + float armorValueIfUnenchanted(item *theItem); void strengthCheck(item *theItem, boolean noisy); void recalculateEquipmentBonuses(void); boolean equipItem(item *theItem, boolean force, item *unequipHint);