Esempio n. 1
0
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);
	}
}
Esempio n. 2
0
/*
 * 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;
}
Esempio n. 3
0
/*
 * 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;
}
Esempio n. 4
0
/**
 * 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;
}
Esempio n. 5
0
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);
	}
}
Esempio n. 6
0
/*
 * 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;
}