/* * 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; }
/** * 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); }
/* * Describe damage. */ static bool describe_damage(textblock *tb, const object_type *o_ptr, player_state state, bitflag f[OF_SIZE], oinfo_detail_t mode) { const char *desc[SL_MAX] = { 0 }; size_t i, cnt; int mult[SL_MAX]; 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; object_type *bow = &p_ptr->inventory[INVEN_BOW]; bitflag 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; bool full = mode & OINFO_FULL; /* Create the "all slays" mask */ create_mask(mask, FALSE, OFT_SLAY, OFT_KILL, OFT_BRAND, OFT_MAX); /* Use displayed dice if real dice not known */ if (full || object_attack_plusses_are_visible(o_ptr)) { dice = o_ptr->dd; sides = o_ptr->ds; } else { dice = o_ptr->kind->dd; sides = o_ptr->kind->ds; } /* Calculate damage */ dam = ((sides + 1) * dice * 5); if (weapon) { xtra_postcrit = state.dis_to_d * 10; if (object_attack_plusses_are_visible(o_ptr) || full) { 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); old_blows = state.num_blows; } else { /* Ammo */ if (object_attack_plusses_are_visible(o_ptr) || full) plus += o_ptr->to_h; calculate_missile_crits(&p_ptr->state, o_ptr->weight, plus, &crit_mult, &crit_add, &crit_div); if (object_attack_plusses_are_visible(o_ptr) || full) 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); } /* Collect slays */ /* Melee weapons get slays and brands from other items now */ if (weapon) { bool nonweap_slay = 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); /* Strip out non-slays */ of_inter(tmp_f, mask); if (of_union(f, tmp_f)) nonweap_slay = TRUE; } if (nonweap_slay) 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; /* Output damage for creatures effected by the brands or slays */ cnt = list_slays(f, mask, desc, NULL, mult, TRUE); for (i = 0; i < cnt; i++) { /* 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 + 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; /* Normal damage, not considering brands or slays */ 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"); 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, 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; }