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);