Esempio n. 1
0
/**
 * Hack -- Teleport to the target.  Oangband asks for a target after 
 * the command.
 */
static void do_cmd_wiz_bamf(void)
{
	feature_type *f_ptr;

	/* target starts at player. */
	s16b ny = 0;
	s16b nx = 0;

	/* Use the targeting function. */
	if (!target_set_interactive(TARGET_LOOK, -1, -1))
		return;

	/* grab the target coords. */
	target_get(&nx, &ny);

	/* Test for passable terrain. */
	f_ptr = &f_info[cave_feat[ny][nx]];
	if (!tf_has(f_ptr->flags, TF_PASSABLE)) {
		msg("The square you are aiming for is impassable.");
	}

	/* The simple act of controlled teleport. */
	else
		teleport_player_to(ny, nx, TRUE);
}
Esempio n. 2
0
/*
 * Hack -- Teleport to the target
 */
static void do_cmd_wiz_bamf(void)
{
	s16b x, y;

	/* Must have a target */
	if (!target_okay()) return;

	/* Teleport to the target */
	target_get(&x, &y);
	teleport_player_to(y, x);
}
void target_wait_all(void)
{
	int i;
	struct target *t;

	target_array_lock();
	for (i = 0; i < MAX_TARGET_COUNT; ++i) {
		if (target_use[i] == 0)
			continue;

		t = target_get(i);

		LOGI("join recv thread [%d] is started\n", i);
		target_wait(t);
		LOGI("join recv thread %d. done\n", i);
	}
	target_array_unlock();
}
/*
 * for all targets
 */
int target_send_msg_to_all(struct msg_target_t *msg)
{
	int i, ret = 0;
	struct target *t;

	target_array_lock();
	for (i = 0; i < MAX_TARGET_COUNT; ++i) {
		if (target_use[i] == 0)
			continue;

		t = target_get(i);
		if (target_send_msg(t, msg))
			ret = 1;
	}
	target_array_unlock();

	return ret;
}
Esempio n. 5
0
/**
 * Target closest monster.
 *
 * XXX: Move to using CMD_TARGET_CLOSEST at some point instead of invoking
 * target_set_closest() directly.
 */
void textui_target_closest(void)
{
	if (target_set_closest(TARGET_KILL)) {
		bool visibility;
		int x, y;

		target_get(&x, &y);

		/* Visual cue */
		Term_fresh();
		Term_get_cursor(&visibility);
		(void)Term_set_cursor(TRUE);
		move_cursor_relative(y, x);
		Term_redraw_section(x, y, x, y);

		/* TODO: what's an appropriate amount of time to spend highlighting */
		Term_xtra(TERM_XTRA_DELAY, 150);
		(void)Term_set_cursor(visibility);
	}
}
uint64_t target_get_total_alloc(pid_t pid)
{
	int i;
	uint64_t ret = 0;
	struct target *t;

	target_array_lock();
	for (i = 0; i < MAX_TARGET_COUNT; i++) {
		if (target_use[i] == 0)
			continue;

		t = target_get(i);
		if (target_get_pid(t) == pid) {
			ret = t->allocmem;
			goto unlock;
		}
	}
unlock:
	target_array_unlock();

	return ret;
}
Esempio n. 7
0
/**
 * Hack -- Teleport to the target.  Oangband asks for a target after
 * the command.
 */
static void do_cmd_wiz_bamf(void)
{
    /* target starts at player. */
    s16b ny = 0;
    s16b nx = 0;

    /* Use the targeting function. */
    if (!target_set_interactive(TARGET_LOOK, -1, -1))
        return;

    /* grab the target coords. */
    target_get(&nx, &ny);

    /* Test for passable terrain. */
    if (!cave_passable_bold(ny, nx)) {
        msg_print("The square you are aiming for is impassable.");
    }

    /* The simple act of controlled teleport. */
    else
        teleport_player_to(ny, nx, TRUE);
}
Esempio n. 8
0
/**
 * This is a helper function used by do_cmd_throw and do_cmd_fire.
 *
 * It abstracts out the projectile path, display code, identify and clean up
 * logic, while using the 'attack' parameter to do work particular to each
 * kind of attack.
 */
