battle_context_unit_stats::battle_context_unit_stats(const unit &u, const map_location& u_loc, int u_attack_num, bool attacking, const unit &opp, const map_location& opp_loc, const attack_type *opp_weapon, const unit_map& units) : weapon(NULL), attack_num(u_attack_num), is_attacker(attacking), is_poisoned(u.get_state(unit::STATE_POISONED)), is_slowed(u.get_state(unit::STATE_SLOWED)), slows(false), drains(false), petrifies(false), plagues(false), poisons(false), backstab_pos(false), swarm(false), firststrike(false), experience(u.experience()), max_experience(u.max_experience()), level(u.level()), rounds(1), hp(0), max_hp(u.max_hitpoints()), chance_to_hit(0), damage(0), slow_damage(0), drain_percent(0), drain_constant(0), num_blows(0), swarm_min(0), swarm_max(0), plague_type() { // Get the current state of the unit. if (attack_num >= 0) { weapon = &u.attacks()[attack_num]; } if(u.hitpoints() < 0) { LOG_CF << "Unit with " << u.hitpoints() << " hitpoints found, set to 0 for damage calculations\n"; hp = 0; } else if(u.hitpoints() > u.max_hitpoints()) { // If a unit has more hp than its maximum, the engine will fail // with an assertion failure due to accessing the prob_matrix // out of bounds. hp = u.max_hitpoints(); } else { hp = u.hitpoints(); } // Get the weapon characteristics, if any. if (weapon) { weapon->set_specials_context(u_loc, opp_loc, attacking, opp_weapon); if (opp_weapon) opp_weapon->set_specials_context(opp_loc, u_loc, !attacking, weapon); slows = weapon->get_special_bool("slow"); drains = !opp.get_state("undrainable") && weapon->get_special_bool("drains"); petrifies = weapon->get_special_bool("petrifies"); poisons = !opp.get_state("unpoisonable") && weapon->get_special_bool("poison") && !opp.get_state(unit::STATE_POISONED); backstab_pos = is_attacker && backstab_check(u_loc, opp_loc, units, *resources::teams); rounds = weapon->get_specials("berserk").highest("value", 1).first; firststrike = weapon->get_special_bool("firststrike"); // Handle plague. unit_ability_list plague_specials = weapon->get_specials("plague"); plagues = !opp.get_state("unplagueable") && !plague_specials.empty() && strcmp(opp.undead_variation().c_str(), "null") && !resources::game_map->is_village(opp_loc); if (plagues) { plague_type = (*plague_specials.front().first)["type"].str(); if (plague_type.empty()) plague_type = u.type().base_id(); } // Compute chance to hit. chance_to_hit = opp.defense_modifier( resources::game_map->get_terrain(opp_loc)) + weapon->accuracy() - (opp_weapon ? opp_weapon->parry() : 0); if(chance_to_hit > 100) { chance_to_hit = 100; } unit_ability_list cth_specials = weapon->get_specials("chance_to_hit"); unit_abilities::effect cth_effects(cth_specials, chance_to_hit, backstab_pos); chance_to_hit = cth_effects.get_composite_value(); // Compute base damage done with the weapon. int base_damage = weapon->modified_damage(backstab_pos); // Get the damage multiplier applied to the base damage of the weapon. int damage_multiplier = 100; // Time of day bonus. damage_multiplier += combat_modifier(u_loc, u.alignment(), u.is_fearless()); // Leadership bonus. int leader_bonus = 0; if (under_leadership(units, u_loc, &leader_bonus).valid()) damage_multiplier += leader_bonus; // Resistance modifier. damage_multiplier *= opp.damage_from(*weapon, !attacking, opp_loc); // Compute both the normal and slowed damage. damage = round_damage(base_damage, damage_multiplier, 10000); slow_damage = round_damage(base_damage, damage_multiplier, 20000); if (is_slowed) damage = slow_damage; // Compute drain amounts only if draining is possible. if(drains) { unit_ability_list drain_specials = weapon->get_specials("drains"); // Compute the drain percent (with 50% as the base for backward compatibility) unit_abilities::effect drain_percent_effects(drain_specials, 50, backstab_pos); drain_percent = drain_percent_effects.get_composite_value(); } // Add heal_on_hit (the drain constant) unit_ability_list heal_on_hit_specials = weapon->get_specials("heal_on_hit"); unit_abilities::effect heal_on_hit_effects(heal_on_hit_specials, 0, backstab_pos); drain_constant += heal_on_hit_effects.get_composite_value(); drains = drain_constant || drain_percent; // Compute the number of blows and handle swarm. weapon->modified_attacks(backstab_pos, swarm_min, swarm_max); swarm = swarm_min != swarm_max; num_blows = calc_blows(hp); } }
/* * 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 blows. */ static bool describe_blows(textblock *tb, const object_type *o_ptr, player_state state, bitflag f[OF_SIZE]) { 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; bitflag tmp_f[OF_SIZE]; dex_plus_bound = STAT_RANGE - state.stat_ind[A_DEX]; str_plus_bound = STAT_RANGE - state.stat_ind[A_STR]; /* Write to the text block */ 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; } } 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; }
battle_context_unit_stats::battle_context_unit_stats(const unit_type* u_type, const attack_type* att_weapon, bool attacking, const unit_type* opp_type, const attack_type* opp_weapon, unsigned int opp_terrain_defense, int lawful_bonus) : weapon(att_weapon), attack_num(-2), // This is and stays invalid. Always use weapon, when using this constructor. is_attacker(attacking), is_poisoned(false), is_slowed(false), slows(false), drains(false), petrifies(false), plagues(false), poisons(false), backstab_pos(false), swarm(false), firststrike(false), disable(false), experience(0), max_experience(0), level(0), rounds(1), hp(0), max_hp(0), chance_to_hit(0), damage(0), slow_damage(0), drain_percent(0), drain_constant(0), num_blows(0), swarm_min(0), swarm_max(0), plague_type() { if (!u_type || !opp_type) { return; } // Get the current state of the unit. if (u_type->hitpoints() < 0) { hp = 0; } else { hp = u_type->hitpoints(); } max_experience = u_type->experience_needed(); level = (u_type->level()); max_hp = (u_type->hitpoints()); // Get the weapon characteristics, if any. if (weapon) { weapon->set_specials_context(map_location::null_location(), attacking); if (opp_weapon) { opp_weapon->set_specials_context(map_location::null_location(), !attacking); } slows = weapon->get_special_bool("slow"); drains = !opp_type->musthave_status("undrainable") && weapon->get_special_bool("drains"); petrifies = weapon->get_special_bool("petrifies"); poisons = !opp_type->musthave_status("unpoisonable") && weapon->get_special_bool("poison"); rounds = weapon->get_specials("berserk").highest("value", 1).first; firststrike = weapon->get_special_bool("firststrike"); disable = weapon->get_special_bool("disable"); unit_ability_list plague_specials = weapon->get_specials("plague"); plagues = !opp_type->musthave_status("unplagueable") && !plague_specials.empty() && strcmp(opp_type->undead_variation().c_str(), "null"); if (plagues) { plague_type = (*plague_specials.front().first)["type"].str(); if (plague_type.empty()) { plague_type = u_type->base_id(); } } signed int cth = 100 - opp_terrain_defense + weapon->accuracy() - (opp_weapon ? opp_weapon->parry() : 0); cth = std::min(100, cth); cth = std::max(0, cth); chance_to_hit = cth; unit_ability_list cth_specials = weapon->get_specials("chance_to_hit"); unit_abilities::effect cth_effects(cth_specials, chance_to_hit, backstab_pos); chance_to_hit = cth_effects.get_composite_value(); int base_damage = weapon->modified_damage(backstab_pos); int damage_multiplier = 100; damage_multiplier += generic_combat_modifier(lawful_bonus, u_type->alignment(), u_type->musthave_status("fearless")); damage_multiplier *= opp_type->resistance_against(weapon->type(), !attacking); damage = round_damage(base_damage, damage_multiplier, 10000); slow_damage = round_damage(base_damage, damage_multiplier, 20000); if (drains) { unit_ability_list drain_specials = weapon->get_specials("drains"); // Compute the drain percent (with 50% as the base for backward compatibility) unit_abilities::effect drain_percent_effects(drain_specials, 50, backstab_pos); drain_percent = drain_percent_effects.get_composite_value(); } // Add heal_on_hit (the drain constant) unit_ability_list heal_on_hit_specials = weapon->get_specials("heal_on_hit"); unit_abilities::effect heal_on_hit_effects(heal_on_hit_specials, 0, backstab_pos); drain_constant += heal_on_hit_effects.get_composite_value(); drains = drain_constant || drain_percent; // Compute the number of blows and handle swarm. weapon->modified_attacks(backstab_pos, swarm_min, swarm_max); swarm = swarm_min != swarm_max; num_blows = calc_blows(hp); } }
/* * Calculate the players current "state", taking into account * not only race/class intrinsics, but also objects being worn * and temporary spell effects. * * See also calc_mana() and calc_hitpoints(). * * Take note of the new "speed code", in particular, a very strong * player will start slowing down as soon as he reaches 150 pounds, * but not until he reaches 450 pounds will he be half as fast as * a normal kobold. This both hurts and helps the player, hurts * because in the old days a player could just avoid 300 pounds, * and helps because now carrying 300 pounds is not very painful. * * The "weapon" and "bow" do *not* add to the bonuses to hit or to * damage, since that would affect non-combat things. These values * are actually added in later, at the appropriate place. * * If id_only is true, calc_bonuses() will only use the known * information of objects; thus it returns what the player _knows_ * the character state to be. */ void calc_bonuses(object_type inventory[], player_state *state, bool id_only) { int i, j, hold; int extra_blows = 0; int extra_shots = 0; int extra_might = 0; object_type *o_ptr; bitflag f[OF_SIZE]; bitflag collect_f[OF_SIZE]; /*** Reset ***/ memset(state, 0, sizeof *state); /* Set various defaults */ state->speed = 110; state->num_blow = 1; /*** Extract race/class info ***/ /* Base infravision (purely racial) */ state->see_infra = rp_ptr->infra; /* Base skills */ for (i = 0; i < SKILL_MAX; i++) state->skills[i] = rp_ptr->r_skills[i] + cp_ptr->c_skills[i]; /*** Analyze player ***/ /* Extract the player flags */ player_flags(collect_f); /*** Analyze equipment ***/ /* Scan the equipment */ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) { o_ptr = &inventory[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Extract the item flags */ if (id_only) object_flags_known(o_ptr, f); else object_flags(o_ptr, f); of_union(collect_f, f); /* Affect stats */ if (of_has(f, OF_STR)) state->stat_add[A_STR] += o_ptr->pval; if (of_has(f, OF_INT)) state->stat_add[A_INT] += o_ptr->pval; if (of_has(f, OF_WIS)) state->stat_add[A_WIS] += o_ptr->pval; if (of_has(f, OF_DEX)) state->stat_add[A_DEX] += o_ptr->pval; if (of_has(f, OF_CON)) state->stat_add[A_CON] += o_ptr->pval; if (of_has(f, OF_CHR)) state->stat_add[A_CHR] += o_ptr->pval; /* Affect stealth */ if (of_has(f, OF_STEALTH)) state->skills[SKILL_STEALTH] += o_ptr->pval; /* Affect searching ability (factor of five) */ if (of_has(f, OF_SEARCH)) state->skills[SKILL_SEARCH] += (o_ptr->pval * 5); /* Affect searching frequency (factor of five) */ if (of_has(f, OF_SEARCH)) state->skills[SKILL_SEARCH_FREQUENCY] += (o_ptr->pval * 5); /* Affect infravision */ if (of_has(f, OF_INFRA)) state->see_infra += o_ptr->pval; /* Affect digging (factor of 20) */ if (of_has(f, OF_TUNNEL)) state->skills[SKILL_DIGGING] += (o_ptr->pval * 20); /* Affect speed */ if (of_has(f, OF_SPEED)) state->speed += o_ptr->pval; /* Affect blows */ if (of_has(f, OF_BLOWS)) extra_blows += o_ptr->pval; /* Affect shots */ if (of_has(f, OF_SHOTS)) extra_shots += o_ptr->pval; /* Affect Might */ if (of_has(f, OF_MIGHT)) extra_might += o_ptr->pval; /* Modify the base armor class */ state->ac += o_ptr->ac; /* The base armor class is always known */ state->dis_ac += o_ptr->ac; /* Apply the bonuses to armor class */ if (!id_only || object_is_known(o_ptr)) state->to_a += o_ptr->to_a; /* Apply the mental bonuses to armor class, if known */ if (object_defence_plusses_are_visible(o_ptr)) state->dis_to_a += o_ptr->to_a; /* Hack -- do not apply "weapon" bonuses */ if (i == INVEN_WIELD) continue; /* Hack -- do not apply "bow" bonuses */ if (i == INVEN_BOW) continue; /* Apply the bonuses to hit/damage */ if (!id_only || object_is_known(o_ptr)) { state->to_h += o_ptr->to_h; state->to_d += o_ptr->to_d; } /* Apply the mental bonuses tp hit/damage, if known */ if (object_attack_plusses_are_visible(o_ptr)) { state->dis_to_h += o_ptr->to_h; state->dis_to_d += o_ptr->to_d; } } /*** Update all flags ***/ /* Good flags */ if (of_has(collect_f, OF_SLOW_DIGEST)) state->slow_digest = TRUE; if (of_has(collect_f, OF_FEATHER)) state->ffall = TRUE; if (of_has(collect_f, OF_REGEN)) state->regenerate = TRUE; if (of_has(collect_f, OF_TELEPATHY)) state->telepathy = TRUE; if (of_has(collect_f, OF_SEE_INVIS)) state->see_inv = TRUE; if (of_has(collect_f, OF_FREE_ACT)) state->free_act = TRUE; if (of_has(collect_f, OF_HOLD_LIFE)) state->hold_life = TRUE; /* Weird flags */ if (of_has(collect_f, OF_BLESSED)) state->bless_blade = TRUE; /* Bad flags */ if (of_has(collect_f, OF_IMPACT)) state->impact = TRUE; if (of_has(collect_f, OF_AGGRAVATE)) state->aggravate = TRUE; if (of_has(collect_f, OF_TELEPORT)) state->teleport = TRUE; if (of_has(collect_f, OF_DRAIN_EXP)) state->exp_drain = TRUE; if (of_has(collect_f, OF_IMPAIR_HP)) state->impair_hp = TRUE; if (of_has(collect_f, OF_IMPAIR_MANA)) state->impair_mana = TRUE; if (of_has(collect_f, OF_AFRAID)) state->afraid = TRUE; /* Vulnerability flags */ if (of_has(collect_f, OF_VULN_FIRE)) state->vuln_fire = TRUE; if (of_has(collect_f, OF_VULN_ACID)) state->vuln_acid = TRUE; if (of_has(collect_f, OF_VULN_COLD)) state->vuln_cold = TRUE; if (of_has(collect_f, OF_VULN_ELEC)) state->vuln_elec = TRUE; /* Immunity flags */ if (of_has(collect_f, OF_IM_FIRE)) state->immune_fire = TRUE; if (of_has(collect_f, OF_IM_ACID)) state->immune_acid = TRUE; if (of_has(collect_f, OF_IM_COLD)) state->immune_cold = TRUE; if (of_has(collect_f, OF_IM_ELEC)) state->immune_elec = TRUE; /* Resistance flags */ if (of_has(collect_f, OF_RES_ACID)) state->resist_acid = TRUE; if (of_has(collect_f, OF_RES_ELEC)) state->resist_elec = TRUE; if (of_has(collect_f, OF_RES_FIRE)) state->resist_fire = TRUE; if (of_has(collect_f, OF_RES_COLD)) state->resist_cold = TRUE; if (of_has(collect_f, OF_RES_POIS)) state->resist_pois = TRUE; if (of_has(collect_f, OF_RES_FEAR)) state->resist_fear = TRUE; if (of_has(collect_f, OF_RES_LIGHT)) state->resist_light = TRUE; if (of_has(collect_f, OF_RES_DARK)) state->resist_dark = TRUE; if (of_has(collect_f, OF_RES_BLIND)) state->resist_blind = TRUE; if (of_has(collect_f, OF_RES_CONFU)) state->resist_confu = TRUE; if (of_has(collect_f, OF_RES_SOUND)) state->resist_sound = TRUE; if (of_has(collect_f, OF_RES_SHARD)) state->resist_shard = TRUE; if (of_has(collect_f, OF_RES_NEXUS)) state->resist_nexus = TRUE; if (of_has(collect_f, OF_RES_NETHR)) state->resist_nethr = TRUE; if (of_has(collect_f, OF_RES_CHAOS)) state->resist_chaos = TRUE; if (of_has(collect_f, OF_RES_DISEN)) state->resist_disen = TRUE; /* Sustain flags */ if (of_has(collect_f, OF_SUST_STR)) state->sustain_str = TRUE; if (of_has(collect_f, OF_SUST_INT)) state->sustain_int = TRUE; if (of_has(collect_f, OF_SUST_WIS)) state->sustain_wis = TRUE; if (of_has(collect_f, OF_SUST_DEX)) state->sustain_dex = TRUE; if (of_has(collect_f, OF_SUST_CON)) state->sustain_con = TRUE; if (of_has(collect_f, OF_SUST_CHR)) state->sustain_chr = TRUE; /*** Handle stats ***/ /* Calculate stats */ for (i = 0; i < A_MAX; i++) { int add, top, use, ind; /* Extract modifier */ add = state->stat_add[i]; /* Maximize mode */ if (OPT(adult_maximize)) { /* Modify the stats for race/class */ add += (rp_ptr->r_adj[i] + cp_ptr->c_adj[i]); } /* Extract the new "stat_top" value for the stat */ top = modify_stat_value(p_ptr->stat_max[i], add); /* Save the new value */ state->stat_top[i] = top; /* Extract the new "stat_use" value for the stat */ use = modify_stat_value(p_ptr->stat_cur[i], add); /* Save the new value */ state->stat_use[i] = use; /* Values: n/a */ if (use <= 3) ind = 0; /* Values: 3, 4, ..., 18 */ else if (use <= 18) ind = (use - 3); /* Ranges: 18/00-18/09, ..., 18/210-18/219 */ else if (use <= 18+219) ind = (15 + (use - 18) / 10); /* Range: 18/220+ */ else ind = (37); assert((0 <= ind) && (ind < STAT_RANGE)); /* Save the new index */ state->stat_ind[i] = ind; } /*** Temporary flags ***/ /* Apply temporary "stun" */ if (p_ptr->timed[TMD_STUN] > 50) { state->to_h -= 20; state->dis_to_h -= 20; state->to_d -= 20; state->dis_to_d -= 20; state->skills[SKILL_DEVICE] = state->skills[SKILL_DEVICE] * 8 / 10; } else if (p_ptr->timed[TMD_STUN]) { state->to_h -= 5; state->dis_to_h -= 5; state->to_d -= 5; state->dis_to_d -= 5; state->skills[SKILL_DEVICE] = state->skills[SKILL_DEVICE] * 9 / 10; } /* Invulnerability */ if (p_ptr->timed[TMD_INVULN]) { state->to_a += 100; state->dis_to_a += 100; } /* Temporary blessing */ if (p_ptr->timed[TMD_BLESSED]) { state->to_a += 5; state->dis_to_a += 5; state->to_h += 10; state->dis_to_h += 10; state->skills[SKILL_DEVICE] = state->skills[SKILL_DEVICE] * 105 / 100; } /* Temporary shield */ if (p_ptr->timed[TMD_SHIELD]) { state->to_a += 50; state->dis_to_a += 50; } /* Temporary stoneskin */ if (p_ptr->timed[TMD_STONESKIN]) { state->to_a += 40; state->dis_to_a += 40; state->speed -= 5; } /* Temporary "Hero" */ if (p_ptr->timed[TMD_HERO]) { state->to_h += 12; state->dis_to_h += 12; state->resist_fear = TRUE; state->skills[SKILL_DEVICE] = state->skills[SKILL_DEVICE] * 105 / 100; } /* Temporary "Berserk" */ if (p_ptr->timed[TMD_SHERO]) { state->to_h += 24; state->dis_to_h += 24; state->to_a -= 10; state->dis_to_a -= 10; state->resist_fear = TRUE; state->skills[SKILL_DEVICE] = state->skills[SKILL_DEVICE] * 9 / 10; } /* Temporary "fast" */ if (p_ptr->timed[TMD_FAST] || p_ptr->timed[TMD_SPRINT]) state->speed += 10; /* Temporary "slow" */ if (p_ptr->timed[TMD_SLOW]) state->speed -= 10; /* Temporary see invisible */ if (p_ptr->timed[TMD_SINVIS]) state->see_inv = TRUE; /* Temporary infravision boost */ if (p_ptr->timed[TMD_SINFRA]) state->see_infra += 5; /* Temporary telepathy */ if (p_ptr->timed[TMD_TELEPATHY]) state->telepathy = TRUE; /* Temporary resist confusion */ if (p_ptr->timed[TMD_OPP_CONF]) state->resist_confu = TRUE; /* Fear */ if (p_ptr->timed[TMD_AFRAID] || p_ptr->timed[TMD_TERROR]) state->afraid = TRUE; if (p_ptr->timed[TMD_TERROR]) state->speed += 5; /* Fear can come from item flags too */ if (state->afraid) { state->to_h -= 20; state->dis_to_h -= 20; state->to_a += 8; state->dis_to_a += 8; state->skills[SKILL_DEVICE] = state->skills[SKILL_DEVICE] * 95 / 100; } /* Confusion */ if (p_ptr->timed[TMD_CONFUSED]) state->skills[SKILL_DEVICE] = state->skills[SKILL_DEVICE] * 75 / 100; /* Amnesia */ if (p_ptr->timed[TMD_AMNESIA]) state->skills[SKILL_DEVICE] = state->skills[SKILL_DEVICE] * 8 / 10; /* Poison */ if (p_ptr->timed[TMD_POISONED]) state->skills[SKILL_DEVICE] = state->skills[SKILL_DEVICE] * 95 / 100; /* Hallucination */ if (p_ptr->timed[TMD_IMAGE]) state->skills[SKILL_DEVICE] = state->skills[SKILL_DEVICE] * 8 / 10; /*** Analyze weight ***/ /* Extract the current weight (in tenth pounds) */ j = p_ptr->total_weight; /* Extract the "weight limit" (in tenth pounds) */ i = weight_limit(state); /* Apply "encumbrance" from weight */ if (j > i / 2) state->speed -= ((j - (i / 2)) / (i / 10)); /* Bloating slows the player down (a little) */ if (p_ptr->food >= PY_FOOD_MAX) state->speed -= 10; /* Searching slows the player down */ if (p_ptr->searching) state->speed -= 10; /* Sanity check on extreme speeds */ if (state->speed < 0) state->speed = 0; if (state->speed > 199) state->speed = 199; /*** Apply modifier bonuses ***/ /* Actual Modifier Bonuses (Un-inflate stat bonuses) */ state->to_a += ((int)(adj_dex_ta[state->stat_ind[A_DEX]]) - 128); state->to_d += ((int)(adj_str_td[state->stat_ind[A_STR]]) - 128); state->to_h += ((int)(adj_dex_th[state->stat_ind[A_DEX]]) - 128); state->to_h += ((int)(adj_str_th[state->stat_ind[A_STR]]) - 128); /* Displayed Modifier Bonuses (Un-inflate stat bonuses) */ state->dis_to_a += ((int)(adj_dex_ta[state->stat_ind[A_DEX]]) - 128); state->dis_to_d += ((int)(adj_str_td[state->stat_ind[A_STR]]) - 128); state->dis_to_h += ((int)(adj_dex_th[state->stat_ind[A_DEX]]) - 128); state->dis_to_h += ((int)(adj_str_th[state->stat_ind[A_STR]]) - 128); /*** Modify skills ***/ /* Affect Skill -- stealth (bonus one) */ state->skills[SKILL_STEALTH] += 1; /* Affect Skill -- disarming (DEX and INT) */ state->skills[SKILL_DISARM] += adj_dex_dis[state->stat_ind[A_DEX]]; state->skills[SKILL_DISARM] += adj_int_dis[state->stat_ind[A_INT]]; /* Affect Skill -- magic devices (INT) */ state->skills[SKILL_DEVICE] += adj_int_dev[state->stat_ind[A_INT]]; /* Affect Skill -- saving throw (WIS) */ state->skills[SKILL_SAVE] += adj_wis_sav[state->stat_ind[A_WIS]]; /* Affect Skill -- digging (STR) */ state->skills[SKILL_DIGGING] += adj_str_dig[state->stat_ind[A_STR]]; /* Affect Skills (Level, by Class) */ for (i = 0; i < SKILL_MAX; i++) state->skills[i] += (cp_ptr->x_skills[i] * p_ptr->lev / 10); /* Limit Skill -- digging from 1 up */ if (state->skills[SKILL_DIGGING] < 1) state->skills[SKILL_DIGGING] = 1; /* Limit Skill -- stealth from 0 to 30 */ if (state->skills[SKILL_STEALTH] > 30) state->skills[SKILL_STEALTH] = 30; if (state->skills[SKILL_STEALTH] < 0) state->skills[SKILL_STEALTH] = 0; /* Apply Skill -- Extract noise from stealth */ state->noise = (1L << (30 - state->skills[SKILL_STEALTH])); /* Obtain the "hold" value */ hold = adj_str_hold[state->stat_ind[A_STR]]; /*** Analyze current bow ***/ /* Examine the "current bow" */ o_ptr = &inventory[INVEN_BOW]; /* Assume not heavy */ state->heavy_shoot = FALSE; /* It is hard to carholdry a heavy bow */ if (hold < o_ptr->weight / 10) { /* Hard to wield a heavy bow */ state->to_h += 2 * (hold - o_ptr->weight / 10); state->dis_to_h += 2 * (hold - o_ptr->weight / 10); /* Heavy Bow */ state->heavy_shoot = TRUE; } /* Analyze launcher */ if (o_ptr->k_idx) { /* Get to shoot */ state->num_fire = 1; /* Analyze the launcher */ switch (o_ptr->sval) { /* Sling and ammo */ case SV_SLING: { state->ammo_tval = TV_SHOT; state->ammo_mult = 2; break; } /* Short Bow and Arrow */ case SV_SHORT_BOW: { state->ammo_tval = TV_ARROW; state->ammo_mult = 2; break; } /* Long Bow and Arrow */ case SV_LONG_BOW: { state->ammo_tval = TV_ARROW; state->ammo_mult = 3; break; } /* Light Crossbow and Bolt */ case SV_LIGHT_XBOW: { state->ammo_tval = TV_BOLT; state->ammo_mult = 3; break; } /* Heavy Crossbow and Bolt */ case SV_HEAVY_XBOW: { state->ammo_tval = TV_BOLT; state->ammo_mult = 4; break; } } /* Apply special flags */ if (o_ptr->k_idx && !state->heavy_shoot) { /* Extra shots */ state->num_fire += extra_shots; /* Extra might */ state->ammo_mult += extra_might; /* Hack -- Rangers love Bows */ if (player_has(PF_EXTRA_SHOT) && (state->ammo_tval == TV_ARROW)) { /* Extra shot at level 20 */ if (p_ptr->lev >= 20) state->num_fire++; /* Extra shot at level 40 */ if (p_ptr->lev >= 40) state->num_fire++; } } /* Require at least one shot */ if (state->num_fire < 1) state->num_fire = 1; } /*** Analyze weapon ***/ /* Examine the "current weapon" */ o_ptr = &inventory[INVEN_WIELD]; /* Assume not heavy */ state->heavy_wield = FALSE; /* It is hard to hold a heavy weapon */ if (hold < o_ptr->weight / 10) { /* Hard to wield a heavy weapon */ state->to_h += 2 * (hold - o_ptr->weight / 10); state->dis_to_h += 2 * (hold - o_ptr->weight / 10); /* Heavy weapon */ state->heavy_wield = TRUE; } /* Non-object means barehanded attacks */ if (!o_ptr->k_idx) assert(o_ptr->weight == 0); /* Normal weapons */ if (!state->heavy_wield) { /* Calculate number of blows */ state->num_blow = calc_blows(o_ptr, state) + extra_blows; /* Boost digging skill by weapon weight */ state->skills[SKILL_DIGGING] += (o_ptr->weight / 10); } /* Assume okay */ state->icky_wield = FALSE; /* Priest weapon penalty for non-blessed edged weapons */ if (player_has(PF_BLESS_WEAPON) && (!state->bless_blade) && ((o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM))) { /* Reduce the real bonuses */ state->to_h -= 2; state->to_d -= 2; /* Reduce the mental bonuses */ state->dis_to_h -= 2; state->dis_to_d -= 2; /* Icky weapon */ state->icky_wield = TRUE; } return; }