/** * Add power for modifiers */ static int modifier_power(const object_type *obj, int p, bool known) { int i, k = 1, extra_stat_bonus = 0, q; for (i = 0; i < OBJ_MOD_MAX; i++) { if (known || object_this_mod_is_visible(obj, i)) { k = obj->modifiers[i]; extra_stat_bonus += (k * mod_mult(i)); } else continue; if (mod_power(i)) { q = (k * mod_power(i) * mod_slot_mult(i, wield_slot(obj))); p += q; if (q) log_obj(format("Add %d power for %d %s, total is %d\n", q, k, mod_name(i), p)); } } /* Add extra power term if there are a lot of ability bonuses */ if (extra_stat_bonus > 249) { log_obj(format("Inhibiting - Total ability bonus of %d is too high\n", extra_stat_bonus)); p += INHIBIT_POWER; } else if (extra_stat_bonus > 0) { q = ability_power[extra_stat_bonus / 10]; if (!q) return p; p += q; log_obj(format("Add %d power for modifier total of %d, total is %d\n", q, extra_stat_bonus, p)); } return p; }
/** * Describe combat properties of an item - damage dice, to-hit, to-dam, armor * class, missile multipler */ static size_t obj_desc_combat(const struct object *obj, char *buf, size_t max, size_t end, bool spoil) { bitflag flags_known[OF_SIZE]; object_flags_known(obj, flags_known); if (kf_has(obj->kind->kind_flags, KF_SHOW_DICE)) { /* Only display the real damage dice if the combat stats are known */ if (spoil || object_attack_plusses_are_visible(obj)) strnfcat(buf, max, &end, " (%dd%d)", obj->dd, obj->ds); else strnfcat(buf, max, &end, " (%dd%d)", obj->kind->dd, obj->kind->ds); } if (kf_has(obj->kind->kind_flags, KF_SHOW_MULT)) { /* Display shooting power as part of the multiplier */ if ((obj->modifiers[OBJ_MOD_MIGHT] > 0) && (spoil || object_this_mod_is_visible(obj, OBJ_MOD_MIGHT))) strnfcat(buf, max, &end, " (x%d)", obj->pval + obj->modifiers[OBJ_MOD_MIGHT]); else strnfcat(buf, max, &end, " (x%d)", obj->pval); } /* Show weapon bonuses */ if (spoil || object_attack_plusses_are_visible(obj)) { if (tval_is_weapon(obj) || obj->to_d || obj->to_h) { /* Make an exception for body armor with only a to-hit penalty */ if (obj->to_h < 0 && obj->to_d == 0 && tval_is_body_armor(obj)) strnfcat(buf, max, &end, " (%+d)", obj->to_h); /* Otherwise, always use the full tuple */ else strnfcat(buf, max, &end, " (%+d,%+d)", obj->to_h, obj->to_d); } } /* Show armor bonuses */ if (spoil || object_defence_plusses_are_visible(obj)) { if (obj_desc_show_armor(obj)) strnfcat(buf, max, &end, " [%d,%+d]", obj->ac, obj->to_a); else if (obj->to_a) strnfcat(buf, max, &end, " [%+d]", obj->to_a); } else if (obj_desc_show_armor(obj)) strnfcat(buf, max, &end, " [%d]", object_was_sensed(obj) ? obj->ac : obj->kind->ac); return end; }
/** * Add power for extra might */ static int extra_might_power(const object_type *obj, int p, int mult, bool known) { if (known || object_this_mod_is_visible(obj, OBJ_MOD_MIGHT)) { if (obj->modifiers[OBJ_MOD_MIGHT] >= INHIBIT_MIGHT) { p += INHIBIT_POWER; log_obj("INHIBITING - too much extra might - quitting\n"); return p; } else { mult += obj->modifiers[OBJ_MOD_MIGHT]; } log_obj(format("Mult after extra might is %d\n", mult)); } p *= mult; log_obj(format("After multiplying power for might, total is %d\n", p)); return p; }
/** * Add power for extra shots - note that we cannot handle negative shots */ static int extra_shots_power(const object_type *obj, int p, bool known) { if (obj->modifiers[OBJ_MOD_SHOTS] == 0) return p; if (known || object_this_mod_is_visible(obj, OBJ_MOD_SHOTS)) { if (obj->modifiers[OBJ_MOD_SHOTS] >= INHIBIT_SHOTS) { p += INHIBIT_POWER; log_obj("INHIBITING - too many extra shots - quitting\n"); return p; } else if (obj->modifiers[OBJ_MOD_SHOTS] > 0) { int q = obj->modifiers[OBJ_MOD_SHOTS]; p = p * (1 + q); log_obj(format("Multiplying power by %d for extra shots, total is %d\n", 1 + q, p)); } } return p; }
/** * Describe stat modifications. */ static bool describe_stats(textblock *tb, const struct object *obj, oinfo_detail_t mode) { size_t count = 0, i; bool detail = false; /* Don't give exact plusses for faked ego items as each real one will * be different */ bool suppress_details = mode & OINFO_EGO ? true : false; /* Fact of but not size of mods is known for egos and flavoured items * the player is aware of */ bool known_effect = false; if (object_ego_is_visible(obj)) known_effect = true; if (tval_can_have_flavor_k(obj->kind) && object_flavor_is_aware(obj)) known_effect = true; /* See what we've got */ for (i = 0; i < N_ELEMENTS(mod_flags); i++) if (obj->modifiers[mod_flags[i].flag] != 0 && mod_flags[i].name[0]) { count++; /* Either all mods are visible, or none are */ if (object_this_mod_is_visible(obj, mod_flags[i].flag)) detail = true; } if (!count) return false; for (i = 0; i < N_ELEMENTS(mod_flags); i++) { const char *desc = mod_flags[i].name; int val = obj->modifiers[mod_flags[i].flag]; if (!val) continue; if (!mod_flags[i].name[0]) continue; if (detail && !suppress_details) { int attr = (val > 0) ? COLOUR_L_GREEN : COLOUR_RED; textblock_append_c(tb, attr, "%+i %s.\n", val, desc); } else if (known_effect) textblock_append(tb, "Affects your %s\n", desc); } return true; }
/** * Describe numerical modifiers to stats and other player qualities which * allow numerical bonuses - speed, stealth, etc */ static size_t obj_desc_mods(const struct object *obj, char *buf, size_t max, size_t end, bool spoil) { int i, j, num_mods = 0; int mods[OBJ_MOD_MAX] = { 0 }; /* Run through possible modifiers and store distinct ones */ for (i = 0; i < OBJ_MOD_MAX; i++) { /* Check for known non-zero mods */ if ((spoil || object_this_mod_is_visible(obj, i)) && (obj->modifiers[i] != 0)) { /* If no mods stored yet, store and move on */ if (!num_mods) { mods[num_mods++] = obj->modifiers[i]; continue; } /* Run through the existing mods, quit on duplicates */ for (j = 0; j < num_mods; j++) if (mods[j] == obj->modifiers[i]) break; /* Add another mod if needed */ if (j == num_mods) mods[num_mods++] = obj->modifiers[i]; } } if (!num_mods) return end; /* Print the modifiers */ strnfcat(buf, max, &end, " <"); for (j = 0; j < num_mods; j++) { if (j) strnfcat(buf, max, &end, ", "); strnfcat(buf, max, &end, "%+d", mods[j]); } strnfcat(buf, max, &end, ">"); return end; }
/** * Add power for extra blows */ static int extra_blows_power(const object_type *obj, int p, bool known) { int q = p; if (obj->modifiers[OBJ_MOD_BLOWS] == 0) return p; if (known || object_this_mod_is_visible(obj, OBJ_MOD_BLOWS)) { if (obj->modifiers[OBJ_MOD_BLOWS] >= INHIBIT_BLOWS) { p += INHIBIT_POWER; log_obj("INHIBITING - too many extra blows - quitting\n"); return p; } else { p = p * (MAX_BLOWS + obj->modifiers[OBJ_MOD_BLOWS]) / MAX_BLOWS; /* Add boost for assumed off-weapon damage */ p += (NONWEAP_DAMAGE * obj->modifiers[OBJ_MOD_BLOWS] * DAMAGE_POWER / 2); log_obj(format("Add %d power for extra blows, total is %d\n", p - q, p)); } } return p; }
/** * Describe stat modifications. */ static bool describe_stats(textblock *tb, const struct object *obj, oinfo_detail_t mode) { size_t count = 0, i; bool detail = FALSE; /* Don't give exact pluses for faked ego items as each real one will be different */ bool suppress_details = mode & OINFO_EGO ? TRUE : FALSE; /* See what we've got */ for (i = 0; i < N_ELEMENTS(mod_flags); i++) if (obj->modifiers[mod_flags[i].flag] != 0 && mod_flags[i].name[0]) { count++; /* Either all mods are visible, or none are */ if (object_this_mod_is_visible(obj, i)) detail = TRUE; } if (!count) return FALSE; for (i = 0; i < N_ELEMENTS(mod_flags); i++) { const char *desc = mod_flags[i].name; int val = obj->modifiers[mod_flags[i].flag]; if (!val) continue; if (!mod_flags[i].name[0]) continue; if (detail && !suppress_details) { int attr = (val > 0) ? COLOUR_L_GREEN : COLOUR_RED; textblock_append_c(tb, attr, "%+i %s.\n", val, desc); } else textblock_append(tb, "Affects your %s\n", desc); } return TRUE; }
/** * Returns information about objects that can be used for digging. * * `deciturns` will be filled in with the avg number of deciturns it will * take to dig through each type of diggable terrain, and must be at least * [DIGGING_MAX]. * * Returns false if the object has no effect on digging, or if the specifics * are meaningless (i.e. the object is an ego template, not a real item). */ static bool obj_known_digging(struct object *obj, int deciturns[]) { struct player_state state; int i; int chances[DIGGING_MAX]; int slot = wield_slot(obj); struct object *current = slot_object(player, slot); /* Doesn't remotely resemble a digger */ if (!tval_is_wearable(obj) || (!tval_is_melee_weapon(obj) && (obj->modifiers[OBJ_MOD_TUNNEL] <= 0))) return false; /* Player has no digging info */ if (!object_this_mod_is_visible(obj, OBJ_MOD_TUNNEL)) return false; /* Pretend we're wielding the object */ player->body.slots[slot].obj = obj; /* Calculate the player's hypothetical state */ calc_bonuses(player, &state, true, false); /* Stop pretending */ player->body.slots[slot].obj = current; calc_digging_chances(&state, chances); /* Digging chance is out of 1600 */ for (i = DIGGING_RUBBLE; i < DIGGING_MAX; i++) { int chance = MIN(1600, chances[i]); deciturns[i] = chance ? (16000 / chance) : 0; } return true; }
/** * Gets information about the number of blows possible for the player with * the given object. * * Fills in whether the object is too_heavy to wield effectively, * and the possible_blows[] information of .str_plus and .dex_plus needed * to achieve the approximate number of blows in centiblows. * * `max_blows` must be at least 1 to hold the current number of blows * `possible_blows` must be at least [`max_blows`] in size, and will be limited * to that number of entries. The theoretical maximum is STAT_RANGE * 2 if * an extra blow/speed boost was given for each combination of STR and DEX. * * Returns the number of entries made in the possible_blows[] table, or 0 * if the object is not a weapon. * * Note that the results are meaningless if called on a fake ego object as * the actual ego may have different properties. */ static int obj_known_blows(const struct object *obj, int max_num, struct blow_info possible_blows[]) { int str_plus, dex_plus, old_blows = 0, new_blows, extra_blows; int str_faster = -1, str_done = -1; int dex_plus_bound; int str_plus_bound; int i; struct player_state state; int weapon_slot = slot_by_name(player, "weapon"); struct object *current_weapon = slot_object(player, weapon_slot); int num = 0; /* Not a weapon - no blows! */ if (!tval_is_melee_weapon(obj)) return 0; /* Pretend we're wielding the object */ player->body.slots[weapon_slot].obj = (struct object *) obj; /* Calculate the player's hypothetical state */ calc_bonuses(player, &state, true, false); /* Stop pretending */ player->body.slots[weapon_slot].obj = current_weapon; /* First entry is always the current num of blows. */ possible_blows[num].str_plus = 0; possible_blows[num].dex_plus = 0; possible_blows[num].centiblows = state.num_blows; num++; /* Check to see if extra STR or DEX would yield extra blows */ old_blows = state.num_blows; extra_blows = 0; /* Start with blows from the weapon being examined */ if (object_this_mod_is_visible(obj, OBJ_MOD_BLOWS)) extra_blows += obj->modifiers[OBJ_MOD_BLOWS]; /* Then we need to look for extra blows on other items, as * state does not track these */ for (i = 0; i < player->body.count; i++) { struct object *helper = slot_object(player, i); if ((i == slot_by_name(player, "weapon")) || !helper || !helper->kind) continue; if (object_this_mod_is_visible(helper, OBJ_MOD_BLOWS)) extra_blows += helper->modifiers[OBJ_MOD_BLOWS]; } dex_plus_bound = STAT_RANGE - state.stat_ind[STAT_DEX]; str_plus_bound = STAT_RANGE - state.stat_ind[STAT_STR]; /* Then we check for extra "real" blows */ for (dex_plus = 0; dex_plus < dex_plus_bound; dex_plus++) { for (str_plus = 0; str_plus < str_plus_bound; str_plus++) { if (num == max_num) return num; state.stat_ind[STAT_STR] += str_plus; state.stat_ind[STAT_DEX] += dex_plus; new_blows = calc_blows(player, obj, &state, extra_blows); state.stat_ind[STAT_STR] -= str_plus; state.stat_ind[STAT_DEX] -= dex_plus; /* Test to make sure that this extra blow is a * new str/dex combination, not a repeat */ if (((new_blows - new_blows % 10) > (old_blows - old_blows % 10)) && (str_plus < str_done || str_done == -1)) { possible_blows[num].str_plus = str_plus; possible_blows[num].dex_plus = dex_plus; possible_blows[num].centiblows = new_blows / 10; possible_blows[num].centiblows *= 10; num++; str_done = str_plus; break; } /* If the combination doesn't increment * the displayed blows number, it might still * take a little less energy */ if ((new_blows > old_blows) && (str_plus < str_faster || str_faster == -1) && (str_plus < str_done || str_done == -1)) { possible_blows[num].str_plus = str_plus; possible_blows[num].dex_plus = dex_plus; possible_blows[num].centiblows = new_blows; num++; str_faster = str_plus; } } } return num; }
/** * Determine the ignore level of an object, which is similar to its pseudo. * * The main point is when the value is undetermined given current info, * return the maximum possible value. */ byte ignore_level_of(const struct object *obj) { byte value = 0; bitflag f[OF_SIZE], f2[OF_SIZE]; int i; bool negative_mod = FALSE; object_flags_known(obj, f); /* Deal with jewelry specially. */ if (tval_is_jewelry(obj)) { /* CC: average jewelry has at least one known positive modifier */ for (i = 0; i < OBJ_MOD_MAX; i++) if ((object_this_mod_is_visible(obj, i)) && (obj->modifiers[i] > 0)) return IGNORE_AVERAGE; if ((obj->to_h > 0) || (obj->to_d > 0) || (obj->to_a > 0)) return IGNORE_AVERAGE; if ((object_attack_plusses_are_visible(obj) && ((obj->to_h < 0) || (obj->to_d < 0))) || (object_defence_plusses_are_visible(obj) && obj->to_a < 0)) return IGNORE_BAD; return IGNORE_AVERAGE; } /* And lights */ if (tval_is_light(obj)) { create_mask(f2, TRUE, OFID_WIELD, OFT_MAX); if (of_is_inter(f, f2)) return IGNORE_ALL; if ((obj->to_h > 0) || (obj->to_d > 0) || (obj->to_a > 0)) return IGNORE_GOOD; if ((obj->to_h < 0) || (obj->to_d < 0) || (obj->to_a < 0)) return IGNORE_BAD; return IGNORE_AVERAGE; } /* We need to redefine "bad" * At the moment we use "all modifiers known and negative" */ for (i = 0; i < OBJ_MOD_MAX; i++) { if (!object_this_mod_is_visible(obj, i) || (obj->modifiers[i] > 0)) break; if (obj->modifiers[i] < 0) negative_mod = TRUE; } if ((i == OBJ_MOD_MAX) && negative_mod) return IGNORE_BAD; if (object_was_sensed(obj)) { obj_pseudo_t pseudo = object_pseudo(obj); switch (pseudo) { case INSCRIP_AVERAGE: { value = IGNORE_AVERAGE; break; } case INSCRIP_EXCELLENT: { /* have to assume splendid until you have tested it */ if (object_was_worn(obj)) { if (object_high_resist_is_possible(obj)) value = IGNORE_EXCELLENT_NO_SPL; else value = IGNORE_EXCELLENT_NO_HI; } else { value = IGNORE_ALL; } break; } case INSCRIP_SPLENDID: value = IGNORE_ALL; break; case INSCRIP_NULL: case INSCRIP_SPECIAL: value = IGNORE_MAX; break; /* This is the interesting case */ case INSCRIP_STRANGE: case INSCRIP_MAGICAL: { value = IGNORE_GOOD; if ((object_attack_plusses_are_visible(obj) || randcalc_valid(obj->kind->to_h, obj->to_h) || randcalc_valid(obj->kind->to_d, obj->to_d)) && (object_defence_plusses_are_visible(obj) || randcalc_valid(obj->kind->to_a, obj->to_a))) { int isgood = is_object_good(obj); if (isgood > 0) { value = IGNORE_GOOD; } else if (isgood < 0) { value = IGNORE_BAD; } else { value = IGNORE_AVERAGE; } } break; } default: /* do not handle any other possible pseudo values */ assert(0); } } else { if (object_was_worn(obj)) value = IGNORE_EXCELLENT_NO_SPL; /* object would be sensed if it were splendid */ else if (object_is_known_not_artifact(obj)) value = IGNORE_ALL; else value = IGNORE_MAX; } return value; }