static void ranged_helper(int item, int dir, int range, int shots, ranged_attack attack) {
	/* Get the ammo */
	object_type *o_ptr = object_from_item_idx(item);

	int i, j;
	byte missile_attr = object_attr(o_ptr);
	char missile_char = object_char(o_ptr);

	object_type object_type_body;
	object_type *i_ptr = &object_type_body;

	char o_name[80];

	int path_n;
	u16b path_g[256];

	int msec = op_ptr->delay_factor;

	/* Start at the player */
	int x = p_ptr->px;
	int y = p_ptr->py;

	/* Predict the "target" location */
	s16b ty = y + 99 * ddy[dir];
	s16b tx = x + 99 * ddx[dir];

	bool hit_target = FALSE;

	/* Check for target validity */
	if ((dir == 5) && target_okay()) {
		int taim;
		char msg[80];
		target_get(&tx, &ty);
		taim = distance(y, x, ty, tx);
		if (taim > range) {
			sprintf (msg, "Target out of range by %d squares. Fire anyway? ",
				taim - range);
			if (!get_check(msg)) return;
		}
	}

	/* Sound */
	sound(MSG_SHOOT);

	object_notice_on_firing(o_ptr);

	/* Describe the object */
	object_desc(o_name, sizeof(o_name), o_ptr, ODESC_FULL | ODESC_SINGULAR);

	/* Actually "fire" the object -- Take a partial turn */
	p_ptr->energy_use = (100 / shots);

	/* Calculate the path */
	path_n = project_path(path_g, range, y, x, ty, tx, 0);

	/* Hack -- Handle stuff */
	handle_stuff(p_ptr);

	/* Start at the player */
	x = p_ptr->px;
	y = p_ptr->py;

	/* Project along the path */
	for (i = 0; i < path_n; ++i) {
		int ny = GRID_Y(path_g[i]);
		int nx = GRID_X(path_g[i]);

		/* Hack -- Stop before hitting walls */
		if (!cave_floor_bold(ny, nx)) break;

		/* Advance */
		x = nx;
		y = ny;

		/* Only do visuals if the player can "see" the missile */
		if (player_can_see_bold(y, x)) {
			print_rel(missile_char, missile_attr, y, x);
			move_cursor_relative(y, x);

			Term_fresh();
			if (p_ptr->redraw) redraw_stuff(p_ptr);

			Term_xtra(TERM_XTRA_DELAY, msec);
			cave_light_spot(cave, y, x);

			Term_fresh();
			if (p_ptr->redraw) redraw_stuff(p_ptr);
		} else {
			/* Delay anyway for consistency */
			Term_xtra(TERM_XTRA_DELAY, msec);
		}

		/* Handle monster */
		if (cave->m_idx[y][x] > 0) break;
	}

	/* Try the attack on the monster at (x, y) if any */
	if (cave->m_idx[y][x] > 0) {
		monster_type *m_ptr = cave_monster(cave, cave->m_idx[y][x]);
		monster_race *r_ptr = &r_info[m_ptr->r_idx];
		int visible = m_ptr->ml;

		bool fear = FALSE;
		char m_name[80];
		const char *note_dies = monster_is_unusual(r_ptr) ? " is destroyed." : " dies.";

		struct attack_result result = attack(o_ptr, y, x);
		int dmg = result.dmg;
		u32b msg_type = result.msg_type;
		const char *hit_verb = result.hit_verb;

		if (result.success) {
			hit_target = TRUE;

			/* Get "the monster" or "it" */
			monster_desc(m_name, sizeof(m_name), m_ptr, 0);
		
			object_notice_attack_plusses(o_ptr);
		
			/* No negative damage; change verb if no damage done */
			if (dmg <= 0) {
				dmg = 0;
				hit_verb = "fail to harm";
			}
		
			if (!visible) {
				/* Invisible monster */
				msgt(MSG_SHOOT_HIT, "The %s finds a mark.", o_name);
			} else {
				/* Visible monster */
				if (msg_type == MSG_SHOOT_HIT)
					msgt(MSG_SHOOT_HIT, "The %s %s %s.", o_name, hit_verb, m_name);
				else if (msg_type == MSG_HIT_GOOD) {
					msgt(MSG_HIT_GOOD, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a good hit!");
				} else if (msg_type == MSG_HIT_GREAT) {
					msgt(MSG_HIT_GREAT, "The %s %s %s. %s", o_name, hit_verb, m_name,
						 "It was a great hit!");
				} else if (msg_type == MSG_HIT_SUPERB) {
					msgt(MSG_HIT_SUPERB, "The %s %s %s. %s", o_name, hit_verb, m_name,
						 "It was a superb hit!");
				}
		
				/* Track this monster */
				if (m_ptr->ml) monster_race_track(m_ptr->r_idx);
				if (m_ptr->ml) health_track(p_ptr, cave->m_idx[y][x]);
			}
		
			/* Complex message */
			if (p_ptr->wizard)
				msg("You do %d (out of %d) damage.", dmg, m_ptr->hp);
		
			/* Hit the monster, check for death */
			if (!mon_take_hit(cave->m_idx[y][x], dmg, &fear, note_dies)) {
				message_pain(cave->m_idx[y][x], dmg);
				if (fear && m_ptr->ml)
					add_monster_message(m_name, cave->m_idx[y][x], MON_MSG_FLEE_IN_TERROR, TRUE);
			}
		}
	}

	/* Obtain a local object */
	object_copy(i_ptr, o_ptr);
	object_split(i_ptr, o_ptr, 1);

	/* See if the ammunition broke or not */
	j = breakage_chance(i_ptr, hit_target);

	/* Drop (or break) near that location */
	drop_near(cave, i_ptr, j, y, x, TRUE);

	if (item >= 0) {
		/* The ammo is from the inventory */
		inven_item_increase(item, -1);
		inven_item_describe(item);
		inven_item_optimize(item);
	} else {
		/* The ammo is from the floor */
		floor_item_increase(0 - item, -1);
		floor_item_optimize(0 - item);
	}
}
Esempio n. 9
0
/**
 * This is a helper function used by do_cmd_throw and do_cmd_fire.
 *
 * It abstracts out the projectile path, display code, identify and clean up
 * logic, while using the 'attack' parameter to do work particular to each
 * kind of attack.
 */
static void ranged_helper(struct object *obj, int dir, int range, int shots,
						  ranged_attack attack)
{
	int i, j;

	char o_name[80];

	int path_n;
	struct loc path_g[256];

	/* Start at the player */
	int x = player->px;
	int y = player->py;

	/* Predict the "target" location */
	int ty = y + 99 * ddy[dir];
	int tx = x + 99 * ddx[dir];

	bool hit_target = FALSE;
	bool none_left = FALSE;

	struct object *missile;

	/* Check for target validity */
	if ((dir == 5) && target_okay()) {
		int taim;
		target_get(&tx, &ty);
		taim = distance(y, x, ty, tx);
		if (taim > range) {
			char msg[80];
			strnfmt(msg, sizeof(msg),
					"Target out of range by %d squares. Fire anyway? ",
				taim - range);
			if (!get_check(msg)) return;
		}
	}

	/* Sound */
	sound(MSG_SHOOT);

	/* Describe the object */
	object_desc(o_name, sizeof(o_name), obj, ODESC_FULL | ODESC_SINGULAR);

	/* Actually "fire" the object -- Take a partial turn */
	player->upkeep->energy_use = (z_info->move_energy / shots);

	/* Calculate the path */
	path_n = project_path(path_g, range, y, x, ty, tx, 0);

	/* Hack -- Handle stuff */
	handle_stuff(player);

	/* Start at the player */
	x = player->px;
	y = player->py;

	/* Project along the path */
	for (i = 0; i < path_n; ++i) {
		struct monster *mon = NULL;
		int ny = path_g[i].y;
		int nx = path_g[i].x;
		bool see = square_isseen(cave, ny, nx);

		/* Stop before hitting walls */
		if (!(square_ispassable(cave, ny, nx)) &&
			!(square_isprojectable(cave, ny, nx)))
			break;

		/* Advance */
		x = nx;
		y = ny;

		/* Tell the UI to display the missile */
		event_signal_missile(EVENT_MISSILE, obj, see, y, x);

		/* Try the attack on the monster at (x, y) if any */
		mon = square_monster(cave, y, x);
		if (mon) {
			int visible = mflag_has(mon->mflag, MFLAG_VISIBLE);

			bool fear = FALSE;
			const char *note_dies = monster_is_unusual(mon->race) ? 
				" is destroyed." : " dies.";

			struct attack_result result = attack(obj, y, x);
			int dmg = result.dmg;
			u32b msg_type = result.msg_type;
			char hit_verb[20];
			my_strcpy(hit_verb, result.hit_verb, sizeof(hit_verb));
			mem_free(result.hit_verb);

			if (result.success) {
				hit_target = TRUE;

				object_notice_attack_plusses(obj);

				/* Learn by use for other equipped items */
				equip_notice_to_hit_on_attack(player);

				/* No negative damage; change verb if no damage done */
				if (dmg <= 0) {
					dmg = 0;
					msg_type = MSG_MISS;
					my_strcpy(hit_verb, "fails to harm", sizeof(hit_verb));
				}

				if (!visible) {
					/* Invisible monster */
					msgt(MSG_SHOOT_HIT, "The %s finds a mark.", o_name);
				} else {
					for (j = 0; j < (int)N_ELEMENTS(ranged_hit_types); j++) {
						char m_name[80];
						const char *dmg_text = "";

						if (msg_type != ranged_hit_types[j].msg)
							continue;

						if (OPT(show_damage))
							dmg_text = format(" (%d)", dmg);

						monster_desc(m_name, sizeof(m_name), mon, MDESC_OBJE);

						if (ranged_hit_types[j].text)
							msgt(msg_type, "Your %s %s %s%s. %s", o_name, 
								 hit_verb, m_name, dmg_text, 
								 ranged_hit_types[j].text);
						else
							msgt(msg_type, "Your %s %s %s%s.", o_name, hit_verb,
								 m_name, dmg_text);
					}
					
					/* Track this monster */
					if (mflag_has(mon->mflag, MFLAG_VISIBLE)) {
						monster_race_track(player->upkeep, mon->race);
						health_track(player->upkeep, mon);
					}
				}

				/* Hit the monster, check for death */
				if (!mon_take_hit(mon, dmg, &fear, note_dies)) {
					message_pain(mon, dmg);
					if (fear && mflag_has(mon->mflag, MFLAG_VISIBLE)) {
						char m_name[80];
						monster_desc(m_name, sizeof(m_name), mon, 
									 MDESC_DEFAULT);
						add_monster_message(m_name, mon, 
											MON_MSG_FLEE_IN_TERROR, TRUE);
					}
				}
			}
			/* Stop the missile */
			break;
		}

		/* Stop if non-projectable but passable */
		if (!(square_isprojectable(cave, ny, nx))) 
			break;
	}

	/* Get the missile */
	if (object_is_carried(player, obj))
		missile = gear_object_for_use(obj, 1, TRUE, &none_left);
	else
		missile = floor_object_for_use(obj, 1, TRUE, &none_left);

	/* Drop (or break) near that location */
	drop_near(cave, missile, breakage_chance(missile, hit_target), y, x, TRUE);
}
Esempio n. 10
0
/*
 * Fire an object from the pack or floor.
 *
 * You may only fire items that "match" your missile launcher.
 *
 * See "calc_bonuses()" for more calculations and such.
 *
 * Note that "firing" a missile is MUCH better than "throwing" it.
 *
 * Note: "unseen" monsters are very hard to hit.
 *
 * Objects are more likely to break if they "attempt" to hit a monster.
 *
 * Rangers (with Bows) and Anyone (with "Extra Shots") get extra shots.
 * The "extra shot" code works by decreasing the amount of energy
 * required to make each shot, spreading the shots out over time.
 *
 * Note that when firing missiles, the launcher multiplier is applied
 * after all the bonuses are added in, making multipliers very useful.
 *
 * Note that Bows of "Extra Might" get extra range and an extra bonus
 * for the damage multiplier.
 */
void do_cmd_fire(cmd_code code, cmd_arg args[])
{
	int dir, item;
	int i, j, y, x;
	s16b ty, tx;
	int tdam, tdis, thits;
	int bonus, chance;

	object_type *o_ptr;
	object_type *j_ptr;

	object_type *i_ptr;
	object_type object_type_body;

	bool hit_body = FALSE;

	byte missile_attr;
	char missile_char;

	char o_name[80];

	u32b msg_type = 0;

	int path_n;
	u16b path_g[256];

	int msec = op_ptr->delay_factor * op_ptr->delay_factor;

	/* Get the "bow" */
	j_ptr = &p_ptr->inventory[INVEN_BOW];

	/* Require a usable launcher */
	if (!j_ptr->tval || !p_ptr->state.ammo_tval)
	{
		msg_print("You have nothing to fire with.");
		return;
	}

	/* Get item to fire and direction to fire in. */
	item = args[0].item;
	dir = args[1].direction;

	/* Check the item being fired is usable by the player. */
	if (!item_is_available(item, NULL, (USE_EQUIP | USE_INVEN | USE_FLOOR)))
	{
		msg_format("That item is not within your reach.");
		return;
	}

	/* Get the object for the ammo */
	o_ptr = object_from_item_idx(item);

	/* Check the ammo can be used with the launcher */
	if (o_ptr->tval != p_ptr->state.ammo_tval)
	{
		msg_format("That ammo cannot be fired by your current weapon.");
		return;
	}

	/* Base range XXX XXX */
	tdis = 6 + 2 * p_ptr->state.ammo_mult;

	/* Start at the player */
	x = p_ptr->px;
	y = p_ptr->py;

	/* Predict the "target" location */
	ty = y + 99 * ddy[dir];
	tx = x + 99 * ddx[dir];

	/* Check for target validity */
	if ((dir == 5) && target_okay())
	{
		target_get(&tx, &ty);
		if (distance(y, x, ty, tx) > tdis)
		{
			if (!get_check("Target out of range.  Fire anyway? "))
				return;
		}
	}

	/* Sound */
	sound(MSG_SHOOT);

	object_notice_on_firing(o_ptr);

	/* Describe the object */
	object_desc(o_name, sizeof(o_name), o_ptr, ODESC_FULL | ODESC_SINGULAR);

	/* Find the color and symbol for the object for throwing */
	missile_attr = object_attr(o_ptr);
	missile_char = object_char(o_ptr);

	/* Use the proper number of shots */
	thits = p_ptr->state.num_fire;

	/* Actually "fire" the object */
	bonus = (p_ptr->state.to_h + o_ptr->to_h + j_ptr->to_h);
	chance = p_ptr->state.skills[SKILL_TO_HIT_BOW] +
			(bonus * BTH_PLUS_ADJ);

	/* Take a (partial) turn */
	p_ptr->energy_use = (100 / thits);

	/* Calculate the path */
	path_n = project_path(path_g, tdis, y, x, ty, tx, 0);

	/* Hack -- Handle stuff */
	handle_stuff();

	/* Project along the path */
	for (i = 0; i < path_n; ++i)
	{
		int ny = GRID_Y(path_g[i]);
		int nx = GRID_X(path_g[i]);

		/* Hack -- Stop before hitting walls */
		if (!cave_floor_bold(ny, nx)) break;

		/* Advance */
		x = nx;
		y = ny;

		/* Only do visuals if the player can "see" the missile */
		if (player_can_see_bold(y, x))
		{
			/* Visual effects */
			print_rel(missile_char, missile_attr, y, x);
			move_cursor_relative(y, x);

			Term_fresh();
			if (p_ptr->redraw) redraw_stuff();

			Term_xtra(TERM_XTRA_DELAY, msec);
			light_spot(y, x);

			Term_fresh();
			if (p_ptr->redraw) redraw_stuff();
		}

		/* Delay anyway for consistency */
		else
		{
			/* Pause anyway, for consistancy */
			Term_xtra(TERM_XTRA_DELAY, msec);
		}

		/* Handle monster */
		if (cave_m_idx[y][x] > 0)
		{
			monster_type *m_ptr = &mon_list[cave_m_idx[y][x]];
			monster_race *r_ptr = &r_info[m_ptr->r_idx];

			int chance2 = chance - distance(p_ptr->py, p_ptr->px, y, x);
			int visible = m_ptr->ml;
			int multiplier = 1;

			const char *hit_verb = "hits";
			const slay_t *best_s_ptr = NULL;

			/* Note the collision */
			hit_body = TRUE;

			/* Did we hit it (penalize distance travelled) */
			if (test_hit(chance2, r_ptr->ac, m_ptr->ml))
			{
				bool fear = FALSE;

				/* Assume a default death */
				cptr note_dies = " dies.";

				improve_attack_modifier(o_ptr, m_ptr, &best_s_ptr);
				improve_attack_modifier(j_ptr, m_ptr, &best_s_ptr);
				if (best_s_ptr != NULL)
					hit_verb = best_s_ptr->range_verb;

				/* Some monsters get "destroyed" */
				if (monster_is_unusual(r_ptr))
				{
					/* Special note at death */
					note_dies = " is destroyed.";
				}

				/* Calculate multiplier */
				multiplier = p_ptr->state.ammo_mult;
				if (best_s_ptr != NULL) multiplier += best_s_ptr->mult;

				/* Apply damage: multiplier, slays, criticals, bonuses */
				tdam = damroll(o_ptr->dd, o_ptr->ds);
				tdam += o_ptr->to_d + j_ptr->to_d;
				tdam *= multiplier;
				tdam = critical_shot(o_ptr->weight, o_ptr->to_h, tdam, &msg_type);

				object_notice_attack_plusses(o_ptr);
				object_notice_attack_plusses(&p_ptr->inventory[INVEN_BOW]);

				/* No negative damage; change verb if no damage done */
				if (tdam <= 0)
				{
					tdam = 0;
					hit_verb = "fail to harm";
				}

				/* Handle unseen monster */
				if (!visible)
				{
					/* Invisible monster */
					message_format(MSG_SHOOT_HIT, 0, "The %s finds a mark.", o_name);
				}

				/* Handle visible monster */
				else
				{
					char m_name[80];

					/* Get "the monster" or "it" */
					monster_desc(m_name, sizeof(m_name), m_ptr, 0);

					/* Tell the player what happened */
					if (msg_type == MSG_SHOOT_HIT)
						message_format(MSG_SHOOT_HIT, 0, "The %s %s %s.", o_name, hit_verb, m_name);
					else
					{
						if (msg_type == MSG_HIT_GOOD)
						{
							message_format(MSG_HIT_GOOD, 0, "The %s %s %s. %s", o_name, hit_verb, m_name,
										   "It was a good hit!");
						}
						else if (msg_type == MSG_HIT_GREAT)
						{
							message_format(MSG_HIT_GREAT, 0, "The %s %s %s. %s", o_name, hit_verb, m_name,
										   "It was a great hit!");
						}
						else if (msg_type == MSG_HIT_SUPERB)
						{
							message_format(MSG_HIT_SUPERB, 0, "The %s %s %s. %s", o_name, hit_verb, m_name,
										   "It was a superb hit!");
						}
					}
					/* Hack -- Track this monster race */
					if (m_ptr->ml) monster_race_track(m_ptr->r_idx);

					/* Hack -- Track this monster */
					if (m_ptr->ml) health_track(cave_m_idx[y][x]);

				}

				/* Complex message */
				if (p_ptr->wizard)
				{
					msg_format("You do %d (out of %d) damage.",
					           tdam, m_ptr->hp);
				}

				/* Hit the monster, check for death */
				if (mon_take_hit(cave_m_idx[y][x], tdam, &fear, note_dies))
				{
					/* Dead monster */
				}

				/* No death */
				else
				{
					/* Message */
					message_pain(cave_m_idx[y][x], tdam);

					/* Take note */
					if (fear && m_ptr->ml)
					{
						char m_name[80];

						/* Get the monster name (or "it") */
						monster_desc(m_name, sizeof(m_name), m_ptr, 0);

						/* Message */
						message_format(MSG_FLEE, m_ptr->r_idx,
						               "%^s flees in terror!", m_name);
					}
				}
			}

			/* Stop looking */
			break;
		}
	}



	/* Get local object */
	i_ptr = &object_type_body;

	/* Obtain a local object */
	object_copy(i_ptr, o_ptr);

	/* Single object */
	i_ptr->number = 1;

	if (item >= 0)
	{
		inven_item_increase(item, -1);
		inven_item_describe(item);
		inven_item_optimize(item);
	}

	/* Reduce and describe floor item */
	else
	{
		floor_item_increase(0 - item, -1);
		floor_item_optimize(0 - item);
	}


	/* Chance of breakage (during attacks) */
	j = (hit_body ? breakage_chance(i_ptr) : 0);

	/* Drop (or break) near that location */
	drop_near(i_ptr, j, y, x, TRUE);
}
Esempio n. 11
0
/*
 * Throw an object from the pack or floor.
 *
 * Note: "unseen" monsters are very hard to hit.
 *
 * Should throwing a weapon do full damage?  Should it allow the magic
 * to hit bonus of the weapon to have an effect?  Should it ever cause
 * the item to be destroyed?  Should it do any damage at all?
 */
void do_cmd_throw(cmd_code code, cmd_arg args[])
{
	int dir, item;
	int i, j, y, x;
	s16b ty, tx;
	int chance, tdam, tdis;
	int weight;

	object_type *o_ptr;

	object_type *i_ptr;
	object_type object_type_body;

	bool hit_body = FALSE;

	byte missile_attr;
	char missile_char;

	char o_name[80];

	u32b msg_type = 0;

	int path_n;
	u16b path_g[256];

	int msec = op_ptr->delay_factor * op_ptr->delay_factor;

	/* Get item to throw and direction in which to throw it. */
	item = args[0].item;
	dir = args[1].direction;

	/* Make sure the player isn't throwing wielded items */
	if (item >= INVEN_WIELD && item < QUIVER_START)
	{
		msg_print("You have cannot throw wielded items.");
		return;
	}

	/* Check the item being thrown is usable by the player. */
	if (!item_is_available(item, NULL, (USE_EQUIP | USE_INVEN | USE_FLOOR)))
	{
		msg_format("That item is not within your reach.");
		return;
	}

	/* Get the object */
	o_ptr = object_from_item_idx(item);
	object_notice_on_firing(o_ptr);

	/* Get local object */
	i_ptr = &object_type_body;

	/* Obtain a local object */
	object_copy(i_ptr, o_ptr);

	/* Distribute the charges of rods/wands/staves between the stacks */
	distribute_charges(o_ptr, i_ptr, 1);

	/* Single object */
	i_ptr->number = 1;

	/* Reduce and describe inventory */
	if (item >= 0)
	{
		inven_item_increase(item, -1);
		inven_item_describe(item);
		inven_item_optimize(item);
	}

	/* Reduce and describe floor item */
	else
	{
		floor_item_increase(0 - item, -1);
		floor_item_optimize(0 - item);
	}


	/* Description */
	object_desc(o_name, sizeof(o_name), i_ptr, ODESC_FULL);

	/* Find the color and symbol for the object for throwing */
	missile_attr = object_attr(i_ptr);
	missile_char = object_char(i_ptr);


	/* Enforce a minimum "weight" of one pound */
	weight = ((i_ptr->weight > 10) ? i_ptr->weight : 10);

	/* Hack -- Distance -- Reward strength, penalize weight */
	tdis = (adj_str_blow[p_ptr->state.stat_ind[A_STR]] + 20) * 10 / weight;

	/* Max distance of 10 */
	if (tdis > 10) tdis = 10;

	/* Hack -- Base damage from thrown object */
	tdam = damroll(i_ptr->dd, i_ptr->ds);
	if (!tdam) tdam = 1;
	tdam += i_ptr->to_d;

	/* Chance of hitting */
	chance = (p_ptr->state.skills[SKILL_TO_HIT_THROW] + (p_ptr->state.to_h * BTH_PLUS_ADJ));


	/* Take a turn */
	p_ptr->energy_use = 100;


	/* Start at the player */
	y = p_ptr->py;
	x = p_ptr->px;

	/* Predict the "target" location */
	ty = p_ptr->py + 99 * ddy[dir];
	tx = p_ptr->px + 99 * ddx[dir];

	/* Check for "target request" */
	if ((dir == 5) && target_okay())
	{
		target_get(&tx, &ty);
	}

	/* Calculate the path */
	path_n = project_path(path_g, tdis, p_ptr->py, p_ptr->px, ty, tx, 0);


	/* Hack -- Handle stuff */
	handle_stuff();

	/* Project along the path */
	for (i = 0; i < path_n; ++i)
	{
		int ny = GRID_Y(path_g[i]);
		int nx = GRID_X(path_g[i]);

		/* Hack -- Stop before hitting walls */
		if (!cave_floor_bold(ny, nx)) break;

		/* Advance */
		x = nx;
		y = ny;

		/* Only do visuals if the player can "see" the missile */
		if (player_can_see_bold(y, x))
		{
			/* Visual effects */
			print_rel(missile_char, missile_attr, y, x);
			move_cursor_relative(y, x);

			Term_fresh();
			if (p_ptr->redraw) redraw_stuff();

			Term_xtra(TERM_XTRA_DELAY, msec);
			light_spot(y, x);

			Term_fresh();
			if (p_ptr->redraw) redraw_stuff();
		}

		/* Delay anyway for consistency */
		else
		{
			/* Pause anyway, for consistancy */
			Term_xtra(TERM_XTRA_DELAY, msec);
		}

		/* Handle monster */
		if (cave_m_idx[y][x] > 0)
		{
			monster_type *m_ptr = &mon_list[cave_m_idx[y][x]];
			monster_race *r_ptr = &r_info[m_ptr->r_idx];

			int chance2 = chance - distance(p_ptr->py, p_ptr->px, y, x);

			int visible = m_ptr->ml;

			/* Note the collision */
			hit_body = TRUE;

			/* Did we hit it (penalize range) */
			if (test_hit(chance2, r_ptr->ac, m_ptr->ml))
			{
				const char *hit_verb = "hits";
				bool fear = FALSE;
				const slay_t *best_s_ptr = NULL;

				/* Assume a default death */
				cptr note_dies = " dies.";

				/* Some monsters get "destroyed" */
				if (monster_is_unusual(r_ptr))
				{
					/* Special note at death */
					note_dies = " is destroyed.";
				}

				/* Apply special damage  - brought forward to fill in hit_verb XXX XXX XXX */
				improve_attack_modifier(i_ptr, m_ptr, &best_s_ptr);
				if (best_s_ptr != NULL)
				{
					tdam *= best_s_ptr->mult;
					hit_verb = best_s_ptr->range_verb;
				}
				/* Apply special damage XXX XXX XXX */
				tdam = critical_shot(i_ptr->weight, i_ptr->to_h, tdam, &msg_type);

				/* No negative damage; change verb if no damage done */
				if (tdam <= 0)
				{
					tdam = 0;
					hit_verb = "fail to harm";
				}

				/* Handle unseen monster */
				if (!visible)
				{
					/* Invisible monster */
					msg_format("The %s finds a mark.", o_name);
				}

				/* Handle visible monster */
				else
				{
					char m_name[80];

					/* Get "the monster" or "it" */
					monster_desc(m_name, sizeof(m_name), m_ptr, 0);

					/* Tell the player what happened */
					if (msg_type == MSG_SHOOT_HIT)
						message_format(MSG_SHOOT_HIT, 0, "The %s %s %s.", o_name, hit_verb, m_name);
					else
					{
						if (msg_type == MSG_HIT_GOOD)
						{
							message_format(MSG_HIT_GOOD, 0, "The %s %s %s. %s", o_name, hit_verb, m_name,
										   "It was a good hit!");
						}
						else if (msg_type == MSG_HIT_GREAT)
						{
							message_format(MSG_HIT_GREAT, 0, "The %s %s %s. %s", o_name, hit_verb, m_name,
										   "It was a great hit!");
						}
						else if (msg_type == MSG_HIT_SUPERB)
						{
							message_format(MSG_HIT_SUPERB, 0, "The %s %s %s. %s", o_name, hit_verb, m_name,
										   "It was a superb hit!");
						}
					}
					/* Hack -- Track this monster race */
					if (m_ptr->ml) monster_race_track(m_ptr->r_idx);

					/* Hack -- Track this monster */
					if (m_ptr->ml) health_track(cave_m_idx[y][x]);
				}

				/* Learn the bonuses */
				/* XXX Eddie This is messed up, better done for firing, */
				/* should use that method [split last] instead */
				/* check if inven_optimize removed what o_ptr referenced */
				if (object_similar(i_ptr, o_ptr, OSTACK_PACK))
					object_notice_attack_plusses(o_ptr);
				object_notice_attack_plusses(i_ptr);

				/* Complex message */
				if (p_ptr->wizard)
					msg_format("You do %d (out of %d) damage.",
							   tdam, m_ptr->hp);

				/* Hit the monster, check for death */
				if (mon_take_hit(cave_m_idx[y][x], tdam, &fear, note_dies))
				{
					/* Dead monster */
				}

				/* No death */
				else
				{
					/* Message */
					message_pain(cave_m_idx[y][x], tdam);

					/* Take note */
					if (fear && m_ptr->ml)
					{
						char m_name[80];

						/* Get the monster name (or "it") */
						monster_desc(m_name, sizeof(m_name), m_ptr, 0);

						/* Message */
						message_format(MSG_FLEE, m_ptr->r_idx,
						               "%^s flees in terror!", m_name);
					}
				}
			}

			/* Stop looking */
			break;
		}
	}

	/* Chance of breakage (during attacks) */
	j = (hit_body ? breakage_chance(i_ptr) : 0);

	/* Drop (or break) near that location */
	drop_near(i_ptr, j, y, x, TRUE);
}
Esempio n. 12
0
/**
 * Let the user select an item, save its "index"
 *
 * Return TRUE only if an acceptable item was chosen by the user.
 *
 * The selected item must satisfy the "item_tester_hook()" function,
 * if that hook is set, and the "item_tester_tval", if that value is set.
 *
 * All "item_tester" restrictions are cleared before this function returns.
 *
 * The user is allowed to choose acceptable items from the equipment,
 * inventory, or floor, respectively, if the proper flag was given,
 * and there are any acceptable items in that location.
 *
 * The equipment or inventory are displayed (even if no acceptable
 * items are in that location) if the proper flag was given.
 *
 * If there are no acceptable items available anywhere, and "str" is
 * not NULL, then it will be used as the text of a warning message
 * before the function returns.
 *
 * Note that the user must press "-" to specify the item on the floor,
 * and there is no way to "examine" the item on the floor, while the
 * use of "capital" letters will "examine" an inventory/equipment item,
 * and prompt for its use.
 *
 * If a legal item is selected from the inventory, we save it in "cp"
 * directly (0 to 35), and return TRUE.
 *
 * If a legal item is selected from the floor, we save it in "cp" as
 * a negative (-1 to -511), and return TRUE.
 *
 * If no item is available, we do nothing to "cp", and we display a
 * warning message, using "str" if available, and return FALSE.
 *
 * If no item is selected, we do nothing to "cp", and return FALSE.
 *
 * Global "p_ptr->command_wrk" is used to choose between equip/inven/floor
 * listings.  It is equal to USE_INVEN or USE_EQUIP or USE_FLOOR, except
 * when this function is first called, when it is equal to zero, which will
 * cause it to be set to USE_INVEN.
 *
 * We always erase the prompt when we are done, leaving a blank line,
 * or a warning message, if appropriate, if no items are available.
 *
 * Note that only "acceptable" floor objects get indexes, so between two
 * commands, the indexes of floor objects may change.  XXX XXX XXX
 */
bool get_item(int *cp, const char *pmt, const char *str, cmd_code cmd, int mode)
{
    s16b py = p_ptr->py;
    s16b px = p_ptr->px;
    unsigned char cmdkey = cmd_lookup_key(cmd, KEYMAP_MODE_ORIG);

    bool done, item;
    int j, k;

    bool oops = FALSE;
    bool toggle = FALSE;

    bool use_inven = ((mode & USE_INVEN) ? TRUE : FALSE);
    bool use_equip = ((mode & USE_EQUIP) ? TRUE : FALSE);
    bool use_floor = ((mode & (USE_FLOOR | USE_TARGET)) ? TRUE : FALSE);
    bool use_quiver = ((mode & QUIVER_TAGS) ? TRUE : FALSE);
    bool can_squelch = ((mode & CAN_SQUELCH) ? TRUE : FALSE);
    bool is_harmless = ((mode & IS_HARMLESS) ? TRUE : FALSE);
    bool quiver_tags = ((mode & QUIVER_TAGS) ? TRUE : FALSE);

    bool allow_inven = FALSE;
    bool allow_equip = FALSE;
    bool allow_floor = FALSE;

    int floor_num;
    ui_event which;
    
    prompt = pmt;
    olist_mode = 0;
    item_mode = mode;
    item_cmd = cmd;

    /* Not done */
    done = FALSE;

    /* No item selected */
    item = FALSE;
    *cp = 0;

    /* Object list display modes */
    if (mode & SHOW_FAIL)
	olist_mode |= (OLIST_FAIL);
    else
	olist_mode |= (OLIST_WEIGHT);
    if (mode & SHOW_PRICES)
	olist_mode |= (OLIST_PRICE);

    show_list  = OPT(show_lists) ? TRUE : FALSE; 

    /* Set target for telekinesis */
    if (mode & (USE_TARGET))
    {
	target_get(&px, &py);
	if (!(px && py))
	    return FALSE;
    }
    
    /* Full inventory */
    i1 = 0;
    i2 = INVEN_PACK - 1;

    /* Forbid inventory */
    if (!use_inven)
	i2 = -1;

    /* Restrict inventory indexes */
    while ((i1 <= i2) && (!get_item_okay(i1)))
	i1++;
    while ((i1 <= i2) && (!get_item_okay(i2)))
	i2--;

    /* Accept inventory */
    if (i1 <= i2)
	allow_inven = TRUE;


    /* Full equipment */
    e1 = INVEN_WIELD;
    e2 = ALL_INVEN_TOTAL - 1;

    /* Forbid equipment */
    if (!use_equip)
	e2 = -1;

    /* Restrict equipment indexes */
    while ((e1 <= e2) && (!get_item_okay(e1)))
	e1++;
    while ((e1 <= e2) && (!get_item_okay(e2)))
	e2--;

    /* Accept equipment */
    if (e1 <= e2)
	allow_equip = TRUE;

    /* Reject quiver */
    if (e2 < QUIVER_START)
	use_quiver = FALSE;

    /* Scan all non-gold objects in the grid */
    floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), py, px, 0x03);

    /* Full floor */
    f1 = 0;
    f2 = floor_num - 1;

    /* Forbid floor */
    if (!use_floor)
	f2 = -1;

    /* Restrict floor indexes */
    while ((f1 <= f2) && (!get_item_okay(0 - floor_list[f1])))
	f1++;
    while ((f1 <= f2) && (!get_item_okay(0 - floor_list[f2])))
	f2--;

    /* Accept floor */
    if (f1 <= f2)
	allow_floor = TRUE;

    /* Require at least one legal choice */
    if (!allow_inven && !allow_equip && !allow_floor) {
	/* Oops */
	oops = TRUE;
	done = TRUE;
    }

    /* Analyze choices, prepare for initial menu */
    else {
	/* Hack -- Start on equipment if requested */
	if ((p_ptr->command_wrk == USE_EQUIP) && use_equip){
	    p_ptr->command_wrk = USE_EQUIP;
	    build_obj_list(INVEN_WIELD, e2, NULL, olist_mode);
	}

	/* If we are using the quiver then start on equipment */
	else if (use_quiver){
	    p_ptr->command_wrk = USE_EQUIP;
	    build_obj_list(INVEN_WIELD, e2, NULL, olist_mode);
	}

	/* Use inventory if allowed */
	else if (use_inven){
	    p_ptr->command_wrk = USE_INVEN;
	    build_obj_list(0, i2, NULL, olist_mode);
	}

	/* Use equipment if allowed */
	else if (use_equip){
	    p_ptr->command_wrk = USE_EQUIP;
	    build_obj_list(INVEN_WIELD, e2, NULL, olist_mode);
	}

	/* Use floor if allowed */
	else if (use_floor){
	    p_ptr->command_wrk = USE_FLOOR;
	    build_obj_list(0, f2, floor_list, olist_mode);
	}

	/* Hack -- Use (empty) inventory */
	else {
	    p_ptr->command_wrk = USE_INVEN;
	    build_obj_list(0, i2, NULL, olist_mode);
	}
    }

    /* Clear all current messages */
    msg_flag = FALSE;

    /* Start out in "display" mode */
    if (show_list) {
	/* Save screen */
	screen_save();
    }

    /* Repeat until done */
    while (!done) {
	static bool refresh;
	int ni = 0;
	int ne = 0;

	/* Scan windows */
	for (j = 0; j < ANGBAND_TERM_MAX; j++) {
	    /* Unused */
	    if (!angband_term[j])
		continue;

	    /* Count windows displaying inven */
	    if (op_ptr->window_flag[j] & (PW_INVEN))
		ni++;

	    /* Count windows displaying equip */
	    if (op_ptr->window_flag[j] & (PW_EQUIP))
		ne++;
	}

	/* Toggle if needed */
	if (((p_ptr->command_wrk == USE_EQUIP) && ni && !ne)
	    || ((p_ptr->command_wrk == USE_INVEN) && !ni && ne)) {
	    /* Toggle */
	    toggle_inven_equip();

	    /* Track toggles */
	    toggle = !toggle;
	}

	/* Redraw */
	p_ptr->redraw |= (PR_INVEN | PR_EQUIP);

	/* Redraw windows */
	redraw_stuff(p_ptr);

	/* Change the list if needed */
	if (refresh) {
	    /* Rebuild object list */
	    if (p_ptr->command_wrk == USE_INVEN) 
		build_obj_list(0, i2, NULL, olist_mode);
	    else if (p_ptr->command_wrk == USE_EQUIP) 
		build_obj_list(INVEN_WIELD, e2, NULL, olist_mode);
	    else 
		build_obj_list(0, f2, floor_list, mode);

	    refresh = FALSE;
	}

	/* Show the prompt */
	item_prompt(mode);

	redraw_stuff(p_ptr);

	/* Menu if requested */
	if (show_list) 
	{
	    which = item_menu(cmd, mode);
	    if (which.type == EVT_ESCAPE)
		which.key.code = ESCAPE;
	}

	/* Get a key */
	else which = inkey_ex();

	/* Parse it */
	switch (which.key.code) 
	{
	    case ESCAPE:
	    {
		done = TRUE;
		break;
	    }

    	    case '*':
	    case '?':
	    case ' ':
	    {
		if (!OPT(show_lists)) 
		{
		    /* Save screen */
		    screen_save();
		    
		    /* Flip flag */
		    show_list = TRUE;
		}
		
		refresh = TRUE;
		break;
	    }

	    case '/':
	    {
		/* Toggle to inventory */
		if (use_inven && (p_ptr->command_wrk != USE_INVEN)) 
		{
		    p_ptr->command_wrk = USE_INVEN;
		    refresh = TRUE;
		}

		/* Toggle to equipment */
		else if (use_equip && (p_ptr->command_wrk != USE_EQUIP)) 
		{
		    p_ptr->command_wrk = USE_EQUIP;
		    refresh = TRUE;
		}

		/* No toggle allowed */
		else 
		{
		    bell("Cannot switch item selector!");
		    break;
		}

		/* Need to redraw */
		break;
	    }

	    case '-':
	    {
		/* Paranoia */
		if (!allow_floor) {
		    bell("Cannot select floor!");
		    break;
		}

		/* There is only one item */
		if (OPT(pickup_single) && (floor_num == 1)) 
		{
		    /* Fall through */
		} 
		else 
		{
		    p_ptr->command_wrk = (USE_FLOOR);
		    refresh = TRUE;
		    break;
		}
	    }
	    case '.':
	    {
		/* 
		 * If we are allow to use the floor, select
		 * the top item. -BR-
		 */
		if (allow_floor) {
		    int k;

		    /* Special index */
		    k = 0 - floor_list[0];

		    /* Allow player to "refuse" certain actions */
		    if (!get_item_allow(k, cmdkey, is_harmless)) {
			done = TRUE;
			break;
		    }

		    /* Accept that choice */
		    (*cp) = k;
		    item = TRUE;
		    done = TRUE;
		}

		break;
	    }

	    case '0':
	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
	    {
		/* Look up the tag */
		if (!get_tag(&k, which.key.code, cmd, quiver_tags)) 
		{
		    bell("Illegal object choice (tag)!");
		    break;
		}

		/* Hack -- Validate the item */
		if ((k < INVEN_WIELD) ? !allow_inven : !allow_equip) 
		{
		    bell("Illegal object choice (tag)!");
		    break;
		}

		/* Validate the item */
		if (!get_item_okay(k)) 
		{
		    bell("Illegal object choice (tag)!");
		    break;
		}

		/* Allow player to "refuse" certain actions */
		if (!get_item_allow(k, cmdkey, is_harmless)) 
		{
		    done = TRUE;
		    break;
		}

		/* Accept that choice */
		(*cp) = k;
		item = TRUE;
		done = TRUE;
		break;
	    }

	    case KC_ENTER:
		{
		    /* Choose "default" inventory item */
		    if (p_ptr->command_wrk == USE_INVEN)
		    {
			if (i1 != i2)
			{
			    bell("Illegal object choice (default)!");
			    break;
			}

			k = i1;
		    }

		    /* Choose the "default" slot (0) of the quiver */
		    else if (quiver_tags)
			k = e1;

		    /* Choose "default" equipment item */
		    else if (p_ptr->command_wrk == USE_EQUIP)
		    {
			if (e1 != e2)
			{
			    bell("Illegal object choice (default)!");
			    break;
			}

			k = e1;
		    }

		    /* Choose "default" floor item */
		    else
		    {
			if (f1 != f2)
			{
			    bell("Illegal object choice (default)!");
			    break;
			}

			k = 0 - floor_list[f1];
		    }

		    /* Validate the item */
		    if (!get_item_okay(k))
		    {
			bell("Illegal object choice (default)!");
			break;
		    }

		    /* Allow player to "refuse" certain actions */
		    if (!get_item_allow(k, cmdkey, is_harmless))
		    {
			done = TRUE;
			break;
		    }

		    /* Accept that choice */
		    (*cp) = k;
		    item = TRUE;
		    done = TRUE;
		    break;
		}

	    case '!':
	    {
		/* Try squelched items */
		if (can_squelch) {
		    (*cp) = ALL_SQUELCHED;
		    item = TRUE;
		    done = TRUE;
		    break;
		}

		/* Just fall through */
	    }

	    default:
	    {
		bool verify;

		/* Note verify */
		verify = (isupper((unsigned char) which.key.code) ? TRUE : FALSE);

		/* Lowercase */
		which.key.code = tolower((unsigned char) which.key.code);

		/* Convert letter to inventory index */
		if (p_ptr->command_wrk == USE_INVEN) {
		    k = label_to_inven(which.key.code);

		    if (k < 0) {
			bell("Illegal object choice (inven)!");
			break;
		    }
		}

		/* Convert letter to equipment index */
		else if (p_ptr->command_wrk == USE_EQUIP) {
		    k = label_to_equip(which.key.code);

		    if (k < 0) {
			bell("Illegal object choice (equip)!");
			break;
		    }
		}

		/* Convert letter to floor index */
		else {
		    k = (islower((unsigned char) which.key.code) ? 
			 A2I((unsigned char)which.key.code) : -1);

		    if (k < 0 || k >= floor_num) {
			bell("Illegal object choice (floor)!");
			break;
		    }

		    /* Special index */
		    k = 0 - floor_list[k];
		}

		/* Validate the item */
		if (!get_item_okay(k)) {
		    bell("Illegal object choice (normal)!");
		    break;
		}

		/* Verify the item */
		if (verify && !verify_item("Try", k)) {
		    done = TRUE;
		    break;
		}

		/* Allow player to "refuse" certain actions */
		if (!get_item_allow(k, cmdkey, is_harmless)) {
		    done = TRUE;
		    break;
		}

		/* Accept that choice */
		(*cp) = k;
		item = TRUE;
		done = TRUE;
		break;
	    }
	}
    }

    /* Fix the screen if necessary */
    if (show_list)
    {
	/* Load screen */
	screen_load();

	/* Hack -- Cancel "display" */
	show_list = FALSE;
    }

    /* Kill buttons */
    button_kill('*');
    button_kill('/');
    button_kill('-');
    button_kill('!');

    /* Forget the item_tester_tval restriction */
    item_tester_tval = 0;

    /* Forget the item_tester_hook restriction */
    item_tester_hook = NULL;

    /* Toggle again if needed */
    if (toggle)
	toggle_inven_equip();

    /* Update */
    p_ptr->redraw |= (PR_INVEN | PR_EQUIP);
    redraw_stuff(p_ptr);

    /* Clear the prompt line */
    prt("", 0, 0);

    /* Warning if needed */
    if (oops && str)
	msg(str);

    /* Result */
    return (item);
}