/** * 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[]) { player_state state; int i; int chances[DIGGING_MAX]; int slot = wield_slot(obj); struct object *current = slot_object(player, slot); if (!tval_is_wearable(obj) || (!tval_is_melee_weapon(obj) && (obj->modifiers[OBJ_MOD_TUNNEL] <= 0))) return FALSE; /* Pretend we're wielding the object */ player->body.slots[slot].obj = obj; /* Calculate the player's hypothetical state */ calc_bonuses(player->gear, &state, TRUE); /* 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 miscellaneous combat information about the given object. * * Fills in whether there is a special effect when thrown in `thrown effect`, * the `range` in ft (or zero if not ammo), whether the weapon has the * impact flag set, the percentage chance of breakage and whether it is * too heavy to be weilded effectively at the moment. */ static void obj_known_misc_combat(const struct object *obj, bool *thrown_effect, int *range, bool *impactful, int *break_chance, bool *too_heavy) { struct object *bow = equipped_item_by_slot_name(player, "shooting"); bool weapon = tval_is_melee_weapon(obj); bool ammo = (player->state.ammo_tval == obj->tval) && (bow); bitflag f[OF_SIZE]; *thrown_effect = *impactful = *too_heavy = false; *range = *break_chance = 0; get_known_flags(obj, 0, f); if (!weapon && !ammo) { /* Potions can have special text */ if (tval_is_potion(obj) && obj->dd != 0 && obj->ds != 0 && object_flavor_is_aware(obj)) *thrown_effect = true; } if (ammo) *range = 10 * MIN(6 + 2 * player->state.ammo_mult, z_info->max_range); /* Note the impact flag */ *impactful = of_has(f, OF_IMPACT); /* Add breakage chance */ *break_chance = breakage_chance(obj, true); /* Is the weapon too heavy? */ if (weapon) { struct player_state state; int weapon_slot = slot_by_name(player, "weapon"); struct object *current = equipped_item_by_slot_name(player, "weapon"); /* 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; /* Warn about heavy weapons */ *too_heavy = state.heavy_wield; } }
/** * Gets miscellaneous combat information about the given object. * * Fills in whether there is a special effect when thrown in `thrown effect`, * the `range` in ft (or zero if not ammo), the percentage chance of breakage * and whether it is too heavy to be wielded effectively at the moment. */ static void obj_known_misc_combat(const struct object *obj, bool *thrown_effect, int *range, int *break_chance, bool *heavy) { struct object *bow = equipped_item_by_slot_name(player, "shooting"); bool weapon = tval_is_melee_weapon(obj); bool ammo = (player->state.ammo_tval == obj->tval) && (bow); *thrown_effect = *heavy = false; *range = *break_chance = 0; if (!weapon && !ammo) { /* Potions can have special text */ if (tval_is_potion(obj) && obj->dd != 0 && obj->ds != 0 && object_flavor_is_aware(obj)) *thrown_effect = true; } if (ammo) *range = 10 * MIN(6 + 2 * player->state.ammo_mult, z_info->max_range); /* Add breakage chance */ *break_chance = breakage_chance(obj, true); /* Is the weapon too heavy? */ if (weapon) { struct player_state state; int weapon_slot = slot_by_name(player, "weapon"); struct object *current = equipped_item_by_slot_name(player, "weapon"); /* Pretend we're wielding the object */ player->body.slots[weapon_slot].obj = (struct object *) obj; /* Calculate the player's hypothetical state */ memcpy(&state, &player->state, sizeof(state)); state.stat_ind[STAT_STR] = 0; //Hack - NRM state.stat_ind[STAT_DEX] = 0; //Hack - NRM calc_bonuses(player, &state, true, false); /* Stop pretending */ player->body.slots[weapon_slot].obj = current; /* Warn about heavy weapons */ *heavy = state.heavy_wield; } }
/** * 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 (!tval_is_melee_weapon(obj) && !obj->known->modifiers[OBJ_MOD_TUNNEL]) return false; /* Pretend we're wielding the object */ player->body.slots[slot].obj = obj; /* Calculate the player's hypothetical state */ memcpy(&state, &player->state, sizeof(state)); state.stat_ind[STAT_STR] = 0; //Hack - NRM state.stat_ind[STAT_DEX] = 0; //Hack - NRM 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; }
/* * Describe objects that can be used for digging. */ static bool describe_digger(textblock *tb, const object_type *o_ptr, oinfo_detail_t mode) { bool full = mode & OINFO_FULL; player_state st; object_type inven[INVEN_TOTAL]; int sl = wield_slot(o_ptr); int i; bitflag f[OF_SIZE]; int chances[4]; /* These are out of 1600 */ static const char *names[4] = { "rubble", "magma veins", "quartz veins", "granite" }; /* abort if we are a dummy object */ if (mode & OINFO_DUMMY) return FALSE; if (full) object_flags(o_ptr, f); else object_flags_known(o_ptr, f); if (sl < 0 || (sl != INVEN_WIELD && !of_has(f, OF_TUNNEL))) return FALSE; memcpy(inven, p_ptr->inventory, INVEN_TOTAL * sizeof(object_type)); /* * Hack -- if we examine a ring that is worn on the right finger, * we shouldn't put a copy of it on the left finger before calculating * digging skills. */ if (o_ptr != &p_ptr->inventory[INVEN_RIGHT]) inven[sl] = *o_ptr; calc_bonuses(inven, &st, TRUE); chances[0] = st.skills[SKILL_DIGGING] * 8; chances[1] = (st.skills[SKILL_DIGGING] - 10) * 4; chances[2] = (st.skills[SKILL_DIGGING] - 20) * 2; chances[3] = (st.skills[SKILL_DIGGING] - 40) * 1; for (i = 0; i < 4; i++) { int chance = MAX(0, MIN(1600, chances[i])); int decis = chance ? (16000 / chance) : 0; if (i == 0 && chance > 0) { if (sl == INVEN_WIELD) textblock_append(tb, "Clears "); else textblock_append(tb, "With this item, your current weapon clears "); } if (i == 3 || (i != 0 && chance == 0)) textblock_append(tb, "and "); if (chance == 0) { textblock_append_c(tb, TERM_L_RED, "doesn't affect "); textblock_append(tb, "%s.\n", names[i]); break; } textblock_append(tb, "%s in ", names[i]); if (chance == 1600) { textblock_append_c(tb, TERM_L_GREEN, "1 "); } else if (decis < 100) { textblock_append_c(tb, TERM_GREEN, "%d.%d ", decis/10, decis%10); } else { textblock_append_c(tb, (decis < 1000) ? TERM_YELLOW : TERM_RED, "%d ", (decis+5)/10); } textblock_append(tb, "turn%s%s", decis == 10 ? "" : "s", (i == 3) ? ".\n" : ", "); } return TRUE; }
/* * Describe combat advantages. */ static bool describe_combat(textblock *tb, const object_type *o_ptr, oinfo_detail_t mode) { bool full = mode & OINFO_FULL; const char *desc[SL_MAX] = { 0 }; int i; int mult[SL_MAX]; int cnt, dam, total_dam, plus = 0; int xtra_postcrit = 0, xtra_precrit = 0; int crit_mult, crit_div, crit_add; int str_plus, dex_plus, old_blows = 0, new_blows, extra_blows; int str_faster = -1, str_done = -1; object_type *bow = &p_ptr->inventory[INVEN_BOW]; bitflag f[OF_SIZE], tmp_f[OF_SIZE], mask[OF_SIZE]; bool weapon = (wield_slot(o_ptr) == INVEN_WIELD); bool ammo = (p_ptr->state.ammo_tval == o_ptr->tval) && (bow->kind); int multiplier = 1; /* Create the "all slays" mask */ create_mask(mask, FALSE, OFT_SLAY, OFT_KILL, OFT_BRAND, OFT_MAX); /* Abort if we've nothing to say */ if (mode & OINFO_DUMMY) return FALSE; if (!weapon && !ammo) { /* Potions can have special text */ if (o_ptr->tval != TV_POTION || o_ptr->dd == 0 || o_ptr->ds == 0 || !object_flavor_is_aware(o_ptr)) return FALSE; textblock_append(tb, "It can be thrown at creatures with damaging effect.\n"); return TRUE; } if (full) { object_flags(o_ptr, f); } else { object_flags_known(o_ptr, f); } textblock_append_c(tb, TERM_L_WHITE, "Combat info:\n"); if (weapon) { /* * Get the player's hypothetical state, were they to be * wielding this item. */ player_state state; int dex_plus_bound; int str_plus_bound; object_type inven[INVEN_TOTAL]; memcpy(inven, p_ptr->inventory, INVEN_TOTAL * sizeof(object_type)); inven[INVEN_WIELD] = *o_ptr; if (full) object_know_all_flags(&inven[INVEN_WIELD]); calc_bonuses(inven, &state, TRUE); dex_plus_bound = STAT_RANGE - state.stat_ind[A_DEX]; str_plus_bound = STAT_RANGE - state.stat_ind[A_STR]; dam = ((o_ptr->ds + 1) * o_ptr->dd * 5); xtra_postcrit = state.dis_to_d * 10; if (object_attack_plusses_are_visible(o_ptr)) { xtra_precrit += o_ptr->to_d * 10; plus += o_ptr->to_h; } calculate_melee_crits(&state, o_ptr->weight, plus, &crit_mult, &crit_add, &crit_div); /* Warn about heavy weapons */ if (adj_str_hold[state.stat_ind[A_STR]] < o_ptr->weight / 10) textblock_append_c(tb, TERM_L_RED, "You are too weak to use this weapon.\n"); textblock_append_c(tb, TERM_L_GREEN, "%d.%d ", state.num_blows / 100, (state.num_blows / 10) % 10); textblock_append(tb, "blow%s/round.\n", (state.num_blows > 100) ? "s" : ""); /* Check to see if extra STR or DEX would yield extra blows */ old_blows = state.num_blows; extra_blows = 0; /* First we need to look for extra blows on other items, as * state does not track these */ for (i = INVEN_BOW; i < INVEN_TOTAL; i++) { if (!p_ptr->inventory[i].kind) continue; object_flags_known(&p_ptr->inventory[i], tmp_f); if (of_has(tmp_f, OF_BLOWS)) extra_blows += p_ptr->inventory[i].pval[which_pval(&p_ptr->inventory[i], OF_BLOWS)]; } /* Then we add blows from the weapon being examined */ if (of_has(f, OF_BLOWS)) extra_blows += o_ptr->pval[which_pval(o_ptr, OF_BLOWS)]; /* 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++) { state.stat_ind[A_STR] += str_plus; state.stat_ind[A_DEX] += dex_plus; new_blows = calc_blows(o_ptr, &state, extra_blows); /* 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)) { textblock_append(tb, "With +%d STR and +%d DEX you would get %d.%d blows\n", str_plus, dex_plus, (new_blows / 100), (new_blows / 10) % 10); state.stat_ind[A_STR] -= str_plus; state.stat_ind[A_DEX] -= dex_plus; 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)) { textblock_append(tb, "With +%d STR and +%d DEX you would attack a bit faster\n", str_plus, dex_plus); state.stat_ind[A_STR] -= str_plus; state.stat_ind[A_DEX] -= dex_plus; str_faster = str_plus; continue; } state.stat_ind[A_STR] -= str_plus; state.stat_ind[A_DEX] -= dex_plus; } } } else { int tdis = 6 + 2 * p_ptr->state.ammo_mult; if (object_attack_plusses_are_visible(o_ptr)) plus += o_ptr->to_h; calculate_missile_crits(&p_ptr->state, o_ptr->weight, plus, &crit_mult, &crit_add, &crit_div); /* Calculate damage */ dam = ((o_ptr->ds + 1) * o_ptr->dd * 5); if (object_attack_plusses_are_visible(o_ptr)) dam += (o_ptr->to_d * 10); if (object_attack_plusses_are_visible(bow)) dam += (bow->to_d * 10); /* Apply brands/slays from the shooter to the ammo, but only if known * Note that this is not dependent on mode, so that viewing shop-held * ammo (fully known) does not leak information about launcher */ object_flags_known(bow, tmp_f); of_union(f, tmp_f); textblock_append(tb, "Hits targets up to "); textblock_append_c(tb, TERM_L_GREEN, format("%d", tdis * 10)); textblock_append(tb, " feet away.\n"); } /* Collect slays */ /* Melee weapons get slays and brands from other items now */ if (weapon) { bool nonweap = FALSE; for (i = INVEN_LEFT; i < INVEN_TOTAL; i++) { if (!p_ptr->inventory[i].kind) continue; object_flags_known(&p_ptr->inventory[i], tmp_f); of_inter(tmp_f, mask); /* strip out non-slays */ if (of_union(f, tmp_f)) nonweap = TRUE; } if (nonweap) textblock_append(tb, "This weapon may benefit from one or more off-weapon brands or slays.\n"); } textblock_append(tb, "Average damage/round: "); if (ammo) multiplier = p_ptr->state.ammo_mult; cnt = list_slays(f, mask, desc, NULL, mult, TRUE); for (i = 0; i < cnt; i++) { int melee_adj_mult = ammo ? 0 : 1; /* ammo mult adds fully, melee mult is times 1, so adds 1 less */ /* Include bonus damage and slay in stated average */ total_dam = dam * (multiplier + mult[i] - melee_adj_mult) + xtra_precrit; total_dam = (total_dam * crit_mult + crit_add) / crit_div; total_dam += xtra_postcrit; if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= p_ptr->state.num_shots; if (total_dam <= 0) textblock_append_c(tb, TERM_L_RED, "%d", 0); else if (total_dam % 10) textblock_append_c(tb, TERM_L_GREEN, "%d.%d", total_dam / 10, total_dam % 10); else textblock_append_c(tb, TERM_L_GREEN, "%d", total_dam / 10); textblock_append(tb, " vs. %s, ", desc[i]); } if (cnt) textblock_append(tb, "and "); /* Include bonus damage in stated average */ total_dam = dam * multiplier + xtra_precrit; total_dam = (total_dam * crit_mult + crit_add) / crit_div; total_dam += xtra_postcrit; if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= p_ptr->state.num_shots; if (total_dam <= 0) textblock_append_c(tb, TERM_L_RED, "%d", 0); else if (total_dam % 10) textblock_append_c(tb, TERM_L_GREEN, "%d.%d", total_dam / 10, total_dam % 10); else textblock_append_c(tb, TERM_L_GREEN, "%d", total_dam / 10); if (cnt) textblock_append(tb, " vs. others"); textblock_append(tb, ".\n"); /* Note the impact flag */ if (of_has(f, OF_IMPACT)) textblock_append(tb, "Sometimes creates earthquakes on impact.\n"); /* Add breakage chance */ if (ammo) { int chance = breakage_chance(o_ptr, TRUE); textblock_append_c(tb, TERM_L_GREEN, "%d%%", chance); textblock_append(tb, " chance of breaking upon contact.\n"); } /* You always have something to say... */ return TRUE; }
/* * Describe combat advantages. */ static bool describe_combat(textblock *tb, const object_type *o_ptr, oinfo_detail_t mode) { bool full = mode & OINFO_FULL; object_type *bow = &p_ptr->inventory[INVEN_BOW]; bitflag f[OF_SIZE]; bool weapon = (wield_slot(o_ptr) == INVEN_WIELD); bool ammo = (p_ptr->state.ammo_tval == o_ptr->tval) && (bow->kind); /* The player's hypothetical state, were they to wield this item */ player_state state; /* Abort if we've nothing to say */ if (mode & OINFO_DUMMY) return FALSE; if (!weapon && !ammo) { /* Potions can have special text */ if (o_ptr->tval != TV_POTION || o_ptr->dd == 0 || o_ptr->ds == 0 || !object_flavor_is_aware(o_ptr)) return FALSE; textblock_append(tb, "It can be thrown at creatures with damaging effect.\n"); return TRUE; } if (full) object_flags(o_ptr, f); else object_flags_known(o_ptr, f); textblock_append_c(tb, TERM_L_WHITE, "Combat info:\n"); if (weapon) { object_type inven[INVEN_TOTAL]; memcpy(inven, p_ptr->inventory, INVEN_TOTAL * sizeof(object_type)); inven[INVEN_WIELD] = *o_ptr; if (full) object_know_all_flags(&inven[INVEN_WIELD]); /* Calculate the player's hypothetical state */ calc_bonuses(inven, &state, TRUE); /* Warn about heavy weapons */ if (adj_str_hold[state.stat_ind[A_STR]] < o_ptr->weight / 10) textblock_append_c(tb, TERM_L_RED, "You are too weak to use this weapon.\n"); /* Describe blows */ describe_blows(tb, o_ptr, state, f); } else { /* Ammo */ /* Range of the weapon */ int tdis = 6 + 2 * p_ptr->state.ammo_mult; /* Output the range */ textblock_append(tb, "Hits targets up to "); textblock_append_c(tb, TERM_L_GREEN, format("%d", tdis * 10)); textblock_append(tb, " feet away.\n"); } /* Describe damage */ describe_damage(tb, o_ptr, state, f, mode); /* Note the impact flag */ if (of_has(f, OF_IMPACT)) textblock_append(tb, "Sometimes creates earthquakes on impact.\n"); /* Add breakage chance */ if (ammo) { int chance = breakage_chance(o_ptr, TRUE); textblock_append_c(tb, TERM_L_GREEN, "%d%%", chance); textblock_append(tb, " chance of breaking upon contact.\n"); } /* Something has been said */ return TRUE; }
/** * Outputs the damage we do/would do with the weapon */ static bool describe_weapon_damage(textblock * tb, const object_type * o_ptr, oinfo_detail_t mode) { object_type *i_ptr; int i, j; bool full = mode & OINFO_FULL; bool terse = mode & OINFO_TERSE; bool first = TRUE; int show_m_tohit; int brand[MAX_P_BRAND], slay[MAX_P_SLAY]; player_state state; object_type inven[INVEN_TOTAL]; /* Abort if we've nothing to say */ if (mode & OINFO_DUMMY) return FALSE; /* Extract the slays and brands */ for (j = 0; j < MAX_P_SLAY; j++) slay[j] = (if_has(o_ptr->id_other, OBJECT_ID_BASE_SLAY + j) || full) ? o_ptr->multiple_slay[j] : MULTIPLE_BASE; for (j = 0; j < MAX_P_BRAND; j++) brand[j] = (if_has(o_ptr->id_other, OBJECT_ID_BASE_BRAND + j) || full) ? o_ptr->multiple_brand[j] : MULTIPLE_BASE; /* Check rings for additional brands (slays) */ for (i = 0; i < 2; i++) { i_ptr = &p_ptr->inventory[INVEN_LEFT + i]; /* If wearing a ring */ if (i_ptr->k_idx) { /* Pick up any brands (and slays!) */ for (j = 0; j < MAX_P_SLAY; j++) slay[j] = MAX(slay[j], ((if_has(i_ptr->id_other, OBJECT_ID_BASE_SLAY + j) || full) ? i_ptr->multiple_slay[j] : MULTIPLE_BASE)); for (j = 0; j < MAX_P_BRAND; j++) brand[j] = MAX(brand[j], ((if_has(i_ptr->id_other, OBJECT_ID_BASE_BRAND + j) || full) ? i_ptr->multiple_brand[j] : MULTIPLE_BASE)); } } /* temporary elemental brands */ if (p_ptr->special_attack & (ATTACK_ACID)) brand[P_BRAND_ACID] = MAX(brand[P_BRAND_ACID], BRAND_BOOST_NORMAL); if (p_ptr->special_attack & (ATTACK_ELEC)) brand[P_BRAND_ELEC] = MAX(brand[P_BRAND_ELEC], BRAND_BOOST_NORMAL); if (p_ptr->special_attack & (ATTACK_FIRE)) brand[P_BRAND_FIRE] = MAX(brand[P_BRAND_FIRE], BRAND_BOOST_NORMAL); if (p_ptr->special_attack & (ATTACK_COLD)) brand[P_BRAND_COLD] = MAX(brand[P_BRAND_COLD], BRAND_BOOST_NORMAL); if (p_ptr->special_attack & (ATTACK_POIS)) brand[P_BRAND_POIS] = MAX(brand[P_BRAND_POIS], BRAND_BOOST_NORMAL); if (p_ptr->special_attack & (ATTACK_HOLY)) slay[P_SLAY_EVIL] = MAX(slay[P_SLAY_EVIL], SLAY_BOOST_SMALL); /* * Get the player's hypothetical state, were they to be * wielding this item (setting irrelevant shield state). */ memcpy(inven, p_ptr->inventory, INVEN_TOTAL * sizeof(object_type)); inven[INVEN_WIELD] = *o_ptr; state.shield_on_back = FALSE; calc_bonuses(inven, &state, TRUE); show_m_tohit = state.dis_to_h; if (if_has(o_ptr->id_other, IF_TO_H) || full) show_m_tohit += o_ptr->to_h; if (terse) { textblock_append_c(tb, TERM_L_GREEN, "%d ", state.num_blow); textblock_append(tb, "blow%s av. dam. ", (state.num_blow) ? "s" : ""); } else { textblock_append(tb, "\nWielding it you would have "); textblock_append_c(tb, TERM_L_GREEN, "%d ", state.num_blow); textblock_append(tb, "blow%s and do an average damage per blow of ", (state.num_blow) ? "s" : ""); } for (i = 0; i < MAX_P_SLAY; i++) { if (slay[i] > MULTIPLE_BASE) output_dam(tb, &state, o_ptr, slay[i], slayee[i], &first, mode); } for (i = 0; i < MAX_P_BRAND; i++) { if (brand[i] > MULTIPLE_BASE) { char buf[40]; strnfmt(buf, sizeof(buf), "non %s resistant creatures", brandee[i]); output_dam(tb, &state, o_ptr, brand[i], buf, &first, mode); } } output_dam(tb, &state, o_ptr, MULTIPLE_BASE, (first) ? "all monsters" : "other monsters", &first, mode); if (terse) { textblock_append(tb, ". + "); textblock_append_c(tb, TERM_L_GREEN, "%d", show_m_tohit); textblock_append(tb, " to skill. "); } else { textblock_append(tb, ". Your + to Skill would be "); textblock_append_c(tb, TERM_L_GREEN, "%d", show_m_tohit); textblock_append(tb, ". "); } return TRUE; }
/** * Gets information about the average damage/turn that can be inflicted if * the player wields the given weapon. * * Fills in the damage against normal adversaries in `normal_damage`, as well * as the slays on the weapon in slay_list[] and corresponding damages in * slay_damage[]. These must both be at least SL_MAX long to be safe. * `nonweap_slay` is set to whether other items being worn could add to the * damage done by branding attacks. * * Returns the number of slays populated in slay_list[] and slay_damage[]. * * Note that the results are meaningless if called on a fake ego object as * the actual ego may have different properties. */ static bool obj_known_damage(const struct object *obj, int *normal_damage, struct brand **brand_list, struct slay **slay_list, bool *nonweap_slay) { int i; int dice, sides, dam, total_dam, plus = 0; int xtra_postcrit = 0, xtra_precrit = 0; int crit_mult, crit_div, crit_add; int old_blows = 0; struct brand *brand; struct slay *slay; struct object *bow = equipped_item_by_slot_name(player, "shooting"); bool weapon = tval_is_melee_weapon(obj); bool ammo = (player->state.ammo_tval == obj->tval) && (bow); int multiplier = 1; struct player_state state; int weapon_slot = slot_by_name(player, "weapon"); struct object *current_weapon = slot_object(player, weapon_slot); /* Pretend we're wielding the object if it's a weapon */ if (weapon) 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; /* Use displayed dice if real dice not known */ if (object_attack_plusses_are_visible(obj)) { dice = obj->dd; sides = obj->ds; } else { dice = obj->kind->dd; sides = obj->kind->ds; } /* Calculate damage */ dam = ((sides + 1) * dice * 5); if (weapon) { xtra_postcrit = state.to_d * 10; if (object_attack_plusses_are_visible(obj)) { xtra_precrit += obj->to_d * 10; plus += obj->to_h; } calculate_melee_crits(&state, obj->weight, plus, &crit_mult, &crit_add, &crit_div); old_blows = state.num_blows; } else { /* Ammo */ if (object_attack_plusses_are_visible(obj)) plus += obj->to_h; calculate_missile_crits(&player->state, obj->weight, plus, &crit_mult, &crit_add, &crit_div); if (object_attack_plusses_are_visible(obj)) dam += (obj->to_d * 10); if (object_attack_plusses_are_visible(bow)) dam += (bow->to_d * 10); } if (ammo) multiplier = player->state.ammo_mult; /* Get the brands */ *brand_list = brand_collect(obj->known->brands, ammo ? bow->known : NULL); /* Get the slays */ *slay_list = slay_collect(obj->known->slays, ammo ? bow->known : NULL); /* Melee weapons may get slays and brands from other items */ *nonweap_slay = false; if (weapon) { for (i = 2; i < player->body.count; i++) { struct object *slot_obj = slot_object(player, i); struct brand *new_brand; struct slay *new_slay; if (!slot_obj) continue; if (slot_obj->known->brands || slot_obj->known->slays) *nonweap_slay = true; else continue; /* Replace the old lists with new ones */ new_brand = brand_collect(*brand_list, slot_obj->known); new_slay = slay_collect(*slay_list, slot_obj->known); free_brand(*brand_list); free_slay(*slay_list); *brand_list = new_brand; *slay_list = new_slay; } } /* Get damage for each brand on the objects */ for (brand = *brand_list; brand; brand = brand->next) { /* ammo mult adds fully, melee mult is times 1, so adds 1 less */ int melee_adj_mult = ammo ? 0 : 1; /* Include bonus damage and slay in stated average */ total_dam = dam * (multiplier + brand->multiplier - melee_adj_mult) + xtra_precrit; total_dam = (total_dam * crit_mult + crit_add) / crit_div; total_dam += xtra_postcrit; if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= player->state.num_shots; brand->damage = total_dam; } /* Get damage for each slay on the objects */ for (slay = *slay_list; slay; slay = slay->next) { /* ammo mult adds fully, melee mult is times 1, so adds 1 less */ int melee_adj_mult = ammo ? 0 : 1; /* Include bonus damage and slay in stated average */ total_dam = dam * (multiplier + slay->multiplier - melee_adj_mult) + xtra_precrit; total_dam = (total_dam * crit_mult + crit_add) / crit_div; total_dam += xtra_postcrit; if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= player->state.num_shots; slay->damage = total_dam; } /* Include bonus damage in stated average */ total_dam = dam * multiplier + xtra_precrit; total_dam = (total_dam * crit_mult + crit_add) / crit_div; total_dam += xtra_postcrit; /* Normal damage, not considering brands or slays */ if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= player->state.num_shots; *normal_damage = total_dam; return (*slay_list || *brand_list); }
/** * 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; }
/* * Handle "p_ptr->update" */ void update_stuff(void) { /* Update stuff */ if (!p_ptr->update) return; if (p_ptr->update & (PU_BODY)) { p_ptr->update &= ~(PU_BODY); calc_body(); } if (p_ptr->update & (PU_BONUS)) { p_ptr->update &= ~(PU_BONUS); calc_bonuses(FALSE); } if (p_ptr->update & (PU_TORCH)) { p_ptr->update &= ~(PU_TORCH); calc_torch(); } if (p_ptr->update & (PU_HP)) { p_ptr->update &= ~(PU_HP); call_lua("player.calc_hitpoints", "()", ""); } if (p_ptr->update & (PU_SANITY)) { p_ptr->update &= ~(PU_SANITY); call_lua("player.calc_sanity", "()", ""); } if (p_ptr->update & (PU_MANA)) { p_ptr->update &= ~(PU_MANA); call_lua("player.calc_mana", "()", ""); } /* Character is not ready yet, no screen updates */ if (!character_generated) return; /* Character is in "icky" mode, no screen updates */ if (character_icky) return; if (p_ptr->update & (PU_UN_VIEW)) { p_ptr->update &= ~(PU_UN_VIEW); forget_view(); } if (p_ptr->update & (PU_VIEW)) { p_ptr->update &= ~(PU_VIEW); update_view(); } if (p_ptr->update & (PU_DISTANCE)) { p_ptr->update &= ~(PU_DISTANCE); p_ptr->update &= ~(PU_MONSTERS); update_monsters(TRUE); } if (p_ptr->update & (PU_MONSTERS)) { p_ptr->update &= ~(PU_MONSTERS); update_monsters(FALSE); } if (p_ptr->update & (PU_MON_LITE)) { p_ptr->update &= ~(PU_MON_LITE); if (monster_lite) update_mon_lite(); } }
/* * Calculate bonuses, and print various things on changes. */ static void update_bonuses(void) { int i; player_state *state = &p_ptr->state; player_state old = p_ptr->state; /*** Calculate bonuses ***/ calc_bonuses(inventory, &p_ptr->state, FALSE); /*** Notice changes ***/ /* Analyze stats */ for (i = 0; i < A_MAX; i++) { /* Notice changes */ if (state->stat_top[i] != old.stat_top[i]) { /* Redisplay the stats later */ p_ptr->redraw |= (PR_STATS); } /* Notice changes */ if (state->stat_use[i] != old.stat_use[i]) { /* Redisplay the stats later */ p_ptr->redraw |= (PR_STATS); } /* Notice changes */ if (state->stat_ind[i] != old.stat_ind[i]) { /* Change in CON affects Hitpoints */ if (i == A_CON) { p_ptr->update |= (PU_HP); } /* Change in INT may affect Mana/Spells */ else if (i == A_INT) { if (cp_ptr->spell_stat == A_INT) { p_ptr->update |= (PU_MANA | PU_SPELLS); } } /* Change in WIS may affect Mana/Spells */ else if (i == A_WIS) { if (cp_ptr->spell_stat == A_WIS) { p_ptr->update |= (PU_MANA | PU_SPELLS); } } } } /* Hack -- Telepathy Change */ if (state->telepathy != old.telepathy) { /* Update monster visibility */ p_ptr->update |= (PU_MONSTERS); } /* Hack -- See Invis Change */ if (state->see_inv != old.see_inv) { /* Update monster visibility */ p_ptr->update |= (PU_MONSTERS); } /* Redraw speed (if needed) */ if (state->speed != old.speed) { /* Redraw speed */ p_ptr->redraw |= (PR_SPEED); } /* Redraw armor (if needed) */ if ((state->dis_ac != old.dis_ac) || (state->dis_to_a != old.dis_to_a)) { /* Redraw */ p_ptr->redraw |= (PR_ARMOR); } /* Hack -- handle "xtra" mode */ if (character_xtra) return; /* Take note when "heavy bow" changes */ if (old.heavy_shoot != state->heavy_shoot) { /* Message */ if (state->heavy_shoot) { msg_print("You have trouble wielding such a heavy bow."); } else if (inventory[INVEN_BOW].k_idx) { msg_print("You have no trouble wielding your bow."); } else { msg_print("You feel relieved to put down your heavy bow."); } } /* Take note when "heavy weapon" changes */ if (old.heavy_wield != state->heavy_wield) { /* Message */ if (state->heavy_wield) { msg_print("You have trouble wielding such a heavy weapon."); } else if (inventory[INVEN_WIELD].k_idx) { msg_print("You have no trouble wielding your weapon."); } else { msg_print("You feel relieved to put down your heavy weapon."); } } /* Take note when "illegal weapon" changes */ if (old.icky_wield != state->icky_wield) { /* Message */ if (state->icky_wield) { msg_print("You do not feel comfortable with your weapon."); } else if (inventory[INVEN_WIELD].k_idx) { msg_print("You feel comfortable with your weapon."); } else { msg_print("You feel more comfortable after removing your weapon."); } } }
/* Scrolls for the reading -RAK- */ void read_scroll() { int32u i; int j, k, item_val, y, x; int tmp[6], flag, used_up; bigvtype out_val, tmp_str; register int ident, l; register inven_type *i_ptr; register struct misc *m_ptr; free_turn_flag = TRUE; if (py.flags.blind > 0) msg_print("You can't see to read the scroll."); else if (no_light()) msg_print("You have no light to read by."); else if (py.flags.confused > 0) msg_print("You are too confused to read a scroll."); else if (inven_ctr == 0) msg_print("You are not carrying anything!"); else if (!find_range(TV_SCROLL1, TV_SCROLL2, &j, &k)) msg_print ("You are not carrying any scrolls!"); else if (get_item(&item_val, "Read which scroll?", j, k, 0)) { i_ptr = &inventory[item_val]; free_turn_flag = FALSE; used_up = TRUE; i = i_ptr->flags; ident = FALSE; while (i != 0) { j = bit_pos(&i) + 1; if (i_ptr->tval == TV_SCROLL2) j += 32; /* Scrolls. */ switch(j) { case 1: i_ptr = &inventory[INVEN_WIELD]; if (i_ptr->tval != TV_NOTHING) { objdes(tmp_str, i_ptr, FALSE); (void) sprintf(out_val, "Your %s glows faintly!", tmp_str); msg_print(out_val); if (enchant(&i_ptr->tohit)) { i_ptr->flags &= ~TR_CURSED; calc_bonuses(); } else msg_print("The enchantment fails."); ident = TRUE; } break; case 2: i_ptr = &inventory[INVEN_WIELD]; if (i_ptr->tval != TV_NOTHING) { objdes(tmp_str, i_ptr, FALSE); (void) sprintf(out_val, "Your %s glows faintly!", tmp_str); msg_print(out_val); if (enchant(&i_ptr->todam)) { i_ptr->flags &= ~TR_CURSED; calc_bonuses (); } else msg_print("The enchantment fails."); ident = TRUE; } break; case 3: k = 0; l = 0; if (inventory[INVEN_BODY].tval != TV_NOTHING) tmp[k++] = INVEN_BODY; if (inventory[INVEN_ARM].tval != TV_NOTHING) tmp[k++] = INVEN_ARM; if (inventory[INVEN_OUTER].tval != TV_NOTHING) tmp[k++] = INVEN_OUTER; if (inventory[INVEN_HANDS].tval != TV_NOTHING) tmp[k++] = INVEN_HANDS; if (inventory[INVEN_HEAD].tval != TV_NOTHING) tmp[k++] = INVEN_HEAD; /* also enchant boots */ if (inventory[INVEN_FEET].tval != TV_NOTHING) tmp[k++] = INVEN_FEET; if (k > 0) l = tmp[randint(k)-1]; if (TR_CURSED & inventory[INVEN_BODY].flags) l = INVEN_BODY; else if (TR_CURSED & inventory[INVEN_ARM].flags) l = INVEN_ARM; else if (TR_CURSED & inventory[INVEN_OUTER].flags) l = INVEN_OUTER; else if (TR_CURSED & inventory[INVEN_HEAD].flags) l = INVEN_HEAD; else if (TR_CURSED & inventory[INVEN_HANDS].flags) l = INVEN_HANDS; else if (TR_CURSED & inventory[INVEN_FEET].flags) l = INVEN_FEET; if (l > 0) { i_ptr = &inventory[l]; objdes(tmp_str, i_ptr, FALSE); (void) sprintf(out_val, "Your %s glows faintly!", tmp_str); msg_print(out_val); if (enchant(&i_ptr->toac)) { i_ptr->flags &= ~TR_CURSED; calc_bonuses (); } else msg_print("The enchantment fails."); ident = TRUE; } break; case 4: msg_print("This is an identify scroll."); ident = TRUE; used_up = ident_spell(); /* the identify may merge objects, causing the identify scroll to move to a different place. Check for that here. */ if (i_ptr->tval != TV_SCROLL1 || i_ptr->flags != 0x00000008) { item_val--; i_ptr = &inventory[item_val]; if (i_ptr->tval != TV_SCROLL1 || i_ptr->flags != 0x00000008) { msg_print("internal error with identify spell."); msg_print("Please tell the wizard!"); return; } } break; case 5: if (remove_curse()) { msg_print("You feel as if someone is watching over you."); ident = TRUE; } break; case 6: ident = light_area(char_row, char_col); break; case 7: for (k = 0; k < randint(3); k++) { y = char_row; x = char_col; ident |= summon_monster(&y, &x, FALSE); } break; case 8: teleport(10); ident = TRUE; break; case 9: teleport(100); ident = TRUE; break; case 10: (void) tele_level(); ident = TRUE; break; case 11: if (py.flags.confuse_monster == 0) { msg_print("Your hands begin to glow."); py.flags.confuse_monster = TRUE; ident = TRUE; } break; case 12: ident = TRUE; map_area(); break; case 13: ident = sleep_monsters1(char_row, char_col); break; case 14: ident = TRUE; warding_glyph(); break; case 15: ident = detect_treasure(); break; case 16: ident = detect_object(); break; case 17: ident = detect_trap(); break; case 18: ident = detect_sdoor(); break; case 19: msg_print("This is a mass genocide scroll."); ident = mass_genocide(TRUE); break; case 20: ident = detect_invisible(); break; case 21: ident = aggravate_monster(20); if (ident) msg_print("There is a high pitched humming noise."); break; case 22: ident = trap_creation(); break; case 23: ident = td_destroy(); break; case 24: /* Not Used , used to be door creation */ break; case 25: msg_print("This is a Recharge-Item scroll."); ident = TRUE; used_up = recharge(60); break; case 26: msg_print("This is a genocide scroll."); ident = genocide(TRUE); break; case 27: ident = unlight_area(char_row, char_col); break; case 28: ident = protect_evil(); break; case 29: ident = TRUE; create_food(); break; case 30: ident = dispel_creature(UNDEAD, 60); break; case 31: remove_all_curse(); ident = TRUE; break; case 33: i_ptr = &inventory[INVEN_WIELD]; if (i_ptr->tval != TV_NOTHING) { objdes(tmp_str, i_ptr, FALSE); (void) sprintf(out_val, "Your %s glows brightly!", tmp_str); msg_print(out_val); flag = FALSE; for (k = 0; k < randint(2); k++) if (enchant(&i_ptr->tohit)) flag = TRUE; for (k = 0; k < randint(2); k++) if (enchant(&i_ptr->todam)) flag = TRUE; if (flag) { i_ptr->flags &= ~TR_CURSED; calc_bonuses (); } else msg_print("The enchantment fails."); ident = TRUE; } break; case 34: i_ptr = &inventory[INVEN_WIELD]; if (i_ptr->tval != TV_NOTHING) { objdes(tmp_str, i_ptr, FALSE); (void)sprintf(out_val,"Your %s glows black, fades.",tmp_str); msg_print(out_val); unmagic_name(i_ptr); i_ptr->tohit = -randint(5) - randint(5); i_ptr->todam = -randint(5) - randint(5); i_ptr->flags = TR_CURSED; py_bonuses(i_ptr, -1); calc_bonuses (); ident = TRUE; } break; case 35: k = 0; l = 0; if (inventory[INVEN_BODY].tval != TV_NOTHING) tmp[k++] = INVEN_BODY; if (inventory[INVEN_ARM].tval != TV_NOTHING) tmp[k++] = INVEN_ARM; if (inventory[INVEN_OUTER].tval != TV_NOTHING) tmp[k++] = INVEN_OUTER; if (inventory[INVEN_HANDS].tval != TV_NOTHING) tmp[k++] = INVEN_HANDS; if (inventory[INVEN_HEAD].tval != TV_NOTHING) tmp[k++] = INVEN_HEAD; /* also enchant boots */ if (inventory[INVEN_FEET].tval != TV_NOTHING) tmp[k++] = INVEN_FEET; if (k > 0) l = tmp[randint(k)-1]; if (TR_CURSED & inventory[INVEN_BODY].flags) l = INVEN_BODY; else if (TR_CURSED & inventory[INVEN_ARM].flags) l = INVEN_ARM; else if (TR_CURSED & inventory[INVEN_OUTER].flags) l = INVEN_OUTER; else if (TR_CURSED & inventory[INVEN_HEAD].flags) l = INVEN_HEAD; else if (TR_CURSED & inventory[INVEN_HANDS].flags) l = INVEN_HANDS; else if (TR_CURSED & inventory[INVEN_FEET].flags) l = INVEN_FEET; if (l > 0) { i_ptr = &inventory[l]; objdes(tmp_str, i_ptr, FALSE); (void) sprintf(out_val,"Your %s glows brightly!", tmp_str); msg_print(out_val); flag = FALSE; for (k = 0; k < randint(2) + 1; k++) if (enchant(&i_ptr->toac)) flag = TRUE; if (flag) { i_ptr->flags &= ~TR_CURSED; calc_bonuses (); } else msg_print("The enchantment fails."); ident = TRUE; } break; case 36: if ((inventory[INVEN_BODY].tval != TV_NOTHING) && (randint(4) == 1)) k = INVEN_BODY; else if ((inventory[INVEN_ARM].tval != TV_NOTHING) && (randint(3) ==1)) k = INVEN_ARM; else if ((inventory[INVEN_OUTER].tval != TV_NOTHING) && (randint(3) ==1)) k = INVEN_OUTER; else if ((inventory[INVEN_HEAD].tval != TV_NOTHING) && (randint(3) ==1)) k = INVEN_HEAD; else if ((inventory[INVEN_HANDS].tval != TV_NOTHING) && (randint(3) ==1)) k = INVEN_HANDS; else if ((inventory[INVEN_FEET].tval != TV_NOTHING) && (randint(3) ==1)) k = INVEN_FEET; else if (inventory[INVEN_BODY].tval != TV_NOTHING) k = INVEN_BODY; else if (inventory[INVEN_ARM].tval != TV_NOTHING) k = INVEN_ARM; else if (inventory[INVEN_OUTER].tval != TV_NOTHING) k = INVEN_OUTER; else if (inventory[INVEN_HEAD].tval != TV_NOTHING) k = INVEN_HEAD; else if (inventory[INVEN_HANDS].tval != TV_NOTHING) k = INVEN_HANDS; else if (inventory[INVEN_FEET].tval != TV_NOTHING) k = INVEN_FEET; else k = 0; if (k > 0) { i_ptr = &inventory[k]; objdes(tmp_str, i_ptr, FALSE); (void)sprintf(out_val,"Your %s glows black, fades.",tmp_str); msg_print(out_val); unmagic_name(i_ptr); i_ptr->flags = TR_CURSED; i_ptr->toac = -randint(5) - randint(5); calc_bonuses (); ident = TRUE; } break; case 37: ident = FALSE; for (k = 0; k < randint(3); k++) { y = char_row; x = char_col; ident |= summon_undead(&y, &x); } break; case 38: ident = TRUE; bless(randint(12)+6); break; case 39: ident = TRUE; bless(randint(24)+12); break; case 40: ident = TRUE; bless(randint(48)+24); break; case 41: ident = TRUE; if (py.flags.word_recall == 0) py.flags.word_recall = 25 + randint(30); msg_print("The air about you becomes charged."); break; case 42: destroy_area(char_row, char_col); ident = TRUE; break; case 43: place_special(char_row, char_col, SPECIAL); prt_map(); break; case 44: special_random_object(char_row, char_col, 1); prt_map(); break; default: msg_print("Internal error in scroll()"); break; } /* End of Scrolls. */ } i_ptr = &inventory[item_val]; if (ident) { if (!known1_p(i_ptr)) { m_ptr = &py.misc; /* round half-way case up */ m_ptr->exp += (i_ptr->level +(m_ptr->lev >> 1)) / m_ptr->lev; prt_experience(); identify(&item_val); i_ptr = &inventory[item_val]; } }
/** * Gets information about the average damage/turn that can be inflicted if * the player wields the given weapon. * * Fills in the damage against normal adversaries in `normal_damage`, as well * as the slays on the weapon in slay_list[] and corresponding damages in * slay_damage[]. These must both be at least SL_MAX long to be safe. * `nonweap_slay` is set to whether other items being worn could add to the * damage done by branding attacks. * * Returns the number of slays populated in slay_list[] and slay_damage[]. * * Note that the results are meaningless if called on a fake ego object as * the actual ego may have different properties. */ static bool obj_known_damage(const struct object *obj, int *normal_damage, int *brand_damage, int *slay_damage, bool *nonweap_slay) { int i; int dice, sides, dam, total_dam, plus = 0; int xtra_postcrit = 0, xtra_precrit = 0; int crit_mult, crit_div, crit_add; int old_blows = 0; bool *total_brands; bool *total_slays; bool has_brands_or_slays = false; struct object *bow = equipped_item_by_slot_name(player, "shooting"); bool weapon = tval_is_melee_weapon(obj); bool ammo = (player->state.ammo_tval == obj->tval) && (bow); int melee_adj_mult = ammo ? 0 : 1; int multiplier = 1; struct player_state state; int weapon_slot = slot_by_name(player, "weapon"); struct object *current_weapon = slot_object(player, weapon_slot); /* Pretend we're wielding the object if it's a weapon */ if (weapon) player->body.slots[weapon_slot].obj = (struct object *) obj; /* Calculate the player's hypothetical state */ memcpy(&state, &player->state, sizeof(state)); state.stat_ind[STAT_STR] = 0; //Hack - NRM state.stat_ind[STAT_DEX] = 0; //Hack - NRM calc_bonuses(player, &state, true, false); /* Stop pretending */ player->body.slots[weapon_slot].obj = current_weapon; /* Finish if dice not known */ dice = obj->known->dd; sides = obj->known->ds; if (!dice || !sides) return false; /* Calculate damage */ dam = ((sides + 1) * dice * 5); if (weapon) { xtra_postcrit = state.to_d * 10; xtra_precrit += obj->known->to_d * 10; plus += obj->known->to_h; calculate_melee_crits(&state, obj->weight, plus, &crit_mult, &crit_add, &crit_div); old_blows = state.num_blows; } else { /* Ammo */ plus += obj->known->to_h; calculate_missile_crits(&player->state, obj->weight, plus, &crit_mult, &crit_add, &crit_div); dam += (obj->known->to_d * 10); dam += (bow->known->to_d * 10); } if (ammo) multiplier = player->state.ammo_mult; /* Get the brands */ total_brands = mem_zalloc(z_info->brand_max * sizeof(bool)); copy_brands(&total_brands, obj->known->brands); if (ammo && bow->known) copy_brands(&total_brands, bow->known->brands); /* Get the slays */ total_slays = mem_zalloc(z_info->slay_max * sizeof(bool)); copy_slays(&total_slays, obj->known->slays); if (ammo && bow->known) copy_slays(&total_slays, bow->known->slays); /* Melee weapons may get slays and brands from other items */ *nonweap_slay = false; if (weapon) { for (i = 2; i < player->body.count; i++) { struct object *slot_obj = slot_object(player, i); if (!slot_obj) continue; if (slot_obj->known->brands || slot_obj->known->slays) *nonweap_slay = true; else continue; /* Replace the old lists with new ones */ copy_brands(&total_brands, slot_obj->known->brands); copy_slays(&total_slays, slot_obj->known->slays); } } /* Get damage for each brand on the objects */ for (i = 1; i < z_info->brand_max; i++) { /* Must have the brand */ if (total_brands[i]) has_brands_or_slays = true; else continue; /* Include bonus damage and brand in stated average */ total_dam = dam * (multiplier + brands[i].multiplier - melee_adj_mult) + xtra_precrit; total_dam = (total_dam * crit_mult + crit_add) / crit_div; total_dam += xtra_postcrit; if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= player->state.num_shots; brand_damage[i] = total_dam; } /* Get damage for each slay on the objects */ for (i = 1; i < z_info->slay_max; i++) { /* Must have the slay */ if (total_slays[i]) has_brands_or_slays = true; else continue; /* Include bonus damage and slay in stated average */ total_dam = dam * (multiplier + slays[i].multiplier - melee_adj_mult) + xtra_precrit; total_dam = (total_dam * crit_mult + crit_add) / crit_div; total_dam += xtra_postcrit; if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= player->state.num_shots; slay_damage[i] = total_dam; } /* Include bonus damage in stated average */ total_dam = dam * multiplier + xtra_precrit; total_dam = (total_dam * crit_mult + crit_add) / crit_div; total_dam += xtra_postcrit; /* Normal damage, not considering brands or slays */ if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= player->state.num_shots; *normal_damage = total_dam; mem_free(total_brands); mem_free(total_slays); return has_brands_or_slays; }
/** * 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; int str_faster = -1, str_done = -1; int dex_plus_bound; int str_plus_bound; 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 */ memcpy(&state, &player->state, sizeof(state)); state.stat_ind[STAT_STR] = 0; //Hack - NRM state.stat_ind[STAT_DEX] = 0; //Hack - NRM calc_bonuses(player, &state, true, false); /* 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; dex_plus_bound = STAT_RANGE - state.stat_ind[STAT_DEX]; str_plus_bound = STAT_RANGE - state.stat_ind[STAT_STR]; /* Re-calculate with increased stats */ for (dex_plus = 0; dex_plus < dex_plus_bound; dex_plus++) { for (str_plus = 0; str_plus < str_plus_bound; str_plus++) { int new_blows = 0; /* Unlikely */ if (num == max_num) { player->body.slots[weapon_slot].obj = current_weapon; return num; } state.stat_ind[STAT_STR] = str_plus; //Hack - NRM state.stat_ind[STAT_DEX] = dex_plus; //Hack - NRM calc_bonuses(player, &state, true, false); new_blows = state.num_blows; /* 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; } } } /* Stop pretending */ player->body.slots[weapon_slot].obj = current_weapon; return num; }