/** * Describe slays and brands on weapons */ static bool describe_slays(textblock *tb, const struct object *obj) { struct slay *known_slays = slay_collect(obj, NULL, TRUE); struct slay *s; if (!known_slays) return FALSE; if (tval_is_weapon(obj) || tval_is_fuel(obj)) textblock_append(tb, "Slays "); else textblock_append(tb, "It causes your melee attacks to slay "); s = known_slays; while (s) { textblock_append(tb, s->name); if (s->multiplier > 3) textblock_append(tb, " (powerfully)"); if (s->next) textblock_append(tb, ", "); else textblock_append(tb, ".\n"); s = s->next; } free_slay(known_slays); return TRUE; }
/** * Calculate the rating for a given slay combination */ static s32b slay_power(const object_type *obj, int p, int verbose, int dice_pwr, bool known) { u32b sv = 0; int i, q, num_brands = 0, num_slays = 0, num_kills = 0; int mult; int tot_mon_power = 0; struct brand *brands = obj->brands; struct slay *slays = obj->slays; /* Count the known brands and slays */ while (brands) { if (known || brands->known) num_brands++; brands = brands->next; } while (slays) { if (known || slays->known) { if (slays->multiplier <= 3) num_slays++; else num_kills++; } slays = slays->next; } /* If there are no slays or brands return */ if ((num_slays + num_brands + num_kills) == 0) return p; /* Look in the cache to see if we know this one yet */ sv = check_slay_cache(obj); /* If it's cached (or there are no slays), return the value */ if (sv) { log_obj("Slay cache hit\n"); } else { /* * Otherwise we need to calculate the expected average multiplier * for this combination (multiplied by the total number of * monsters, which we'll divide out later). */ for (i = 0; i < z_info->r_max; i++) { monster_type *mon = mem_zalloc(sizeof(*mon)); const struct brand *b = NULL; const struct slay *s = NULL; char verb[20]; mult = 1; mon->race = &r_info[i]; /* Find the best multiplier against this monster */ improve_attack_modifier((object_type *)obj, mon, &b, &s, verb, FALSE, FALSE, !known); if (s) mult = s->multiplier; else if (b) mult = b->multiplier; /* Add up totals */ tot_mon_power += mon->race->scaled_power; sv += mult * mon->race->scaled_power; mem_free(mon); } /* * To get the expected damage for this weapon, multiply the * average damage from base dice by sv, and divide by the * total number of monsters. */ if (verbose) { struct brand *b, *brands = NULL; struct slay *s, *slays = NULL; /* Write info about the slay combination and multiplier */ log_obj("Slay multiplier for: "); brands = brand_collect(obj->brands, NULL, !known); slays = slay_collect(obj->slays, NULL, !known); for (b = brands; b; b = b->next) { log_obj(format("%sx%d ", b->name, b->multiplier)); } for (s = slays; s; s = s->next) { log_obj(format("%sx%d ", s->name, s->multiplier)); } log_obj(format("\nsv is: %d\n", sv)); log_obj(format(" and t_m_p is: %d \n", tot_mon_power)); log_obj(format("times 1000 is: %d\n", (1000 * sv) / tot_mon_power)); free_brand(brands); free_slay(slays); } /* Add to the cache */ if (fill_slay_cache(obj, sv)) log_obj("Added to slay cache\n"); } q = (dice_pwr * (sv / 100)) / (tot_mon_power / 100); p += q; log_obj(format("Add %d for slay power, total is %d\n", q, p)); /* Bonuses for multiple brands and slays */ if (num_slays > 1) { q = (num_slays * num_slays * dice_pwr) / (DAMAGE_POWER * 5); p += q; log_obj(format("Add %d power for multiple slays, total is %d\n", q, p)); } if (num_brands > 1) { q = (2 * num_brands * num_brands * dice_pwr) / (DAMAGE_POWER * 5); p += q; log_obj(format("Add %d power for multiple brands, total is %d\n",q, p)); } if (num_kills > 1) { q = (3 * num_kills * num_kills * dice_pwr) / (DAMAGE_POWER * 5); p += q; log_obj(format("Add %d power for multiple kills, total is %d\n", q, p)); } if (num_slays == 8) { p += 10; log_obj(format("Add 10 power for full set of slays, total is %d\n", p)); } if (num_brands == 5) { p += 20; log_obj(format("Add 20 power for full set of brands, total is %d\n",p)); } if (num_kills == 3) { p += 20; log_obj(format("Add 20 power for full set of kills, total is %d\n", p)); } return p; }
/** * 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); }