Ejemplo n.º 1
0
/**
 * Attempt to reproduce, if possible.  All monsters are checked here for
 * lore purposes, the unfit fail.
 */
static bool process_monster_multiply(struct chunk *c, struct monster *mon)
{
	int k = 0, y, x;

	struct monster_lore *lore = get_lore(mon->race);

	/* Too many breeders on the level already */
	if (num_repro >= z_info->repro_monster_max) return false;

	/* Count the adjacent monsters */
	for (y = mon->fy - 1; y <= mon->fy + 1; y++)
		for (x = mon->fx - 1; x <= mon->fx + 1; x++)
			if (c->squares[y][x].mon > 0) k++;

	/* Multiply slower in crowded areas */
	if ((k < 4) && (k == 0 || one_in_(k * z_info->repro_monster_rate))) {
		/* Successful breeding attempt, learn about that now */
		if (mflag_has(mon->mflag, MFLAG_VISIBLE))
			rf_on(lore->flags, RF_MULTIPLY);

		/* Leave now if not a breeder */
		if (!rf_has(mon->race->flags, RF_MULTIPLY))
			return false;

		/* Try to multiply */
		if (multiply_monster(mon)) {
			/* Make a sound */
			if (mflag_has(mon->mflag, MFLAG_VISIBLE))
				sound(MSG_MULTIPLY);

			/* Multiplying takes energy */
			return true;
		}
	}

	return false;
}
Ejemplo n.º 2
0
/**
 * Helper function used with ranged_helper by do_cmd_throw.
 */
static struct attack_result make_ranged_throw(object_type *o_ptr, int y, int x) {
	struct attack_result result = {FALSE, 0, 0, "hit"};

	monster_type *m_ptr = cave_monster_at(cave, y, x);
	monster_race *r_ptr = &r_info[m_ptr->r_idx];

	int bonus = p_ptr->state.to_h + o_ptr->to_h;
	int chance = p_ptr->state.skills[SKILL_TO_HIT_THROW] + bonus * BTH_PLUS_ADJ;
	int chance2 = chance - distance(p_ptr->py, p_ptr->px, y, x);

	int multiplier = 1;
	const struct slay *best_s_ptr = NULL;

	/* If we missed then we're done */
	if (!test_hit(chance2, r_ptr->ac, m_ptr->ml)) return result;

	result.success = TRUE;

	improve_attack_modifier(o_ptr, m_ptr, &best_s_ptr, TRUE, FALSE);

	/* If we have a slay, modify the multiplier appropriately */
	if (best_s_ptr != NULL) {
		result.hit_verb = best_s_ptr->range_verb;
		multiplier += best_s_ptr->mult;
		if (best_s_ptr->vuln_flag &&
				rf_has(r_ptr->flags, best_s_ptr->vuln_flag))
			multiplier += 1;
	}

	/* Apply damage: multiplier, slays, criticals, bonuses */
	result.dmg = damroll(o_ptr->dd, o_ptr->ds);
	result.dmg += o_ptr->to_d;
	result.dmg *= multiplier;
	result.dmg = critical_norm(o_ptr->weight, o_ptr->to_h, result.dmg, &result.msg_type);

	return result;
}
Ejemplo n.º 3
0
/* 
 * A rewrite of monster death that gets rid of some features
 * That we don't want to deal with.  Namely, no notifying the
 * player and no generation of Morgoth artifacts
 * 
 * It also replaces drop near with a new function that drops all 
 * the items on the exact square that the monster was on.
 */
void monster_death_stats(int m_idx)
{
	struct object *obj;
	struct monster *mon;
	bool uniq;

	assert(m_idx > 0);
	mon = cave_monster(cave, m_idx);

	/* Check if monster is UNIQUE */
	uniq = rf_has(mon->race->flags,RF_UNIQUE);

	/* Mimicked objects will have already been counted as floor objects */
	mon->mimicked_obj = NULL;

	/* Drop objects being carried */
	obj = mon->held_obj;
	while (obj) {
		struct object *next = obj->next;

		/* Object no longer held */
		obj->held_m_idx = 0;

		/* Get data */
		get_obj_data(obj, mon->fy, mon->fx, true, uniq);

		/* Delete the object */
		delist_object(cave, obj);
		object_delete(&obj);

		/* Next */
		obj = next;
	}

	/* Forget objects */
	mon->held_obj = NULL;
}
Ejemplo n.º 4
0
/**
 * Monster regeneration of HPs.
 */
static void regen_monster(struct monster *mon)
{
	/* Regenerate (if needed) */
	if (mon->hp < mon->maxhp) {
		/* Base regeneration */
		int frac = mon->maxhp / 100;

		/* Minimal regeneration rate */
		if (!frac) frac = 1;

		/* Some monsters regenerate quickly */
		if (rf_has(mon->race->flags, RF_REGENERATE)) frac *= 2;

		/* Regenerate */
		mon->hp += frac;

		/* Do not over-regenerate */
		if (mon->hp > mon->maxhp) mon->hp = mon->maxhp;

		/* Redraw (later) if needed */
		if (player->upkeep->health_who == mon)
			player->upkeep->redraw |= (PR_HEALTH);
	}
}
Ejemplo n.º 5
0
/**
 * Calculate minimum and desired combat ranges.  -BR-
 */
static void find_range(struct monster *mon)
{
	u16b p_lev, m_lev;
	u16b p_chp, p_mhp;
	u16b m_chp, m_mhp;
	u32b p_val, m_val;

	/* Monsters will run up to z_info->flee_range grids out of sight */
	int flee_range = z_info->max_sight + z_info->flee_range;

	/* All "afraid" monsters will run away */
	if (mon->m_timed[MON_TMD_FEAR])
		mon->min_range = flee_range;

	else {

		/* Minimum distance - stay at least this far if possible */
		mon->min_range = 1;

		/* Examine player power (level) */
		p_lev = player->lev;

		/* Hack - increase p_lev based on specialty abilities */

		/* Examine monster power (level plus morale) */
		m_lev = mon->race->level + (mon->midx & 0x08) + 25;

		/* Simple cases first */
		if (m_lev + 3 < p_lev)
			mon->min_range = flee_range;
		else if (m_lev - 5 < p_lev) {

			/* Examine player health */
			p_chp = player->chp;
			p_mhp = player->mhp;

			/* Examine monster health */
			m_chp = mon->hp;
			m_mhp = mon->maxhp;

			/* Prepare to optimize the calculation */
			p_val = (p_lev * p_mhp) + (p_chp << 2);	/* div p_mhp */
			m_val = (m_lev * m_mhp) + (m_chp << 2);	/* div m_mhp */

			/* Strong players scare strong monsters */
			if (p_val * m_mhp > m_val * p_mhp)
				mon->min_range = flee_range;
		}
	}

	if (mon->min_range < flee_range) {
		/* Creatures that don't move never like to get too close */
		if (rf_has(mon->race->flags, RF_NEVER_MOVE))
			mon->min_range += 3;

		/* Spellcasters that don't strike never like to get too close */
		if (rf_has(mon->race->flags, RF_NEVER_BLOW))
			mon->min_range += 3;
	}

	/* Maximum range to flee to */
	if (!(mon->min_range < flee_range))
		mon->min_range = flee_range;

	/* Nearby monsters won't run away */
	else if (mon->cdis < z_info->turn_range)
		mon->min_range = 1;

	/* Now find preferred range */
	mon->best_range = mon->min_range;

	/* Archers are quite happy at a good distance */
	//if (rf_has(mon->race->flags, RF_ARCHER))
	//	mon->best_range += 3;

	if (mon->race->freq_spell > 24) {
		/* Breathers like point blank range */
		if (flags_test(mon->race->spell_flags, RSF_SIZE, RSF_BREATH_MASK,
					   FLAG_END)
			&& (mon->best_range < 6) && (mon->hp > mon->maxhp / 2))
			mon->best_range = 6;

		/* Other spell casters will sit back and cast */
		else
			mon->best_range += 3;
	}
}
Ejemplo n.º 6
0
/**
 * Draw a visible path over the squares between (x1,y1) and (x2,y2).
 *
 * The path consists of "*", which are white except where there is a
 * monster, object or feature in the grid.
 *
 * This routine has (at least) three weaknesses:
 * - remembered objects/walls which are no longer present are not shown,
 * - squares which (e.g.) the player has walked through in the dark are
 *   treated as unknown space.
 * - walls which appear strange due to hallucination aren't treated correctly.
 *
 * The first two result from information being lost from the dungeon arrays,
 * which requires changes elsewhere
 */
static int draw_path(u16b path_n, struct loc *path_g, wchar_t *c, int *a,
					 int y1, int x1)
{
	int i;
	bool on_screen;

	/* No path, so do nothing. */
	if (path_n < 1) return 0;

	/* The starting square is never drawn, but notice if it is being
     * displayed. In theory, it could be the last such square.
     */
	on_screen = panel_contains(y1, x1);

	/* Draw the path. */
	for (i = 0; i < path_n; i++) {
		byte colour;

		/* Find the co-ordinates on the level. */
		int y = path_g[i].y;
		int x = path_g[i].x;
		struct monster *mon = square_monster(cave, y, x);
		struct object *obj = square_object(cave, y, x);

		/*
		 * As path[] is a straight line and the screen is oblong,
		 * there is only section of path[] on-screen.
		 * If the square being drawn is visible, this is part of it.
		 * If none of it has been drawn, continue until some of it
		 * is found or the last square is reached.
		 * If some of it has been drawn, finish now as there are no
		 * more visible squares to draw.
		 */
		 if (panel_contains(y,x)) on_screen = TRUE;
		 else if (on_screen) break;
		 else continue;

	 	/* Find the position on-screen */
		move_cursor_relative(y,x);

		/* This square is being overwritten, so save the original. */
		Term_what(Term->scr->cx, Term->scr->cy, a+i, c+i);

		/* Choose a colour. */
		if (mon && mflag_has(mon->mflag, MFLAG_VISIBLE)) {
			/* Mimics act as objects */
			if (rf_has(mon->race->flags, RF_UNAWARE)) 
				colour = COLOUR_YELLOW;
			else
				/* Visible monsters are red. */
				colour = COLOUR_L_RED;
		} else if (obj && obj->marked)
			/* Known objects are yellow. */
			colour = COLOUR_YELLOW;

		else if ((!square_isprojectable(cave, y,x) && square_ismark(cave, y, x))
				 || square_isseen(cave, y, x))
			/* Known walls are blue. */
			colour = COLOUR_BLUE;

		else if (!square_ismark(cave, y, x) && !square_isseen(cave, y, x))
			/* Unknown squares are grey. */
			colour = COLOUR_L_DARK;

		else
			/* Unoccupied squares are white. */
			colour = COLOUR_WHITE;

		/* Draw the path segment */
		(void)Term_addch(colour, L'*');
	}
	return i;
}
Ejemplo n.º 7
0
/**
 * Choose "logical" directions for monster movement
 */
static bool get_moves(struct chunk *c, struct monster *mon, int *dir)
{
	int py = player->py;
	int px = player->px;

	int y, x;

	/* Monsters will run up to z_info->flee_range grids out of sight */
	int flee_range = z_info->max_sight + z_info->flee_range;

	bool done = false;

	/* Calculate range */
	find_range(mon);

	/* Flow towards the player */
	if (get_moves_flow(c, mon)) {
		/* Extract the "pseudo-direction" */
		y = mon->ty - mon->fy;
		x = mon->tx - mon->fx;
	} else {
		/* Head straight for the player */
		y = player->py - mon->fy;
		x = player->px - mon->fx;
	}

	/* Normal animal packs try to get the player out of corridors. */
	if (rf_has(mon->race->flags, RF_GROUP_AI) &&
	    !flags_test(mon->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL,
					FLAG_END)) {
		int i, open = 0;

		/* Count empty grids next to player */
		for (i = 0; i < 8; i++) {
			int ry = py + ddy_ddd[i];
			int rx = px + ddx_ddd[i];
			/* Check grid around the player for room interior (room walls count)
			 * or other empty space */
			if (square_ispassable(c, ry, rx) || square_isroom(c, ry, rx)) {
				/* One more open grid */
				open++;
			}
		}

		/* Not in an empty space and strong player */
		if ((open < 7) && (player->chp > player->mhp / 2)) {
			/* Find hiding place */
			if (find_hiding(c, mon)) {
				done = true;
				y = mon->ty - mon->fy;
				x = mon->tx - mon->fx;
			}
		}
	}

	/* Apply fear */
	if (!done && (mon->min_range == flee_range)) {
		/* Try to find safe place */
		if (!find_safety(c, mon)) {
			/* Just leg it away from the player */
			y = (-y);
			x = (-x);
		} else {
			/* Set a course for the safe place */
			get_moves_fear(c, mon);
			y = mon->ty - mon->fy;
			x = mon->tx - mon->fx;
		}

		done = true;
	}

	/* Monster groups try to surround the player */
	if (!done && rf_has(mon->race->flags, RF_GROUP_AI)) {
		int i, yy = mon->ty, xx = mon->tx;

		/* If we are not already adjacent */
		if (mon->cdis > 1) {
			/* Find an empty square near the player to fill */
			int tmp = randint0(8);
			for (i = 0; i < 8; i++) {
				/* Pick squares near player (pseudo-randomly) */
				yy = py + ddy_ddd[(tmp + i) & 7];
				xx = px + ddx_ddd[(tmp + i) & 7];

				/* Ignore filled grids */
				if (!square_isempty(cave, yy, xx)) continue;

				/* Try to fill this hole */
				break;
			}
		}

		/* Extract the new "pseudo-direction" */
		y = yy - mon->fy;
		x = xx - mon->fx;
	}

	/* Check for no move */
	if (!x && !y) return (false);

	/* Pick the correct direction */
	*dir = choose_direction(y, x);

	/* Want to move */
	return (true);
}
Ejemplo n.º 8
0
/*
 * Examine a grid, return a keypress.
 *
 * The "mode" argument contains the "TARGET_LOOK" bit flag, which
 * indicates that the "space" key should scan through the contents
 * of the grid, instead of simply returning immediately.  This lets
 * the "look" command get complete information, without making the
 * "target" command annoying.
 *
 * The "info" argument contains the "commands" which should be shown
 * inside the "[xxx]" text.  This string must never be empty, or grids
 * containing monsters will be displayed with an extra comma.
 *
 * Note that if a monster is in the grid, we update both the monster
 * recall info and the health bar info to track that monster.
 *
 * This function correctly handles multiple objects per grid, and objects
 * and terrain features in the same grid, though the latter never happens.
 *
 * This function must handle blindness/hallucination.
 */
static ui_event_data target_set_interactive_aux(int y, int x, int mode)
{
	s16b this_o_idx = 0, next_o_idx = 0;

	cptr s1, s2, s3;

	bool boring;

	bool floored;

	int feat;

	int floor_list[MAX_FLOOR_STACK];
	int floor_num;

	ui_event_data query;

	char out_val[256];

	char coords[20];
	
	/* Describe the square location */
	coords_desc(coords, sizeof(coords), y, x);

	/* Repeat forever */
	while (1)
	{
		/* Paranoia */
		query.key = ' ';

		/* Assume boring */
		boring = TRUE;

		/* Default */
		s1 = "You see ";
		s2 = "";
		s3 = "";


		/* The player */
		if (cave_m_idx[y][x] < 0)
		{
			/* Description */
			s1 = "You are ";

			/* Preposition */
			s2 = "on ";
		}

		/* Hack -- hallucination */
		if (p_ptr->timed[TMD_IMAGE])
		{
			cptr name = "something strange";

			/* Display a message */
			if (p_ptr->wizard)
			{
				strnfmt(out_val, sizeof(out_val),
						"%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x);
			}
			else
			{
				strnfmt(out_val, sizeof(out_val),
						"%s%s%s%s, %s.", s1, s2, s3, name, coords);
			}

			prt(out_val, 0, 0);
			move_cursor_relative(y, x);
			query = inkey_ex();

			/* Stop on everything but "return" */
			if ((query.key != '\n') && (query.key != '\r')) break;

			/* Repeat forever */
			continue;
		}

		/* Actual monsters */
		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];

			/* Visible */
			if (m_ptr->ml)
			{
				bool recall = FALSE;

				char m_name[80];

				/* Not boring */
				boring = FALSE;

				/* Get the monster name ("a kobold") */
				monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND2);

				/* Hack -- track this monster race */
				monster_race_track(m_ptr->r_idx);

				/* Hack -- health bar for this monster */
				health_track(cave_m_idx[y][x]);

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

				/* Interact */
				while (1)
				{
					/* Recall */
					if (recall)
					{
						/* Save screen */
						screen_save();

						/* Recall on screen */
						screen_roff(m_ptr->r_idx);

						/* Command */
						query = inkey_ex();

						/* Load screen */
						screen_load();
					}

					/* Normal */
					else
					{
						char buf[80];

						/* Describe the monster */
						look_mon_desc(buf, sizeof(buf), cave_m_idx[y][x]);

						/* Describe, and prompt for recall */
						if (p_ptr->wizard)
						{
							strnfmt(out_val, sizeof(out_val),
									"%s%s%s%s (%s), %s (%d:%d).",
									s1, s2, s3, m_name, buf, coords, y, x);
						}
						else
						{
							strnfmt(out_val, sizeof(out_val),
									"%s%s%s%s (%s), %s.",
									s1, s2, s3, m_name, buf, coords);
						}

						prt(out_val, 0, 0);

						/* Place cursor */
						move_cursor_relative(y, x);

						/* Command */
						query = inkey_ex();
					}

					/* Normal commands */
					if (query.key != 'r') break;

					/* Toggle recall */
					recall = !recall;
				}

				/* Stop on everything but "return"/"space" */
				if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break;

				/* Sometimes stop at "space" key */
				if ((query.key == ' ') && !(mode & (TARGET_LOOK))) break;

				/* Change the intro */
				s1 = "It is ";

				/* Hack -- take account of gender */
				if (rf_has(r_ptr->flags, RF_FEMALE)) s1 = "She is ";
				else if (rf_has(r_ptr->flags, RF_MALE)) s1 = "He is ";

				/* Use a preposition */
				s2 = "carrying ";

				/* Scan all objects being carried */
				for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
				{
					char o_name[80];

					object_type *o_ptr;

					/* Get the object */
					o_ptr = &o_list[this_o_idx];

					/* Get the next object */
					next_o_idx = o_ptr->next_o_idx;

					/* Obtain an object description */
					object_desc(o_name, sizeof(o_name), o_ptr,
								ODESC_PREFIX | ODESC_FULL);

					/* Describe the object */
					if (p_ptr->wizard)
					{
						strnfmt(out_val, sizeof(out_val),
								"%s%s%s%s, %s (%d:%d).",
								s1, s2, s3, o_name, coords, y, x);
					}
					else
					{
						strnfmt(out_val, sizeof(out_val),
								"%s%s%s%s, %s.", s1, s2, s3, o_name, coords);
					}

					prt(out_val, 0, 0);
					move_cursor_relative(y, x);
					query = inkey_ex();

					/* Stop on everything but "return"/"space" */
					if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break;

					/* Sometimes stop at "space" key */
					if ((query.key == ' ') && !(mode & (TARGET_LOOK))) break;

					/* Change the intro */
					s2 = "also carrying ";
				}

				/* Double break */
				if (this_o_idx) break;

				/* Use a preposition */
				s2 = "on ";
			}
		}


		/* Assume not floored */
		floored = FALSE;

		floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), y, x, 0x02);

		/* Scan all marked objects in the grid */
		if ((floor_num > 0) &&
		    (!(p_ptr->timed[TMD_BLIND]) || (y == p_ptr->py && x == p_ptr->px)))
		{
			/* Not boring */
			boring = FALSE;

			track_object(-floor_list[0]);
			handle_stuff();

			/* If there is more than one item... */
			if (floor_num > 1) while (1)
			{
				floored = TRUE;

				/* Describe the pile */
				if (p_ptr->wizard)
				{
					strnfmt(out_val, sizeof(out_val),
							"%s%s%sa pile of %d objects, %s (%d:%d).",
							s1, s2, s3, floor_num, coords, y, x);
				}
				else
				{
					strnfmt(out_val, sizeof(out_val),
							"%s%s%sa pile of %d objects, %s.",
							s1, s2, s3, floor_num, coords);
				}

				prt(out_val, 0, 0);
				move_cursor_relative(y, x);
				query = inkey_ex();

				/* Display objects */
				if (query.key == 'r')
				{
					int rdone = 0;
					int pos;
					while (!rdone)
					{
						/* Save screen */
						screen_save();

						/* Display */
						show_floor(floor_list, floor_num, (OLIST_WEIGHT | OLIST_GOLD));

						/* Describe the pile */
						prt(out_val, 0, 0);
						query = inkey_ex();

						/* Load screen */
						screen_load();

						pos = query.key - 'a';
						if (0 <= pos && pos < floor_num)
						{
							track_object(-floor_list[pos]);
							handle_stuff();
							continue;
						}
						rdone = 1;
					}

					/* Now that the user's done with the display loop, let's */
					/* the outer loop over again */
					continue;
				}

				/* Done */
				break;
			}
			/* Only one object to display */
			else
			{

				char o_name[80];

				/* Get the single object in the list */
				object_type *o_ptr = &o_list[floor_list[0]];

				/* Not boring */
				boring = FALSE;

				/* Obtain an object description */
				object_desc(o_name, sizeof(o_name), o_ptr,
							ODESC_PREFIX | ODESC_FULL);

				/* Describe the object */
				if (p_ptr->wizard)
				{
					strnfmt(out_val, sizeof(out_val),
							"%s%s%s%s, %s (%d:%d).",
							s1, s2, s3, o_name, coords, y, x);
				}
				else
				{
					strnfmt(out_val, sizeof(out_val),
							"%s%s%s%s, %s.", s1, s2, s3, o_name, coords);
				}

				prt(out_val, 0, 0);
				move_cursor_relative(y, x);
				query = inkey_ex();

				/* Stop on everything but "return"/"space" */
				if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break;

				/* Sometimes stop at "space" key */
				if ((query.key == ' ') && !(mode & (TARGET_LOOK))) break;

				/* Change the intro */
				s1 = "It is ";

				/* Plurals */
				if (o_ptr->number != 1) s1 = "They are ";

				/* Preposition */
				s2 = "on ";
			}

		}

		/* Double break */
		if (this_o_idx) break;


		/* Feature (apply "mimic") */
		feat = f_info[cave_feat[y][x]].mimic;

		/* Require knowledge about grid, or ability to see grid */
		if (!(cave_info[y][x] & (CAVE_MARK)) && !player_can_see_bold(y,x))
		{
			/* Forget feature */
			feat = FEAT_NONE;
		}

		/* Terrain feature if needed */
		if (boring || (feat > FEAT_INVIS))
		{
			cptr name = f_info[feat].name;

			/* Hack -- handle unknown grids */
			if (feat == FEAT_NONE) name = "unknown grid";

			/* Pick a prefix */
			if (*s2 && (feat >= FEAT_DOOR_HEAD)) s2 = "in ";

			/* Pick proper indefinite article */
			s3 = (is_a_vowel(name[0])) ? "an " : "a ";

			/* Hack -- special introduction for store doors */
			if ((feat >= FEAT_SHOP_HEAD) && (feat <= FEAT_SHOP_TAIL))
			{
				s3 = "the entrance to the ";
			}

			/* Display a message */
			if (p_ptr->wizard)
			{
				strnfmt(out_val, sizeof(out_val),
						"%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x);
			}
			else
			{
				strnfmt(out_val, sizeof(out_val),
						"%s%s%s%s, %s.", s1, s2, s3, name, coords);
			}

			prt(out_val, 0, 0);
			move_cursor_relative(y, x);
			query = inkey_ex();

			/* Stop on everything but "return"/"space" */
			if ((query.key != '\n') && (query.key != '\r') && (query.key != ' ')) break;
		}

		/* Stop on everything but "return" */
		if ((query.key != '\n') && (query.key != '\r')) break;
	}

	/* Keep going */
	return (query);
}
Ejemplo n.º 9
0
/**
 * Evaluate the whole monster list and write a new one.  power and scaled_power
 * are always adjusted, level, rarity and mexp only if requested.
 */
errr eval_monster_power(struct monster_race *racelist)
{
	int i, j, iteration;
	byte lvl;
	struct monster_race *race = NULL;
	ang_file *mon_fp;
	char buf[1024];
	bool dump = FALSE;
	bool wrote = TRUE;

	/* Allocate arrays */
	power = mem_zalloc(z_info->r_max * sizeof(long));
	scaled_power = mem_zalloc(z_info->r_max * sizeof(long));
	final_hp = mem_zalloc(z_info->r_max * sizeof(long));
	final_melee_dam = mem_zalloc(z_info->r_max * sizeof(long));
	final_spell_dam = mem_zalloc(z_info->r_max * sizeof(long));
	highest_threat = mem_zalloc(z_info->r_max * sizeof(int));

	for (iteration = 0; iteration < 3; iteration ++) {
		long hp, av_hp, dam, av_dam;
		long *tot_hp = mem_zalloc(z_info->max_depth * sizeof(long));
		long *tot_dam = mem_zalloc(z_info->max_depth * sizeof(long));
		long *mon_count = mem_zalloc(z_info->max_depth * sizeof(long));

		/* Reset the sum of all monster power values */
		tot_mon_power = 0;

		/* Go through r_info and evaluate power ratings & flows. */
		for (i = 0; i < z_info->r_max; i++)	{

			/* Point at the "info" */
			race = &racelist[i];

			/* Set the current level */
			lvl = race->level;

			/* Maximum damage this monster can do in 10 game turns */
			dam = eval_max_dam(race, i);

			/* Adjust hit points based on resistances */
			hp = eval_hp_adjust(race);

			/* Hack -- set exp */
			if (lvl == 0)
				race->mexp = 0L;
			else {
				/* Compute depths of non-unique monsters */
				if (!rf_has(race->flags, RF_UNIQUE)) {
					long mexp = (hp * dam) / 25;
					long threat = highest_threat[i];

					/* Compute level algorithmically */
					for (j = 1; (mexp > j + 4) || (threat > j + 5);
						 mexp -= j * j, threat -= (j + 4), j++);

					/* Set level */
					lvl = MIN(( j > 250 ? 90 + (j - 250) / 20 : /* Level 90+ */
								(j > 130 ? 70 + (j - 130) / 6 :	/* Level 70+ */
								 (j > 40 ? 40 + (j - 40) / 3 :	/* Level 40+ */
								  j))), 99);

					/* Set level */
					if (arg_rebalance)
						race->level = lvl;
				}

				if (arg_rebalance) {
					/* Hack -- for Ungoliant */
					if (hp > 10000)
						race->mexp = (hp / 25) * (dam / lvl);
					else race->mexp = (hp * dam) / (lvl * 25);

					/* Round to 2 significant figures */
					if (race->mexp > 100) {
						if (race->mexp < 1000) {
							race->mexp = (race->mexp + 5) / 10;
							race->mexp *= 10;
						}
						else if (race->mexp < 10000) {
							race->mexp = (race->mexp + 50) / 100;
							race->mexp *= 100;
						}
						else if (race->mexp < 100000) {
							race->mexp = (race->mexp + 500) / 1000;
							race->mexp *= 1000;
						}
						else if (race->mexp < 1000000) {
							race->mexp = (race->mexp + 5000) / 10000;
							race->mexp *= 10000;
						}
						else if (race->mexp < 10000000) {
							race->mexp = (race->mexp + 50000) / 100000;
							race->mexp *= 100000;
						}
					}
				}
			}

			/* If we're rebalancing, this is a nop, if not, we restore the
			 * orig value */
			lvl = race->level;
			if ((lvl) && (race->mexp < 1L))
				race->mexp = 1L;

			/*
			 * Hack - We have to use an adjustment factor to prevent overflow.
			 * Try to scale evenly across all levels instead of scaling by level
			 */
			hp /= 2;
			if(hp < 1)
				hp = 1;
			final_hp[i] = hp;

			/* Define the power rating */
			power[i] = hp * dam;

			/* Adjust for group monsters, using somewhat arbitrary 
			 * multipliers for now */
			if (!rf_has(race->flags, RF_UNIQUE)) {
				if (race->friends)
					power[i] *= 3;
			}

			/* Adjust for escorts */
			if (race->friends_base) 
				power[i] *= 2;


			/* Adjust for multiplying monsters. This is modified by the speed,
			 * as fast multipliers are much worse than slow ones. We also
			 * adjust for ability to bypass walls or doors. */
			if (rf_has(race->flags, RF_MULTIPLY)) {
				int adj_power;

				if (flags_test(race->flags, RF_SIZE, RF_KILL_WALL,
							   RF_PASS_WALL, FLAG_END))
					adj_power = power[i] * adj_energy(race);
				else if (flags_test(race->flags, RF_SIZE, RF_OPEN_DOOR,
									RF_BASH_DOOR, FLAG_END))
					adj_power = power[i] * adj_energy(race) * 3 / 2;
				else
					adj_power = power[i] * adj_energy(race) / 2;

				power[i] = MAX(power[i], adj_power);
			}

			/* Update the running totals - these will be used as divisors later
			 * Total HP / dam / count for everything up to the current level */
			for (j = lvl; j < (lvl == 0 ? lvl + 1: z_info->max_depth); j++)	{
				int count = 10;

				/* Uniques don't count towards monster power on the level. */
				if (rf_has(race->flags, RF_UNIQUE)) continue;

				/* Specifically placed monsters don't count towards monster
				 * power on the level. */
				if (!(race->rarity)) continue;

				/* Hack -- provide adjustment factor to prevent overflow */
				if ((j == 90) && (race->level < 90)) {
					hp /= 10;
					dam /= 10;
				}

				if ((j == 65) && (race->level < 65)) {
					hp /= 10;
					dam /= 10;
				}

				if ((j == 40) && (race->level < 40)) {
					hp /= 10;
					dam /= 10;
				}

				/* Hack - if it's a group monster or multiplying monster, add
				 * several to the count so the averages don't get thrown off */

				if (race->friends || race->friends_base)
					count = 15;

				if (rf_has(race->flags, RF_MULTIPLY)) {
					int adj_energy_amt;

					if (flags_test(race->flags, RF_SIZE, RF_KILL_WALL,
								   RF_PASS_WALL, FLAG_END))
						adj_energy_amt = adj_energy(race);
					else if (flags_test(race->flags, RF_SIZE, RF_OPEN_DOOR,
										RF_BASH_DOOR, FLAG_END))
						adj_energy_amt = adj_energy(race) * 3 / 2;
					else
						adj_energy_amt = adj_energy(race) / 2;

					count = MAX(1, adj_energy_amt) * count;
				}

				/* Very rare monsters count less towards total monster power
				 * on the level. */
				if (race->rarity > count) {
					hp = hp * count / race->rarity;
					dam = dam * count / race->rarity;

					count = race->rarity;
				}

				tot_hp[j] += hp;
				tot_dam[j] += dam;

				mon_count[j] += count / race->rarity;
			}

		}

		/* Apply divisors now */
		for (i = 0; i < z_info->r_max; i++) {
			int new_power;

			/* Point at the "info" */
			race = &racelist[i];

			/* Extract level */
			lvl = race->level;

			/* Paranoia */
			if (tot_hp[lvl] != 0 && tot_dam[lvl] != 0) {
				scaled_power[i] = power[i];

				/* Divide by av HP and av damage for all in-level monsters */
				/* Note we have factored in the above 'adjustment factor' */
				av_hp = tot_hp[lvl] * 10 / mon_count[lvl];
				av_dam = tot_dam[lvl] * 10 / mon_count[lvl];

				/* Justifiable paranoia - avoid divide by zero errors */
				if (av_hp > 0)
					scaled_power[i] = scaled_power[i] / av_hp;
				if (av_dam > 0)
					scaled_power[i] = scaled_power[i] / av_dam;

				/* Never less than 1 */
				if (power[i] < 1)
					power[i] = 1;

				/* Set powers */
				if (arg_rebalance) {
					race->power = power[i];
					race->scaled_power = scaled_power[i];
				}

				/* Get power */
				new_power = power[i];

				/* Compute rarity algorithmically */
				for (j = 1; new_power > j; new_power -= j * j, j++);

				/* Set rarity */
				if (arg_rebalance)
					race->rarity = j;
			}
		}

		mem_free(mon_count);
		mem_free(tot_dam);
		mem_free(tot_hp);
	}

	/* Determine total monster power */
	for (i = 0; i < z_info->r_max; i++)
		tot_mon_power += r_info[i].scaled_power;

	if (dump) {
		/* Dump the power details */
		path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "mon_power.txt");
		mon_fp = file_open(buf, MODE_WRITE, FTYPE_TEXT);

		file_putf(mon_fp, "ridx|level|rarity|d_char|name|pwr|scaled|melee|spell|hp\n");

		for (i = 0; i < z_info->r_max; i++) {
			char mbstr[MB_LEN_MAX + 1] = { 0 };
			race = &r_info[i];

			/* Don't print anything for nonexistent monsters */
			if (!race->name) continue;

			wctomb(mbstr, race->d_char);
			file_putf(mon_fp, "%d|%d|%d|%s|%s|%d|%d|%d|%d|%d\n", race->ridx,
				race->level, race->rarity, mbstr, race->name,
				power[i], scaled_power[i], final_melee_dam[i],
				final_spell_dam[i], final_hp[i]);
		}

		file_close(mon_fp);
	}

	/* Write to the user directory */
	path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "new_monster.txt");

	if (text_lines_to_file(buf, write_monster_entries)) {
		msg("Failed to create file %s.new", buf);
		wrote = FALSE;
	}

	/* Free power arrays */
	mem_free(highest_threat);
	mem_free(final_spell_dam);
	mem_free(final_melee_dam);
	mem_free(final_hp);
	mem_free(scaled_power);
	mem_free(power);

	/* Success */
	return wrote ? 0 : -1;
}
Ejemplo n.º 10
0
/**
 * Show and delete the stacked monster messages.
 * Some messages are delayed so that they show up after everything else.
 * This is to avoid things like "The snaga dies. The snaga runs in fear!"
 * So we only flush messages matching the delay parameter.
 */
static void flush_monster_messages(bool delay)
{
	int i, r_idx, count;
	const monster_race *r_ptr;
	char buf[512];
	char *action;
	bool action_only;

	/* Show every message */
	for (i = 0; i < size_mon_msg; i++) {
		if (mon_msg[i].delay != delay) continue;
   
		/* Cache the monster count */
		count = mon_msg[i].mon_count;

		/* Paranoia */
		if (count < 1) continue;

		/* Start with an empty string */
		buf[0] = '\0';

		/* Cache the race index */
		r_idx = mon_msg[i].mon_race;
		   
		/* Get the proper message action */
		action = get_mon_msg_action(mon_msg[i].msg_code, (count > 1),
			&r_info[r_idx]);

		/* Is it a regular race? */
		if (r_idx > 0) {
		   /* Get the race */
		   r_ptr = &r_info[r_idx];
		}
		/* It's the special mark for non-visible monsters */
		else {
		   /* No race */
		   r_ptr = NULL;
		}

		/* Monster is marked as invisible */
		if(mon_msg[i].mon_flags & 0x04) r_ptr = NULL;

		/* Special message? */
		action_only = (*action == '~');

		/* Format the proper message for visible monsters */
		if (r_ptr && !action_only)
		{
			char race_name[80];

			/* Get the race name */
			my_strcpy(race_name, r_ptr->name, sizeof(race_name));

			/* Uniques */
			if (rf_has(r_ptr->flags, RF_UNIQUE)) {
				/* Just copy the race name */
				my_strcpy(buf, (r_ptr->name), sizeof(buf));
			}
			/* We have more than one monster */
			else if (count > 1) {
				/* Get the plural of the race name */
				plural_aux(race_name, sizeof(race_name));

				/* Put the count and the race name together */
				strnfmt(buf, sizeof(buf), "%d %s", count, race_name);
			}
			/* Normal lonely monsters */
			else {
				/* Just add a slight flavor */
				strnfmt(buf, sizeof(buf), "the %s", race_name);
			}
		}
		/* Format the message for non-viewable monsters if necessary */
		else if (!r_ptr && !action_only) {
			if (count > 1) {
				/* Show the counter */
				strnfmt(buf, sizeof(buf), "%d monsters", count);
			}
			else {
				/* Just one non-visible monster */
				my_strcpy(buf, "it", sizeof(buf));
			}
		}

		/* Special message. Nuke the mark */
		if (action_only)
			++action;
		/* Regular message */
		else {
			/* Add special mark. Monster is offscreen */
			if (mon_msg[i].mon_flags & 0x02) my_strcat(buf, " (offscreen)",
					sizeof(buf));
        
			/* Add the separator */
			my_strcat(buf, " ", sizeof(buf));
		}

		/* Append the action to the message */
		my_strcat(buf, action, sizeof(buf));

		/* Capitalize the message */
		*buf = toupper((unsigned char)*buf);

		/* Hack - play sound for fear message */
		if (mon_msg[i].msg_code == MON_MSG_FLEE_IN_TERROR)
			sound(MSG_FLEE);

		/* Show the message */
		msg(buf);
   }
}
Ejemplo n.º 11
0
static bool describe_origin(textblock *tb, const object_type *o_ptr)
{
	char origin_text[80];

	if (o_ptr->origin_depth)
		strnfmt(origin_text, sizeof(origin_text), "%d feet (level %d)",
		        o_ptr->origin_depth * 50, o_ptr->origin_depth);
	else
		my_strcpy(origin_text, "town", sizeof(origin_text));

	switch (o_ptr->origin)
	{
		case ORIGIN_NONE:
		case ORIGIN_MIXED:
		case ORIGIN_STOLEN:
			return FALSE;

		case ORIGIN_BIRTH:
			textblock_append(tb, "An inheritance from your family.\n");
			break;

		case ORIGIN_STORE:
			textblock_append(tb, "Bought from a store.\n");
			break;

		case ORIGIN_FLOOR:
			textblock_append(tb, "Found lying on the floor %s %s.\n",
			         (o_ptr->origin_depth ? "at" : "in"),
			         origin_text);
 			break;

		case ORIGIN_PIT:
			textblock_append(tb, "Found lying on the floor in a pit at %s.\n",
			         origin_text);
 			break;

		case ORIGIN_VAULT:
			textblock_append(tb, "Found lying on the floor in a vault at %s.\n",
			         origin_text);
 			break;

		case ORIGIN_SPECIAL:
			textblock_append(tb, "Found lying on the floor of a special room at %s.\n",
			         origin_text);
 			break;

		case ORIGIN_LABYRINTH:
			textblock_append(tb, "Found lying on the floor of a labyrinth at %s.\n",
			         origin_text);
 			break;

		case ORIGIN_CAVERN:
			textblock_append(tb, "Found lying on the floor of a cavern at %s.\n",
			         origin_text);
 			break;

		case ORIGIN_RUBBLE:
			textblock_append(tb, "Found under some rubble at %s.\n",
			         origin_text);
 			break;

		case ORIGIN_DROP:
		case ORIGIN_DROP_SPECIAL:
		case ORIGIN_DROP_PIT:
		case ORIGIN_DROP_VAULT:
		case ORIGIN_DROP_SUMMON:
		case ORIGIN_DROP_BREED:
		case ORIGIN_DROP_POLY:
		case ORIGIN_DROP_WIZARD:
		{
			const char *name;

			if (r_info[o_ptr->origin_xtra].ridx)
				name = r_info[o_ptr->origin_xtra].name;
			else
				name = "monster lost to history";

			textblock_append(tb, "Dropped by ");

			if (rf_has(r_info[o_ptr->origin_xtra].flags, RF_UNIQUE))
				textblock_append(tb, "%s", name);
			else
				textblock_append(tb, "%s%s",
						is_a_vowel(name[0]) ? "an " : "a ", name);

			textblock_append(tb, " %s %s.\n",
					(o_ptr->origin_depth ? "at" : "in"),
					origin_text);
 			break;
		}

		case ORIGIN_DROP_UNKNOWN:
			textblock_append(tb, "Dropped by an unknown monster %s %s.\n",
					(o_ptr->origin_depth ? "at" : "in"),
					origin_text);
			break;

		case ORIGIN_ACQUIRE:
			textblock_append(tb, "Conjured forth by magic %s %s.\n",
					(o_ptr->origin_depth ? "at" : "in"),
					origin_text);
 			break;

		case ORIGIN_CHEAT:
			textblock_append(tb, "Created by debug option.\n");
 			break;

		case ORIGIN_CHEST:
			textblock_append(tb, "Found in a chest from %s.\n",
			         origin_text);
			break;
	}

	textblock_append(tb, "\n");

	return TRUE;
}
Ejemplo n.º 12
0
/**
 * Allow one item to "absorb" another, assuming they are similar.
 *
 * The blending of the "note" field assumes that either (1) one has an
 * inscription and the other does not, or (2) neither has an inscription.
 * In both these cases, we can simply use the existing note, unless the
 * blending object has a note, in which case we use that note.
 *
* These assumptions are enforced by the "object_similar()" code.
 */
static void object_absorb_merge(struct object *obj1, const struct object *obj2)
{
	int total;

	/* First object gains any extra knowledge from second */
	if (obj1->known && obj2->known) {
		if (obj2->known->effect)
			obj1->known->effect = obj1->effect;
		player_know_object(player, obj1);
	}

	/* Merge inscriptions */
	if (obj2->note)
		obj1->note = obj2->note;

	/* Combine timeouts for rod stacking */
	if (tval_can_have_timeout(obj1))
		obj1->timeout += obj2->timeout;

	/* Combine pvals for wands and staves */
	if (tval_can_have_charges(obj1) || tval_is_money(obj1)) {
		total = obj1->pval + obj2->pval;
		obj1->pval = total >= MAX_PVAL ? MAX_PVAL : total;
	}

	/* Combine origin data as best we can */
	if (obj1->origin != obj2->origin ||
		obj1->origin_depth != obj2->origin_depth ||
		obj1->origin_xtra != obj2->origin_xtra) {
		int act = 2;

		if (obj1->origin_xtra && obj2->origin_xtra) {
			struct monster_race *race1 = &r_info[obj1->origin_xtra];
			struct monster_race *race2 = &r_info[obj2->origin_xtra];

			bool r1_uniq = rf_has(race1->flags, RF_UNIQUE) ? true : false;
			bool r2_uniq = rf_has(race2->flags, RF_UNIQUE) ? true : false;

			if (r1_uniq && !r2_uniq) act = 0;
			else if (r2_uniq && !r1_uniq) act = 1;
			else act = 2;
		}

		switch (act)
		{
				/* Overwrite with obj2 */
			case 1:
			{
				obj1->origin = obj2->origin;
				obj1->origin_depth = obj2->origin_depth;
				obj1->origin_xtra = obj2->origin_xtra;
			}

				/* Set as "mixed" */
			case 2:
			{
				obj1->origin = ORIGIN_MIXED;
			}
		}
	}
}
Ejemplo n.º 13
0
/**
 * Work out if a monster can move through the grid, if necessary bashing 
 * down doors in the way.
 *
 * Returns true if the monster is able to move through the grid.
 */
static bool process_monster_can_move(struct chunk *c, struct monster *mon,
		const char *m_name, int nx, int ny, bool *did_something)
{
	struct monster_lore *lore = get_lore(mon->race);

	/* Only fiery creatures can handle lava */
	if (square_isfiery(c, ny, nx) && !rf_has(mon->race->flags, RF_IM_FIRE))
		return false;

	/* Floor is open? */
	if (square_ispassable(c, ny, nx))
		return true;

	/* Permanent wall in the way */
	if (square_iswall(c, ny, nx) && square_isperm(c, ny, nx))
		return false;

	/* Normal wall, door, or secret door in the way */

	/* There's some kind of feature in the way, so learn about
	 * kill-wall and pass-wall now */
	if (mflag_has(mon->mflag, MFLAG_VISIBLE)) {
		rf_on(lore->flags, RF_PASS_WALL);
		rf_on(lore->flags, RF_KILL_WALL);
	}

	/* Monster may be able to deal with walls and doors */
	if (rf_has(mon->race->flags, RF_PASS_WALL)) {
		return true;
	} else if (rf_has(mon->race->flags, RF_KILL_WALL)) {
		/* Remove the wall */
		square_destroy_wall(c, ny, nx);

		/* Note changes to viewable region */
		if (square_isview(c, ny, nx))
			player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS);

		/* Fully update the flow since terrain changed */
		player->upkeep->update |= (PU_FORGET_FLOW | PU_UPDATE_FLOW);

		return true;
	} else if (square_iscloseddoor(c, ny, nx) ||
			   square_issecretdoor(c, ny, nx)) {
		bool may_bash = rf_has(mon->race->flags, RF_BASH_DOOR) && one_in_(2);

		/* Take a turn */
		*did_something = true;

		/* Learn about door abilities */
		if (mflag_has(mon->mflag, MFLAG_VISIBLE)) {
			rf_on(lore->flags, RF_OPEN_DOOR);
			rf_on(lore->flags, RF_BASH_DOOR);
		}

		/* Creature can open or bash doors */
		if (!rf_has(mon->race->flags, RF_OPEN_DOOR) &&
			!rf_has(mon->race->flags, RF_BASH_DOOR))
			return false;

		/* Stuck door -- try to unlock it */
		if (square_islockeddoor(c, ny, nx)) {
			int k = square_door_power(c, ny, nx);

			if (randint0(mon->hp / 10) > k) {
				if (may_bash)
					msg("%s slams against the door.", m_name);
				else
					msg("%s fiddles with the lock.", m_name);

				/* Reduce the power of the door by one */
				square_set_door_lock(c, ny, nx, k - 1);
			}
		} else {
			/* Handle viewable doors */
			if (square_isview(c, ny, nx))
				player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS);

			/* Closed or secret door -- open or bash if allowed */
			if (may_bash) {
				square_smash_door(c, ny, nx);

				msg("You hear a door burst open!");
				disturb(player, 0);

				/* Fall into doorway */
				return true;
			} else if (rf_has(mon->race->flags, RF_OPEN_DOOR)) {
				square_open_door(c, ny, nx);
			}
		}
	}

	return false;
}
Ejemplo n.º 14
0
/**
 * Use various selection criteria (set elsewhere) to restrict monster 
 * generation.
 *
 * This function is capable of selecting monsters by:
 *   - racial symbol (may be any of the characters allowed)
 *   - symbol color (may be any of up to four colors).
 *   - racial flag(s) (monster may have any allowed flag)
 *   - breath flag(s) (monster must have exactly the flags specified)
 *
 * Uniques may be forbidden, or allowed on rare occasions.
 *
 * Some situations (like the elemental war themed level) require special 
 * processing; this is done in helper functions called from this one.
 */
static bool mon_select(int r_idx)
{
    monster_race *r_ptr = &r_info[r_idx];
    bool ok = FALSE;
    bitflag mon_breath[RSF_SIZE];


    /* Require correct breath attack */
    rsf_copy(mon_breath, r_ptr->spell_flags);
    flags_mask(mon_breath, RSF_SIZE, RSF_BREATH_MASK, FLAG_END);

    /* Special case: Elemental war themed level. */
    if (p_ptr->themed_level == THEME_ELEMENTAL) {
	return (vault_aux_elemental(r_idx));
    }

    /* Special case: Estolad themed level. */
    if (p_ptr->themed_level == THEME_ESTOLAD) {
	if (!(rf_has(r_ptr->flags, RF_RACIAL)))
	    return (FALSE);
    }


    /* Require that the monster symbol be correct. */
    if (d_char_req[0] != '\0') {
	if (strchr(d_char_req, r_ptr->d_char) == 0)
	    return (FALSE);
    }

    /* Require correct racial type. */
    if (racial_flag_mask) {
	if (!(rf_has(r_ptr->flags, racial_flag_mask)))
	    return (FALSE);

	/* Hack -- no invisible undead until deep. */
	if ((p_ptr->danger < 40) && (rf_has(r_ptr->flags, RF_UNDEAD))
	    && (rf_has(r_ptr->flags, RF_INVISIBLE)))
	    return (FALSE);
    }

    /* Require that monster breaths be exactly those specified. */
    /* Exception for winged dragons */
    if (!rsf_is_empty(breath_flag_mask)) {
	/* 'd'ragons */
	if (!rsf_is_equal(mon_breath, breath_flag_mask)
	    && (r_ptr->d_char != 'D'))
	    return (FALSE);

	/* Winged 'D'ragons */
	if (r_ptr->d_char == 'D') {
	    if (!rsf_is_subset(mon_breath, breath_flag_mask))
		return (FALSE);

	    /* Hack - this deals with all current Ds that need excluding */
	    if (rsf_has(r_ptr->flags, RSF_BRTH_SOUND))
		return (FALSE);
	}
    }

    /* Require that the monster color be correct. */
    if (d_attr_req[0]) {
	/* Check all allowed colors, if given. */
	if ((d_attr_req[0]) && (r_ptr->d_attr == d_attr_req[0]))
	    ok = TRUE;
	if ((d_attr_req[1]) && (r_ptr->d_attr == d_attr_req[1]))
	    ok = TRUE;
	if ((d_attr_req[2]) && (r_ptr->d_attr == d_attr_req[2]))
	    ok = TRUE;
	if ((d_attr_req[3]) && (r_ptr->d_attr == d_attr_req[3]))
	    ok = TRUE;

	/* Hack -- No multihued dragons allowed in the arcane dragon pit. */
	if ((strchr(d_char_req, 'd') || strchr(d_char_req, 'D'))
	    && (d_attr_req[0] == TERM_VIOLET)
	    && (rf_has(r_ptr->flags, RSF_BRTH_ACID)
		|| rf_has(r_ptr->flags, RSF_BRTH_ELEC)
		|| rf_has(r_ptr->flags, RSF_BRTH_FIRE)
		|| rf_has(r_ptr->flags, RSF_BRTH_COLD)
		|| rf_has(r_ptr->flags, RSF_BRTH_POIS))) {
	    return (FALSE);
	}

	/* Doesn't match any of the given colors? Not good. */
	if (!ok)
	    return (FALSE);
    }

    /* Usually decline unique monsters. */
    if (rf_has(r_ptr->flags, RF_UNIQUE)) {
	if (!allow_unique)
	    return (FALSE);
	else if (randint0(5) != 0)
	    return (FALSE);
    }

    /* Okay */
    return (TRUE);
}
Ejemplo n.º 15
0
/**
 * Accept characters representing a race or group of monsters and 
 * an (adjusted) depth, and use these to set values for required racial 
 * type, monster symbol, monster symbol color, and breath type.  -LM-
 *
 * This function is called to set restrictions, point the monster 
 * allocation function to mon_select(), and remake monster allocation.  
 * It undoes all of this things when called with the symbol '\0'.
 * 
 * Describe the monsters (used by cheat_room) and determine if they 
 * should be neatly ordered or randomly placed (used in monster pits).
 */
extern char *mon_restrict(char symbol, byte depth, bool * ordered,
			  bool unique_ok)
{
    int i, j;

    /* Assume no definite name */
    char name[80] = "misc";

    /* Clear global monster restriction variables. */
    allow_unique = unique_ok;
    for (i = 0; i < 10; i++)
	d_char_req[i] = '\0';
    for (i = 0; i < 4; i++)
	d_attr_req[i] = 0;
    racial_flag_mask = 0;
    rsf_wipe(breath_flag_mask);


    /* No symbol, no restrictions. */
    if (symbol == '\0') {
	get_mon_num_hook = NULL;
	get_mon_num_prep();
	return ("misc");
    }

    /* Handle the "wild card" symbol '*' */
    if (symbol == '*') {
	for (i = 0; i < 2500; i++) {
	    /* Get a random monster. */
	    j = randint1(z_info->r_max - 1);

	    /* Must be a real monster */
	    if (!r_info[j].rarity)
		continue;

	    /* Try for close to depth, accept in-depth if necessary */
	    if (i < 200) {
		if ((!rf_has(r_info[j].flags, RF_UNIQUE))
		    && (r_info[j].level != 0) && (r_info[j].level <= depth)
		    && (ABS(r_info[j].level - p_ptr->danger) <
			1 + (p_ptr->danger / 4)))
		    break;
	    } else {
		if ((!rf_has(r_info[j].flags, RF_UNIQUE))
		    && (r_info[j].level != 0) && (r_info[j].level <= depth))
		    break;
	    }
	}

	/* We've found a monster. */
	if (i < 2499) {
	    /* ...use that monster's symbol for all monsters. */
	    symbol = r_info[j].d_char;
	} else {
	    /* Paranoia - pit stays empty if no monster is found */
	    return (NULL);
	}
    }

    /* Apply monster restrictions according to symbol. */
    switch (symbol) {
	/* All animals */
    case 'A':
	{
	    strcpy(name, "animal");
	    racial_flag_mask = RF_ANIMAL;
	    *ordered = FALSE;
	    break;
	}

	/* Insects */
    case '1':
	{
	    strcpy(name, "insect");
	    strcpy(d_char_req, "aclFIK");
	    *ordered = FALSE;
	    break;
	}

	/* Reptiles */
    case '2':
	{
	    strcpy(name, "reptile");
	    strcpy(d_char_req, "nJR");
	    *ordered = FALSE;
	    break;
	}

	/* Jellies, etc. */
    case '3':
	{
	    strcpy(name, "jelly");
	    strcpy(d_char_req, "ijm,");
	    *ordered = FALSE;
	    break;
	}

	/* Humans and humaniods */
    case 'p':
    case 'h':
	{
	    /* 'p's and 'h's can coexist. */
	    if (randint0(3) == 0) {
		strcpy(d_char_req, "ph");

		/* If so, they will usually all be of similar classes. */
		if (randint0(4) != 0) {
		    /* Randomizer. */
		    i = randint0(5);

		    /* Magicians and necromancers */
		    if (i == 0) {
			d_attr_req[0] = TERM_RED;
			d_attr_req[1] = TERM_L_RED;
			d_attr_req[2] = TERM_VIOLET;
			strcpy(name, "school of sorcery");
		    }
		    /* Priests and paladins */
		    else if (i == 1) {
			d_attr_req[0] = TERM_GREEN;
			d_attr_req[1] = TERM_L_GREEN;
			d_attr_req[2] = TERM_WHITE;
			d_attr_req[3] = TERM_L_WHITE;
			strcpy(name, "temple of piety");
		    }
		    /* Druids and ninjas */
		    else if (i == 2) {
			d_attr_req[0] = TERM_ORANGE;
			d_attr_req[1] = TERM_YELLOW;
			strcpy(name, "gathering of nature");
		    }
		    /* Thieves and assassins */
		    else if (i == 3) {
			d_attr_req[0] = TERM_BLUE;
			d_attr_req[1] = TERM_L_BLUE;
			d_attr_req[2] = TERM_SLATE;
			d_attr_req[3] = TERM_L_DARK;
			strcpy(name, "den of thieves");
		    }
		    /* Warriors and rangers */
		    else {
			d_attr_req[0] = TERM_UMBER;
			d_attr_req[1] = TERM_L_UMBER;
			strcpy(name, "fighter's hall");
		    }
		} else {
		    strcpy(name, "humans and humanoids");
		}
	    }

	    /* Usually, just accept the symbol. */
	    else {
		d_char_req[0] = symbol;

		if (symbol == 'p')
		    strcpy(name, "human");
		else if (symbol == 'h')
		    strcpy(name, "humanoid");
	    }

	    *ordered = FALSE;
	    break;
	}

	/* Orcs */
    case 'o':
	{
	    strcpy(name, "orc");
	    strcpy(d_char_req, "o");
	    *ordered = TRUE;
	    break;
	}

	/* Trolls */
    case 'T':
	{
	    strcpy(name, "troll");
	    strcpy(d_char_req, "T");
	    *ordered = TRUE;
	    break;
	}

	/* Giants (sometimes ogres at low levels) */
    case 'P':
	{
	    strcpy(name, "giant");
	    if ((p_ptr->danger < 30) && (randint0(3) == 0))
		strcpy(d_char_req, "O");
	    else
		strcpy(d_char_req, "P");
	    *ordered = TRUE;
	    break;
	}

	/* Orcs, ogres, trolls, or giants */
    case '%':
	{
	    strcpy(name, "moria");
	    strcpy(d_char_req, "oOPT");
	    *ordered = FALSE;
	    break;
	}

	/* Monsters found in caves */
    case '0':
	{
	    strcpy(name, "dungeon monsters");
	    strcpy(d_char_req, "ykoOT");
	    *ordered = FALSE;
	    break;
	}

	/* Monsters found in wilderness caves */
    case 'x':
	{
	    strcpy(name, "underworld monsters");
	    strcpy(d_char_req, "bgkosuyOTUVXW");
	    *ordered = FALSE;
	    break;
	}


	/* Undead */
    case 'N':
	{
	    /* Sometimes, restrict by symbol. */
	    if ((depth > 40) && (randint0(3) == 0)) {
		for (i = 0; i < 500; i++) {
		    /* Find a suitable monster near depth. */
		    j = randint1(z_info->r_max - 1);

		    /* Require a non-unique undead. */
		    if (rf_has(r_info[j].flags, RF_UNDEAD)
			&& (!rf_has(r_info[j].flags, RF_UNIQUE))
			&& (strchr("GLWV", r_info[j].d_char))
			&& (ABS(r_info[j].level - p_ptr->danger) <
			    1 + (p_ptr->danger / 4))) {
			break;
		    }
		}

		/* If we find a monster, */
		if (i < 499) {
		    /* Use that monster's symbol for all monsters */
		    d_char_req[0] = r_info[j].d_char;

		    /* No pit name (yet) */

		    /* In this case, we do order the monsters */
		    *ordered = TRUE;
		} else {
		    /* Accept any undead. */
		    strcpy(name, "undead");
		    racial_flag_mask = RF_UNDEAD;
		    *ordered = FALSE;
		}
	    } else {
		/* No restrictions on symbol. */
		strcpy(name, "undead");
		racial_flag_mask = RF_UNDEAD;
		*ordered = FALSE;
	    }
	    break;
	}

	/* Demons */
    case 'u':
    case 'U':
	{
	    strcpy(name, "demon");
	    if (depth > 55)
		strcpy(d_char_req, "U");
	    else if (depth < 40)
		strcpy(d_char_req, "u");
	    else
		strcpy(d_char_req, "uU");
	    *ordered = TRUE;
	    break;
	}

	/* Dragons */
    case 'd':
    case 'D':
	{
	    strcpy(d_char_req, "dD");

	    /* Dragons usually associate with others of their kind. */
	    if (randint0(6) != 0) {
		/* Dragons of a single kind are ordered. */
		*ordered = TRUE;

		/* Some dragon types are not found everywhere */
		if (depth > 70)
		    i = randint0(35);
		else if (depth > 45)
		    i = randint0(32);
		else if (depth > 32)
		    i = randint0(30);
		else if (depth > 23)
		    i = randint0(28);
		else
		    i = randint0(24);

		if (i < 4) {
		    flags_init(breath_flag_mask, RSF_SIZE, RSF_BRTH_ACID,
			       FLAG_END);
		    strcpy(name, "dragon - acid");
		} else if (i < 8) {
		    flags_init(breath_flag_mask, RSF_SIZE, RSF_BRTH_ELEC,
			       FLAG_END);
		    strcpy(name, "dragon - electricity");
		} else if (i < 12) {
		    flags_init(breath_flag_mask, RSF_SIZE, RSF_BRTH_FIRE,
			       FLAG_END);
		    strcpy(name, "dragon - fire");
		} else if (i < 16) {
		    flags_init(breath_flag_mask, RSF_SIZE, RSF_BRTH_COLD,
			       FLAG_END);
		    strcpy(name, "dragon - cold");
		} else if (i < 20) {
		    flags_init(breath_flag_mask, RSF_SIZE, RSF_BRTH_POIS,
			       FLAG_END);
		    strcpy(name, "dragon - poison");
		} else if (i < 24) {
		    flags_init(breath_flag_mask, RSF_SIZE, RSF_BRTH_ACID,
			       RSF_BRTH_ELEC, RSF_BRTH_FIRE, RSF_BRTH_COLD,
			       RSF_BRTH_POIS, FLAG_END);
		    strcpy(name, "dragon - multihued");
		} else if (i < 26) {
		    flags_init(breath_flag_mask, RSF_SIZE, RSF_BRTH_CONFU,
			       FLAG_END);
		    strcpy(name, "dragon - confusion");
		} else if (i < 28) {
		    flags_init(breath_flag_mask, RSF_SIZE, RSF_BRTH_SOUND,
			       FLAG_END);
		    strcpy(name, "dragon - sound");
		} else if (i < 30) {
		    flags_init(breath_flag_mask, RSF_SIZE, RSF_BRTH_LIGHT,
			       RSF_BRTH_DARK, FLAG_END);
		    strcpy(name, "dragon - ethereal");
		}

		/* Chaos, Law, Balance, Power, etc.) */
		else {
		    d_attr_req[0] = TERM_VIOLET;
		    d_attr_req[1] = TERM_L_BLUE;
		    d_attr_req[2] = TERM_L_GREEN;
		    strcpy(name, "dragon - arcane");
		}
	    } else {
		strcpy(name, "dragon - mixed");

		/* Dragons of all kinds are not ordered. */
		*ordered = FALSE;
	    }
	    break;
	}

	/* Vortexes and elementals */
    case 'v':
    case 'E':
	{
	    /* Usually, just have any kind of 'v' or 'E' */
	    if (randint0(3) != 0) {
		d_char_req[0] = symbol;

		if (symbol == 'v')
		    strcpy(name, "vortex");
		if (symbol == 'E')
		    strcpy(name, "elemental");
	    }

	    /* Sometimes, choose both 'v' and 'E's of one element */
	    else {
		strcpy(d_char_req, "vE");

		i = randint0(4);

		/* Fire */
		if (i == 0) {
		    d_attr_req[0] = TERM_RED;
		    strcpy(name, "fire");
		}
		/* Frost */
		if (i == 1) {
		    d_attr_req[0] = TERM_L_WHITE;
		    d_attr_req[1] = TERM_WHITE;
		    strcpy(name, "frost");
		}
		/* Air/electricity */
		if (i == 2) {
		    d_attr_req[0] = TERM_L_BLUE;
		    d_attr_req[1] = TERM_BLUE;
		    strcpy(name, "air");
		}
		/* Acid/water/earth */
		if (i == 3) {
		    d_attr_req[0] = TERM_GREEN;
		    d_attr_req[1] = TERM_L_UMBER;
		    d_attr_req[2] = TERM_UMBER;
		    d_attr_req[3] = TERM_SLATE;
		    strcpy(name, "earth & water");
		}
	    }

	    *ordered = FALSE;
	    break;
	}

	/* Special case: mimics and treasure */
    case '!':
    case '?':
    case '=':
    case '~':
    case '|':
    case '.':
    case '$':
	{
	    if (symbol == '$') {
		strcpy(name, "treasure");

		/* Nothing but loot! */
		if (randint0(3) == 0)
		    strcpy(d_char_req, "$");

		/* Guard the money well. */
		else
		    strcpy(d_char_req, "$!?=~|.");
	    } else {
		/* No treasure. */
		strcpy(d_char_req, "!?=~|.");
		strcpy(name, "mimic");
	    }

	    *ordered = FALSE;
	    break;
	}

	/* Special case: creatures of earth. */
    case 'X':
    case '#':
	{
	    strcpy(d_char_req, "X#");
	    strcpy(name, "creatures of earth");
	    *ordered = FALSE;
	    break;
	}

	/* Water creatures. */
    case '6':
	{
	    allow_unique = TRUE;
	    strcpy(d_char_req, "vEZ");
	    d_attr_req[0] = TERM_SLATE;
	    break;
	}

	/* Beings of fire or ice. */
    case '7':
	{
	    allow_unique = TRUE;
	    strcpy(d_char_req, "vE");
	    if (randint0(2) == 0)
		d_attr_req[0] = TERM_RED;
	    else {
		d_attr_req[0] = TERM_L_WHITE;
		d_attr_req[1] = TERM_WHITE;
	    }

	    break;
	}

	/* Space for more monster types here. */


	/* Any symbol not handled elsewhere. */
    default:
	{
	    /* Accept the character. */
	    d_char_req[0] = symbol;

	    /* Some monsters should logically be ordered. */
	    if (strchr("knosuyzGLMOPTUVW", symbol))
		*ordered = TRUE;

	    /* Most should not */
	    else
		*ordered = FALSE;

	    break;
	}
    }

    /* If monster pit hasn't been named already, get a name. */
    if (streq(name, "misc")) {
	/* Search a table for a description of the symbol */
	for (i = 0; d_char_req_desc[i]; ++i) {
	    if (symbol == d_char_req_desc[i][0]) {
		/* Get all but the 1st 2 characters of the text. */
		sprintf(name, "%s", d_char_req_desc[i] + 2);
		break;
	    }
	}
    }

    /* Apply our restrictions */
    get_mon_num_hook = mon_select;

    /* Prepare allocation table */
    get_mon_num_prep();

    /* Return the name. */
    return (format("%s", name));
}
Ejemplo n.º 16
0
/**
 * Generate a random level.
 *
 * Confusingly, this function also generate the town level (level 0).
 * \param c is the level we're going to end up with, in practice the global cave
 * \param p is the current player struct, in practice the global player
 */
void cave_generate(struct chunk **c, struct player *p) {
	const char *error = "no generation";
	int y, x, tries = 0;
	struct chunk *chunk;

	assert(c);

	/* Generate */
	for (tries = 0; tries < 100 && error; tries++) {
		struct dun_data dun_body;

		error = NULL;

		/* Mark the dungeon as being unready (to avoid artifact loss, etc) */
		character_dungeon = FALSE;

		/* Allocate global data (will be freed when we leave the loop) */
		dun = &dun_body;
		dun->cent = mem_zalloc(z_info->level_room_max * sizeof(struct loc));
		dun->door = mem_zalloc(z_info->level_door_max * sizeof(struct loc));
		dun->wall = mem_zalloc(z_info->wall_pierce_max * sizeof(struct loc));
		dun->tunn = mem_zalloc(z_info->tunn_grid_max * sizeof(struct loc));

		/* Choose a profile and build the level */
		dun->profile = choose_profile(p->depth);
		chunk = dun->profile->builder(p);
		if (!chunk) {
			error = "Failed to find builder";
			mem_free(dun->cent);
			mem_free(dun->door);
			mem_free(dun->wall);
			mem_free(dun->tunn);
			continue;
		}

		/* Ensure quest monsters */
		if (is_quest(chunk->depth)) {
			int i;
			for (i = 1; i < z_info->r_max; i++) {
				monster_race *r_ptr = &r_info[i];
				int y, x;
				
				/* The monster must be an unseen quest monster of this depth. */
				if (r_ptr->cur_num > 0) continue;
				if (!rf_has(r_ptr->flags, RF_QUESTOR)) continue;
				if (r_ptr->level != chunk->depth) continue;
	
				/* Pick a location and place the monster */
				find_empty(chunk, &y, &x);
				place_new_monster(chunk, y, x, r_ptr, TRUE, TRUE, ORIGIN_DROP);
			}
		}

		/* Clear generation flags. */
		for (y = 0; y < chunk->height; y++) {
			for (x = 0; x < chunk->width; x++) {
				sqinfo_off(chunk->squares[y][x].info, SQUARE_WALL_INNER);
				sqinfo_off(chunk->squares[y][x].info, SQUARE_WALL_OUTER);
				sqinfo_off(chunk->squares[y][x].info, SQUARE_WALL_SOLID);
				sqinfo_off(chunk->squares[y][x].info, SQUARE_MON_RESTRICT);
			}
		}

		/* Regenerate levels that overflow their maxima */
		if (cave_monster_max(chunk) >= z_info->level_monster_max)
			error = "too many monsters";

		if (error) ROOM_LOG("Generation restarted: %s.", error);

		mem_free(dun->cent);
		mem_free(dun->door);
		mem_free(dun->wall);
		mem_free(dun->tunn);
	}

	if (error) quit_fmt("cave_generate() failed 100 times!");

	/* Free the old cave, use the new one */
	if (*c)
		cave_free(*c);
	*c = chunk;

	/* Place dungeon squares to trigger feeling (not in town) */
	if (player->depth)
		place_feeling(*c);

	/* Save the town */
	else if (!chunk_find_name("Town")) {
		struct chunk *town = chunk_write(0, 0, z_info->town_hgt,
										 z_info->town_wid, FALSE, FALSE, FALSE);
		town->name = string_make("Town");
		chunk_list_add(town);
	}

	(*c)->feeling = calc_obj_feeling(*c) + calc_mon_feeling(*c);

	/* Validate the dungeon (we could use more checks here) */
	chunk_validate_objects(*c);

	/* The dungeon is ready */
	character_dungeon = TRUE;

	/* Free old and allocate new known level */
	if (cave_k)
		cave_free(cave_k);
	cave_k = cave_new(cave->height, cave->width);
	if (!cave->depth)
		cave_known();

	(*c)->created_at = turn;
}
Ejemplo n.º 17
0
/*
 * Display known monsters.
 */
static void do_cmd_knowledge_monsters(const char *name, int row)
{
	group_funcs r_funcs = { N_ELEMENTS(monster_group), FALSE, race_name,
		m_cmp_race, default_group, mon_summary
	};

	member_funcs m_funcs =
		{ display_monster, mon_lore, m_xchar, m_xattr, recall_prompt, 0,
0 };

	int *monsters;
	int m_count = 0;
	int i;
	size_t j;

	for (i = 0; i < z_info->r_max; i++) {
		monster_race *r_ptr = &r_info[i];
		if (!OPT(cheat_know) && !l_list[i].sights)
			continue;
		if (!r_ptr->name)
			continue;

		if (rf_has(r_ptr->flags, RF_UNIQUE))
			m_count++;

		for (j = 1; j < N_ELEMENTS(monster_group) - 1; j++) {
			const wchar_t *pat = monster_group[j].chars;
			if (wcschr(pat, r_ptr->d_char))
				m_count++;
		}
	}

	default_join = C_ZNEW(m_count, join_t);
	monsters = C_ZNEW(m_count, int);

	m_count = 0;
	for (i = 0; i < z_info->r_max; i++) {
		monster_race *r_ptr = &r_info[i];
		if (!OPT(cheat_know) && !l_list[i].sights)
			continue;
		if (!r_ptr->name)
			continue;

		for (j = 0; j < N_ELEMENTS(monster_group) - 1; j++) {
			const wchar_t *pat = monster_group[j].chars;
			if (j == 0 && !(rf_has(r_ptr->flags, RF_UNIQUE)))
				continue;
			else if (j > 0 && !wcschr(pat, r_ptr->d_char))
				continue;

			monsters[m_count] = m_count;
			default_join[m_count].oid = i;
			default_join[m_count++].gid = j;
		}
	}

	display_knowledge("monsters", monsters, m_count, r_funcs, m_funcs,
					  "                   Sym  Kills");
	FREE(default_join);
	FREE(monsters);
}
Ejemplo n.º 18
0
/**
 * Determines whether the given monster successfully resists the given effect.
 *
 * If MON_TMD_FLG_NOFAIL is set in `flag`, this returns false.
 * Then we determine if the monster resists the effect for some racial
 * reason. For example, the monster might have the NO_SLEEP flag, in which
 * case it always resists sleep. Or if it breathes chaos, it always resists
 * confusion. If the given monster doesn't resist for any of these reasons,
 * then it makes a saving throw. If MON_TMD_MON_SOURCE is set in `flag`,
 * indicating that another monster caused this effect, then the chance of
 * success on the saving throw just depends on the monster's native depth.
 * Otherwise, the chance of success decreases as `timer` increases.
 *
 * Also marks the lore for any appropriate resists.
 */
static bool mon_resist_effect(const struct monster *mon, int ef_idx, int timer, u16b flag)
{
	struct mon_timed_effect *effect;
	int resist_chance;
	struct monster_lore *lore;

	assert(ef_idx >= 0 && ef_idx < MON_TMD_MAX);
	assert(mon);

	effect = &effects[ef_idx];
	lore = get_lore(mon->race);
	
	/* Hasting never fails */
	if (ef_idx == MON_TMD_FAST) return (false);
	
	/* Some effects are marked to never fail */
	if (flag & MON_TMD_FLG_NOFAIL) return (false);

	/* A sleeping monster resists further sleeping */
	if (ef_idx == MON_TMD_SLEEP && mon->m_timed[ef_idx]) return (true);

	/* If the monster resists innately, learn about it */
	if (rf_has(mon->race->flags, effect->flag_resist)) {
		if (mflag_has(mon->mflag, MFLAG_VISIBLE))
			rf_on(lore->flags, effect->flag_resist);

		return (true);
	}

	/* Monsters with specific breaths resist stunning */
	if (ef_idx == MON_TMD_STUN &&
		(rsf_has(mon->race->spell_flags, RSF_BR_SOUN) ||
		 rsf_has(mon->race->spell_flags, RSF_BR_WALL))) {
		/* Add the lore */
		if (mflag_has(mon->mflag, MFLAG_VISIBLE)) {
			if (rsf_has(mon->race->spell_flags, RSF_BR_SOUN))
				rsf_on(lore->spell_flags, RSF_BR_SOUN);
			if (rsf_has(mon->race->spell_flags, RSF_BR_WALL))
				rsf_on(lore->spell_flags, RSF_BR_WALL);
		}

		return (true);
	}

	/* Monsters with specific breaths resist confusion */
	if ((ef_idx == MON_TMD_CONF) &&
		rsf_has(mon->race->spell_flags, RSF_BR_CHAO)) {
		/* Add the lore */
		if (mflag_has(mon->mflag, MFLAG_VISIBLE))
			if (rsf_has(mon->race->spell_flags, RSF_BR_CHAO))
				rsf_on(lore->spell_flags, RSF_BR_CHAO);

		return (true);
	}

	/* Inertia breathers resist slowing */
	if (ef_idx == MON_TMD_SLOW && rsf_has(mon->race->spell_flags, RSF_BR_INER)){
		rsf_on(lore->spell_flags, RSF_BR_INER);
		return (true);
	}

	/* Calculate the chance of the monster making its saving throw. */
	if (ef_idx == MON_TMD_SLEEP)
		timer /= 25; /* Hack - sleep uses much bigger numbers */

	if (flag & MON_TMD_MON_SOURCE)
		resist_chance = mon->race->level;
	else
		resist_chance = mon->race->level + 40 - (timer / 2);

	if (randint0(100) < resist_chance) return (true);

	/* Uniques are doubly hard to affect */
	if (rf_has(mon->race->flags, RF_UNIQUE))
		if (randint0(100) < resist_chance) return (true);

	return (false);
}
Ejemplo n.º 19
0
/**
 * Draw a visible path over the squares between (x1,y1) and (x2,y2).
 *
 * The path consists of "*", which are white except where there is a
 * monster, object or feature in the grid.
 *
 * This routine has (at least) three weaknesses:
 * - remembered objects/walls which are no longer present are not shown,
 * - squares which (e.g.) the player has walked through in the dark are
 *   treated as unknown space.
 * - walls which appear strange due to hallucination aren't treated correctly.
 *
 * The first two result from information being lost from the dungeon arrays,
 * which requires changes elsewhere
 */
static int draw_path(u16b path_n, u16b *path_g, wchar_t *c, int *a, int y1, int x1)
{
	int i;
	bool on_screen;

	/* No path, so do nothing. */
	if (path_n < 1) return 0;

	/* The starting square is never drawn, but notice if it is being
     * displayed. In theory, it could be the last such square.
     */
	on_screen = panel_contains(y1, x1);

	/* Draw the path. */
	for (i = 0; i < path_n; i++) {
		byte colour;

		/* Find the co-ordinates on the level. */
		int y = GRID_Y(path_g[i]);
		int x = GRID_X(path_g[i]);

		/*
		 * As path[] is a straight line and the screen is oblong,
		 * there is only section of path[] on-screen.
		 * If the square being drawn is visible, this is part of it.
		 * If none of it has been drawn, continue until some of it
		 * is found or the last square is reached.
		 * If some of it has been drawn, finish now as there are no
		 * more visible squares to draw.
		 */
		 if (panel_contains(y,x)) on_screen = TRUE;
		 else if (on_screen) break;
		 else continue;

	 	/* Find the position on-screen */
		move_cursor_relative(y,x);

		/* This square is being overwritten, so save the original. */
		Term_what(Term->scr->cx, Term->scr->cy, a+i, c+i);

		/* Choose a colour. */
		if (cave->m_idx[y][x] && cave_monster_at(cave, y, x)->ml) {
			/* Visible monsters are red. */
			monster_type *m_ptr = cave_monster_at(cave, y, x);

			/* Mimics act as objects */
			if (rf_has(m_ptr->race->flags, RF_UNAWARE)) 
				colour = TERM_YELLOW;
			else
				colour = TERM_L_RED;
		}

		else if (cave->o_idx[y][x] && object_byid(cave->o_idx[y][x])->marked)
			/* Known objects are yellow. */
			colour = TERM_YELLOW;

		else if (!cave_ispassable(cave, y,x) &&
				 ((cave->info[y][x] & (CAVE_MARK)) || player_can_see_bold(y,x)))
			/* Known walls are blue. */
			colour = TERM_BLUE;

		else if (!(cave->info[y][x] & (CAVE_MARK)) && !player_can_see_bold(y,x))
			/* Unknown squares are grey. */
			colour = TERM_L_DARK;

		else
			/* Unoccupied squares are white. */
			colour = TERM_WHITE;

		/* Draw the path segment */
		(void)Term_addch(colour, L'*');
	}
	return i;
}
Ejemplo n.º 20
0
/**
 * Grab all objects from the grid.
 */
void process_monster_grab_objects(struct chunk *c, struct monster *mon, 
		const char *m_name, int nx, int ny)
{
	struct monster_lore *lore = get_lore(mon->race);
	struct object *obj;
	bool visible = mflag_has(mon->mflag, MFLAG_VISIBLE);

	/* Learn about item pickup behavior */
	for (obj = square_object(c, ny, nx); obj; obj = obj->next) {
		if (!tval_is_money(obj) && visible) {
			rf_on(lore->flags, RF_TAKE_ITEM);
			rf_on(lore->flags, RF_KILL_ITEM);
			break;
		}
	}

	/* Abort if can't pickup/kill */
	if (!rf_has(mon->race->flags, RF_TAKE_ITEM) &&
		!rf_has(mon->race->flags, RF_KILL_ITEM)) {
		return;
	}

	/* Take or kill objects on the floor */
	obj = square_object(c, ny, nx);
	while (obj) {
		char o_name[80];
		bool safe = obj->artifact ? true : false;
		struct object *next = obj->next;

		/* Skip gold */
		if (tval_is_money(obj)) {
			obj = next;
			continue;
		}

		/* Skip mimicked objects */
		if (obj->mimicking_m_idx) {
			obj = next;
			continue;
		}

		/* Get the object name */
		object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL);

		/* React to objects that hurt the monster */
		if (react_to_slay(obj, mon))
			safe = true;

		/* Try to pick up, or crush */
		if (safe) {
			/* Only give a message for "take_item" */
			if (rf_has(mon->race->flags, RF_TAKE_ITEM) && visible &&
				square_isview(c, ny, nx) && !ignore_item_ok(obj)) {
				/* Dump a message */
				msg("%s tries to pick up %s, but fails.", m_name, o_name);
			}
		} else if (rf_has(mon->race->flags, RF_TAKE_ITEM)) {
			/* Describe observable situations */
			if (square_isseen(c, ny, nx) && !ignore_item_ok(obj))
				msg("%s picks up %s.", m_name, o_name);

			/* Carry the object */
			square_excise_object(c, ny, nx, obj);
			monster_carry(c, mon, obj);
			square_note_spot(c, ny, nx);
			square_light_spot(c, ny, nx);
		} else {
			/* Describe observable situations */
			if (square_isseen(c, ny, nx) && !ignore_item_ok(obj))
				msgt(MSG_DESTROY, "%s crushes %s.", m_name, o_name);

			/* Delete the object */
			square_excise_object(c, ny, nx, obj);
			delist_object(c, obj);
			object_delete(&obj);
			square_note_spot(c, ny, nx);
			square_light_spot(c, ny, nx);
		}

		/* Next object */
		obj = next;
	}
}
Ejemplo n.º 21
0
//static struct keypress target_set_interactive_aux(int y, int x, int mode)
static ui_event target_set_interactive_aux(int y, int x, int mode)
{
	s16b this_o_idx = 0, next_o_idx = 0;

	const char *s1, *s2, *s3;

	bool boring;

	int floor_list[MAX_FLOOR_STACK];
	int floor_num;

	//struct keypress query;
	ui_event press;

	char out_val[256];

	char coords[20];

	const char *name;

	/* Describe the square location */
	coords_desc(coords, sizeof(coords), y, x);

	/* Repeat forever */
	while (1)
	{
		/* Paranoia */
		press.type = EVT_KBRD;
		press.key.code = ' ';
		press.key.mods = 0;

		/* Assume boring */
		boring = TRUE;

		/* Default */
		s1 = "You see ";
		s2 = "";
		s3 = "";


		/* The player */
		if (cave->m_idx[y][x] < 0)
		{
			/* Description */
			s1 = "You are ";

			/* Preposition */
			s2 = "on ";
		}

		/* Hallucination messes things up */
		if (p_ptr->timed[TMD_IMAGE])
		{
			const char *name = "something strange";

			/* Display a message */
			if (p_ptr->wizard)
				strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).",
						s1, s2, s3, name, coords, y, x);
			else
				strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.",
						s1, s2, s3, name, coords);

			prt(out_val, 0, 0);
			move_cursor_relative(y, x);
			//input = inkey_m();
			//if (
			press.key = inkey();

			/* Stop on everything but "return" */
			if (press.key.code == KC_ENTER)
				continue;

			return press;
		}

		/* Actual monsters */
		if (cave->m_idx[y][x] > 0)
		{
			monster_type *m_ptr = cave_monster_at(cave, y, x);
			const monster_lore *l_ptr = get_lore(m_ptr->race);

			/* Visible */
			if (m_ptr->ml && !m_ptr->unaware)
			{
				bool recall = FALSE;

				char m_name[80];

				/* Not boring */
				boring = FALSE;

				/* Get the monster name ("a kobold") */
				monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND2);

				/* Hack -- track this monster race */
				monster_race_track(m_ptr->race);

				/* Hack -- health bar for this monster */
				health_track(p_ptr, m_ptr);

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

				/* Interact */
				while (1)
				{
					/* Recall */
					if (recall)
					{
						/* Save screen */
						screen_save();

						/* Recall on screen */
						screen_roff(m_ptr->race, l_ptr);

						/* Command */
						press = inkey_m();

						/* Load screen */
						screen_load();
					}

					/* Normal */
					else
					{
						char buf[80];

						/* Describe the monster */
						look_mon_desc(buf, sizeof(buf), cave->m_idx[y][x]);

						/* Describe, and prompt for recall */
						if (p_ptr->wizard)
						{
							strnfmt(out_val, sizeof(out_val),
									"%s%s%s%s (%s), %s (%d:%d).",
									s1, s2, s3, m_name, buf, coords, y, x);
						}
						else
						{
							strnfmt(out_val, sizeof(out_val),
									"%s%s%s%s (%s), %s.",
									s1, s2, s3, m_name, buf, coords);
						}

						prt(out_val, 0, 0);

						/* Place cursor */
						move_cursor_relative(y, x);

						/* Command */
						press = inkey_m();
					}

					/* Normal commands */
				  if ((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y))
						recall = !recall;
					else
					if ((press.type == EVT_KBRD) && (press.key.code == 'r'))
						recall = !recall;
					else
						break;
				}

				if (press.type == EVT_MOUSE) {
					/* Stop on right click */
					if (press.mouse.button == 2)
						break;

					/* Sometimes stop at "space" key */
					if (press.mouse.button && !(mode & (TARGET_LOOK))) break;
				} else {
					/* Stop on everything but "return"/"space" */
					if (press.key.code != KC_ENTER && press.key.code != ' ')
						break;

					/* Sometimes stop at "space" key */
					if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break;
				}

				/* Take account of gender */
				if (rf_has(m_ptr->race->flags, RF_FEMALE)) s1 = "She is ";
				else if (rf_has(m_ptr->race->flags, RF_MALE)) s1 = "He is ";
				else s1 = "It is ";

				/* Use a verb */
				s2 = "carrying ";

				/* Scan all objects being carried */
				for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
				{
					char o_name[80];

					object_type *o_ptr;

					/* Get the object */
					o_ptr = object_byid(this_o_idx);

					/* Get the next object */
					next_o_idx = o_ptr->next_o_idx;

					/* Obtain an object description */
					object_desc(o_name, sizeof(o_name), o_ptr,
								ODESC_PREFIX | ODESC_FULL);

					/* Describe the object */
					if (p_ptr->wizard)
					{
						strnfmt(out_val, sizeof(out_val),
								"%s%s%s%s, %s (%d:%d).",
								s1, s2, s3, o_name, coords, y, x);
					}
					/* Disabled since monsters now carry their drops
					else
					{
						strnfmt(out_val, sizeof(out_val),
								"%s%s%s%s, %s.", s1, s2, s3, o_name, coords);
					} */

					prt(out_val, 0, 0);
					move_cursor_relative(y, x);
					press = inkey_m();

					if (press.type == EVT_MOUSE) {
						/* Stop on right click */
						if (press.mouse.button == 2)
							break;

						/* Sometimes stop at "space" key */
						if (press.mouse.button && !(mode & (TARGET_LOOK))) break;
					} else {
						/* Stop on everything but "return"/"space" */
						if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break;

						/* Sometimes stop at "space" key */
						if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break;
					}

					/* Change the intro */
					s2 = "also carrying ";
				}

				/* Double break */
				if (this_o_idx) break;

				/* Use a preposition */
				s2 = "on ";
			}
		}

		/* Assume not floored */
		floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), y, x, 0x0A);

		/* Scan all marked objects in the grid */
		if ((floor_num > 0) &&
		    (!(p_ptr->timed[TMD_BLIND]) || (y == p_ptr->py && x == p_ptr->px)))
		{
			/* Not boring */
			boring = FALSE;

			track_object(-floor_list[0]);
			handle_stuff(p_ptr);

			/* If there is more than one item... */
			if (floor_num > 1) while (1)
			{
				/* Describe the pile */
				if (p_ptr->wizard)
				{
					strnfmt(out_val, sizeof(out_val),
							"%s%s%sa pile of %d objects, %s (%d:%d).",
							s1, s2, s3, floor_num, coords, y, x);
				}
				else
				{
					strnfmt(out_val, sizeof(out_val),
							"%s%s%sa pile of %d objects, %s.",
							s1, s2, s3, floor_num, coords);
				}

				prt(out_val, 0, 0);
				move_cursor_relative(y, x);
				press = inkey_m();

				/* Display objects */
				if (((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y))
						|| ((press.type == EVT_KBRD) && (press.key.code == 'r')))
				{
					int rdone = 0;
					int pos;
					while (!rdone)
					{
						/* Save screen */
						screen_save();

						/* Display */
						show_floor(floor_list, floor_num, (OLIST_WEIGHT | OLIST_GOLD));

						/* Describe the pile */
						prt(out_val, 0, 0);
						press = inkey_m();

						/* Load screen */
						screen_load();

						if (press.type == EVT_MOUSE) {
							pos = press.mouse.y-1;
						} else {
							pos = press.key.code - 'a';
						}
						if (0 <= pos && pos < floor_num)
						{
							track_object(-floor_list[pos]);
							handle_stuff(p_ptr);
							continue;
						}
						rdone = 1;
					}

					/* Now that the user's done with the display loop, let's */
					/* the outer loop over again */
					continue;
				}

				/* Done */
				break;
			}
			/* Only one object to display */
			else
			{

				char o_name[80];

				/* Get the single object in the list */
				object_type *o_ptr = object_byid(floor_list[0]);

				/* Not boring */
				boring = FALSE;

				/* Obtain an object description */
				object_desc(o_name, sizeof(o_name), o_ptr,
							ODESC_PREFIX | ODESC_FULL);

				/* Describe the object */
				if (p_ptr->wizard)
				{
					strnfmt(out_val, sizeof(out_val),
							"%s%s%s%s, %s (%d:%d).",
							s1, s2, s3, o_name, coords, y, x);
				}
				else
				{
					strnfmt(out_val, sizeof(out_val),
							"%s%s%s%s, %s.", s1, s2, s3, o_name, coords);
				}

				prt(out_val, 0, 0);
				move_cursor_relative(y, x);
				press = inkey_m();

				/* Stop on everything but "return"/"space" */
				if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break;

				/* Sometimes stop at "space" key */
				if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break;

				/* Change the intro */
				s1 = "It is ";

				/* Plurals */
				if (o_ptr->number != 1) s1 = "They are ";

				/* Preposition */
				s2 = "on ";
			}

		}

		/* Double break */
		if (this_o_idx) break;

		name = cave_apparent_name(cave, p_ptr, y, x);

		/* Terrain feature if needed */
		if (boring || cave_isinteresting(cave, y, x))
		{
			/* Hack -- handle unknown grids */

			/* Pick a prefix */
			if (*s2 && cave_isdoor(cave, y, x)) s2 = "in ";

			/* Pick proper indefinite article */
			s3 = (is_a_vowel(name[0])) ? "an " : "a ";

			/* Hack -- special introduction for store doors */
			if (cave_isshop(cave, y, x))
			{
				s3 = "the entrance to the ";
			}

			/* Display a message */
			if (p_ptr->wizard)
			{
				strnfmt(out_val, sizeof(out_val),
						"%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x);
			}
			else
			{
				strnfmt(out_val, sizeof(out_val),
						"%s%s%s%s, %s.", s1, s2, s3, name, coords);
			}

			prt(out_val, 0, 0);
			move_cursor_relative(y, x);
			press = inkey_m();

			if (press.type == EVT_MOUSE) {
				/* Stop on right click */
				if (press.mouse.button == 2)
					break;
			} else {
				/* Stop on everything but "return"/"space" */
				if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break;
			}
		}

		/* Stop on everything but "return" */
		if (press.type == EVT_MOUSE) {
				/* Stop on right click */
				if (press.mouse.button != 2)
					break;
		} else {
    			if (press.key.code != KC_ENTER) break;
		}
	}

	/* Keep going */
	return (press);
}
Ejemplo n.º 22
0
/**
 * Process a monster
 *
 * In several cases, we directly update the monster lore
 *
 * Note that a monster is only allowed to "reproduce" if there
 * are a limited number of "reproducing" monsters on the current
 * level.  This should prevent the level from being "swamped" by
 * reproducing monsters.  It also allows a large mass of mice to
 * prevent a louse from multiplying, but this is a small price to
 * pay for a simple multiplication method.
 *
 * XXX Monster fear is slightly odd, in particular, monsters will
 * fixate on opening a door even if they cannot open it.  Actually,
 * the same thing happens to normal monsters when they hit a door
 *
 * In addition, monsters which *cannot* open or bash down a door
 * will still stand there trying to open it...  XXX XXX XXX
 *
 * Technically, need to check for monster in the way combined
 * with that monster being in a wall (or door?) XXX
 */
static void process_monster(struct chunk *c, struct monster *mon)
{
	struct monster_lore *lore = get_lore(mon->race);

	bool did_something = false;

	int i;
	int dir = 0;
	bool stagger = false;
	char m_name[80];

	/* Get the monster name */
	monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL | MDESC_IND_HID);

	/* Try to multiply - this can use up a turn */
	if (process_monster_multiply(c, mon))
		return;

	/* Attempt to cast a spell */
	if (make_attack_spell(mon)) return;

	/* Work out what kind of movement to use - AI or staggered movement */
	if (!process_monster_should_stagger(mon)) {
		if (!get_moves(c, mon, &dir)) return;
	} else {
		stagger = true;
	}

	/* Process moves */
	for (i = 0; i < 5 && !did_something; i++) {
		int oy = mon->fy;
		int ox = mon->fx;

		/* Get the direction (or stagger) */
		int d = (stagger ? ddd[randint0(8)] : side_dirs[dir][i]);

		/* Get the destination */
		int ny = oy + ddy[d];
		int nx = ox + ddx[d];

		/* Check if we can move */
		if (!process_monster_can_move(c, mon, m_name, nx, ny, &did_something))
			continue;

		/* Try to break the glyph if there is one.  This can happen multiple
		 * times per turn because failure does not break the loop */
		if (square_iswarded(c, ny, nx) &&
			!process_monster_glyph(c, mon, nx, ny))
			continue;

		/* The player is in the way. */
		if (square_isplayer(c, ny, nx)) {
			/* Learn about if the monster attacks */
			if (mflag_has(mon->mflag, MFLAG_VISIBLE))
				rf_on(lore->flags, RF_NEVER_BLOW);

			/* Some monsters never attack */
			if (rf_has(mon->race->flags, RF_NEVER_BLOW))
				continue;

			/* Otherwise, attack the player */
			make_attack_normal(mon, player);

			did_something = true;
			break;
		} else {
			/* Some monsters never move */
			if (rf_has(mon->race->flags, RF_NEVER_MOVE)) {
				/* Learn about lack of movement */
				if (mflag_has(mon->mflag, MFLAG_VISIBLE))
					rf_on(lore->flags, RF_NEVER_MOVE);

				return;
			}
		}

		/* A monster is in the way, try to push past/kill */
		if (square_monster(c, ny, nx)) {
			did_something = process_monster_try_push(c, mon, m_name, nx, ny);
		} else {
			/* Otherwise we can just move */
			monster_swap(oy, ox, ny, nx);
			did_something = true;
		}

		/* Scan all objects in the grid, if we reached it */
		if (mon == square_monster(c, ny, nx)) {
			monster_desc(m_name, sizeof(m_name), mon,
						 MDESC_CAPITAL | MDESC_IND_HID);
			process_monster_grab_objects(c, mon, m_name, nx, ny);
		}
	}

	if (did_something) {
		/* Learn about no lack of movement */
		if (mflag_has(mon->mflag, MFLAG_VISIBLE))
			rf_on(lore->flags, RF_NEVER_MOVE);

		/* Possible disturb */
		if (mflag_has(mon->mflag, MFLAG_VISIBLE) &&
			mflag_has(mon->mflag, MFLAG_VIEW) && OPT(player, disturb_near))
			disturb(player, 0);		
	}

	/* Hack -- get "bold" if out of options */
	if (!did_something && mon->m_timed[MON_TMD_FEAR])
		mon_clear_timed(mon, MON_TMD_FEAR, MON_TMD_FLG_NOTIFY, false);

	/* If we see an unaware monster do something, become aware of it */
	if (did_something && mflag_has(mon->mflag, MFLAG_UNAWARE))
		become_aware(mon);
}
Ejemplo n.º 23
0
/**
 * Accept characters representing a race or group of monsters and 
 * an (adjusted) depth, and use these to set values for required
 * monster base symbol.
 *
 * \param monster_type the monster type to be selected, as described below
 * \param depth the native depth to choose monsters
 * \param unique_ok whether to allow uniques to be chosen
 * \return success if the monster allocation table has been rebuilt
 *
 * This code has been adapted from Oangband code to make use of monster bases.
 *
 * This function is called to set restrictions, point the monster 
 * allocation function to mon_select() or mon_pit_hook(), and remake monster 
 * allocation.  
 * It undoes all of this things when called with monster_type NULL.
 * If called with a pit profile name, it will get monsters from that profile.
 * If called with monster_type "random", it will get a random monster base and 
 * describe the monsters by its name (for use by cheat_room).
 */
bool mon_restrict(const char *monster_type, int depth, bool unique_ok)
{
    int i, j = 0;

    /* Clear global monster restriction variables. */
    allow_unique = unique_ok;
    for (i = 0; i < 10; i++)
		base_d_char[i] = '\0';

    /* No monster type specified, no restrictions. */
    if (monster_type == NULL) {
		get_mon_num_prep(NULL);
		return true;
	} else if (streq(monster_type, "random")) {
		/* Handle random */
		for (i = 0; i < 2500; i++) {
			/* Get a random monster. */
			j = randint1(z_info->r_max - 1);

			/* Must be a real monster */
			if (!r_info[j].rarity)
				continue;

			/* Try for close to depth, accept in-depth if necessary */
			if (i < 200) {
				if ((!rf_has(r_info[j].flags, RF_UNIQUE))
					&& (r_info[j].level != 0) && (r_info[j].level <= depth)
					&& (ABS(r_info[j].level - player->depth) <
						1 + (player->depth / 4)))
					break;
			} else {
				if ((!rf_has(r_info[j].flags, RF_UNIQUE))
					&& (r_info[j].level != 0) && (r_info[j].level <= depth))
					break;
			}
		}

		/* We've found a monster. */
		if (i < 2499) {
			/* Use that monster's base type for all monsters. */
			my_strcpy(base_d_char, format("%c", r_info[j].base->d_char),
					  sizeof(base_d_char));

			/* Prepare allocation table */
			get_mon_num_prep(mon_select);
			return true;
		} else
			/* Paranoia - area stays empty if no monster is found */
			return false;
    } else {
		/* Use a pit profile */
		struct pit_profile *profile = lookup_pit_profile(monster_type);

		/* Accept the profile or leave area empty if none found */
		if (profile)
			dun->pit_type = profile;
		else
			return false;

		/* Prepare allocation table */
		get_mon_num_prep(mon_pit_hook);
		return true;
	}
}
Ejemplo n.º 24
0
bool describe_origin(textblock * tb, const object_type * o_ptr)
{
	char origin_text[80];

	/* Format location of origin */
	if (stage_map[o_ptr->origin_stage][DEPTH])
		strnfmt(origin_text, sizeof(origin_text), "%s Level %d",
				locality_name[stage_map[o_ptr->origin_stage][LOCALITY]],
				stage_map[o_ptr->origin_stage][DEPTH]);
	else
		strnfmt(origin_text, sizeof(origin_text), "%s %s",
				locality_name[stage_map[o_ptr->origin_stage][LOCALITY]],
				"Town");

	switch (o_ptr->origin) {
	case ORIGIN_NONE:
	case ORIGIN_MIXED:
		return FALSE;

	case ORIGIN_BIRTH:
		textblock_append(tb, "An inheritance from your family.\n");
		break;

	case ORIGIN_STORE:
		textblock_append(tb, "Bought from a store in %s.\n", origin_text);
		break;

	case ORIGIN_FLOOR:
		textblock_append(tb, "Found lying on the ground in %s.\n",
						 origin_text);
		break;

	case ORIGIN_DROP:
		{
			const char *name = r_info[o_ptr->origin_xtra].name;

			textblock_append(tb, "Dropped by ");

			if (rf_has(r_info[o_ptr->origin_xtra].flags, RF_UNIQUE) &&
				!rf_has(r_info[o_ptr->origin_xtra].flags, RF_PLAYER_GHOST))
				textblock_append(tb, "%s", name);
			else
				textblock_append(tb, "%s%s",
								 is_a_vowel(name[0]) ? "an " : "a ", name);

			textblock_append(tb, " in %s.\n", origin_text);
			break;
		}

	case ORIGIN_DROP_UNKNOWN:
		textblock_append(tb, "Dropped by an unknown monster in %s.\n",
						 origin_text);
		break;

	case ORIGIN_ACQUIRE:
		textblock_append(tb, "Conjured forth by magic in %s.\n",
						 origin_text);
		break;

	case ORIGIN_CHEAT:
		textblock_append(tb, "Created by debug option.\n");
		break;

	case ORIGIN_CHEST:
		//if (o_ptr->origin_xtra)
		if (0) {				/* Add in when player ghost issues are fixed */
			const char *name = r_info[o_ptr->origin_xtra].name;

			textblock_append(tb, "Found in a chest dropped by ");

			if (rf_has(r_info[o_ptr->origin_xtra].flags, RF_UNIQUE) &&
				!rf_has(r_info[o_ptr->origin_xtra].flags, RF_PLAYER_GHOST))
				textblock_append(tb, "%s", name);
			else
				textblock_append(tb, "%s%s",
								 is_a_vowel(name[0]) ? "an " : "a ", name);

			textblock_append(tb, " in %s.\n", origin_text);
			break;
		}
		textblock_append(tb, "Found in a chest from %s.\n", origin_text);
		break;

	case ORIGIN_RUBBLE:
		textblock_append(tb, "Found under some rubble in %s.\n",
						 origin_text);
		break;

	case ORIGIN_VAULT:
		textblock_append(tb, "Found in a vault in %s.\n", origin_text);
		break;

	case ORIGIN_CHAOS:
		textblock_append(tb, "Created by the forces of chaos in %s.\n");
		break;

	}

	return TRUE;
}
Ejemplo n.º 25
0
bool describe_origin(textblock *tb, const object_type *o_ptr)
{
	char origin_text[80];

	if (o_ptr->origin_depth)
		strnfmt(origin_text, sizeof(origin_text), "%d feet (level %d)",
		        o_ptr->origin_depth * 50, o_ptr->origin_depth);
	else
		my_strcpy(origin_text, "town", sizeof(origin_text));

	switch (o_ptr->origin)
	{
		case ORIGIN_NONE:
		case ORIGIN_MIXED:
			return FALSE;

		case ORIGIN_BIRTH:
			textblock_append(tb, "An inheritance from your family.\n");
			break;

		case ORIGIN_STORE:
			textblock_append(tb, "Bought from a store.\n");
			break;

		case ORIGIN_FLOOR:
			textblock_append(tb, "Found lying on the floor %s %s.\n",
			         (o_ptr->origin_depth ? "at" : "in"),
			         origin_text);
 			break;

		case ORIGIN_DROP:
		{
			const char *name = r_info[o_ptr->origin_xtra].name;

			textblock_append(tb, "Dropped by ");

			if (rf_has(r_info[o_ptr->origin_xtra].flags, RF_UNIQUE))
				textblock_append(tb, "%s", name);
			else
				textblock_append(tb, "%s%s",
						is_a_vowel(name[0]) ? "an " : "a ", name);

			textblock_append(tb, " %s %s.\n",
					(o_ptr->origin_depth ? "at" : "in"),
					origin_text);
 			break;
		}

		case ORIGIN_DROP_UNKNOWN:
			textblock_append(tb, "Dropped by an unknown monster %s %s.\n",
					(o_ptr->origin_depth ? "at" : "in"),
					origin_text);
			break;

		case ORIGIN_ACQUIRE:
			textblock_append(tb, "Conjured forth by magic %s %s.\n",
					(o_ptr->origin_depth ? "at" : "in"),
					origin_text);
 			break;

		case ORIGIN_CHEAT:
			textblock_append(tb, "Created by debug option.\n");
 			break;

		case ORIGIN_CHEST:
			textblock_append(tb, "Found in a chest from %s.\n",
			         origin_text);
			break;
	}

	textblock_append(tb, "\n");

	return TRUE;
}
Ejemplo n.º 26
0
/**
 * Choose the best direction for "flowing".
 *
 * Note that ghosts and rock-eaters generally don't flow because they can move
 * through obstacles.
 *
 * Monsters first try to use up-to-date distance information ('sound') as
 * saved in cave->squares[y][x].noise.  Failing that, they'll try using scent
 * ('scent') which is just old noise information.
 *
 * Tracking by 'scent' means that monsters end up near enough the player to
 * switch to 'sound' (noise), or they end up somewhere the player left via 
 * teleport.  Teleporting away from a location will cause the monsters who
 * were chasing the player to converge on that location as long as the player
 * is still near enough to "annoy" them without being close enough to chase
 * directly.
 */
static bool get_moves_flow(struct chunk *c, struct monster *mon)
{
	int i;

	int best_scent = 0;
	int best_noise = 999;
	int best_direction = 0;
	bool found_direction = false;

	int py = player->py, px = player->px;
	int my = mon->fy, mx = mon->fx;

	/* Only use this algorithm for passwall monsters if near permanent walls,
	 * to avoid getting snagged */
	if (flags_test(mon->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL,
				   FLAG_END) && !near_permwall(mon, c))
		return (false);

	/* The player is not currently near the monster grid */
	if (c->squares[my][mx].scent < c->squares[py][px].scent)
		/* If the player has never been near this grid, abort */
		if (c->squares[my][mx].scent == 0) return false;

	/* Monster is too far away to notice the player */
	if (c->squares[my][mx].noise > z_info->max_flow_depth) return false;
	if (c->squares[my][mx].noise > mon->race->aaf) return false;

	/* If the player can see monster, set target and run towards them */
	if (square_isview(c, my, mx)) {
		mon->ty = player->py;
		mon->tx = player->px;
		return false;
	}

	/* Check nearby grids, diagonals first */
	/* This gives preference to the cardinal directions */
	for (i = 7; i >= 0; i--) {
		/* Get the location */
		int y = my + ddy_ddd[i];
		int x = mx + ddx_ddd[i];

		/* Bounds check */
		if (!square_in_bounds(c, y, x)) continue;

		/* Ignore unvisited/unpassable locations */
		if (c->squares[y][x].scent == 0) continue;

		/* Ignore locations whose data is more stale */
		if (c->squares[y][x].scent < best_scent) continue;

		/* Ignore locations which are farther away */
		if (c->squares[y][x].noise > best_noise) continue;

		/* Ignore lava if they can't handle the heat */
		if (square_isfiery(c, y, x) && !rf_has(mon->race->flags, RF_IM_FIRE))
			continue;

		/* Save the noise and time */
		best_scent = c->squares[y][x].scent;
		best_noise = c->squares[y][x].noise;
		best_direction = i;
		found_direction = true;
	}

	/* Save the location to flow toward */
	/* Multiply by 16 to angle slightly toward the player's actual location */
	if (found_direction) {
		int dy = 0, dx = 0;

		/* Ridiculous - actually multiply by whatever doesn't underflow the 
		 * byte for ty and tx.  Really should do a better solution - NRM */
		for (i = 0; i < 16; i++)
			if ((py + dy > 0) && (px + dx > 0)) {
				dy += ddy_ddd[best_direction];
				dx += ddx_ddd[best_direction];
			}

		mon->ty = py + dy;
		mon->tx = px + dx;
		return true;
	}

	return false;
}
Ejemplo n.º 27
0
static long eval_hp_adjust(struct monster_race *race)
{
	long hp;
	int resists = 1;
	int hide_bonus = 0;

	/* Get the monster base hitpoints */
	hp = race->avg_hp;

	/* Never moves with no ranged attacks - high hit points count for less */
	if (rf_has(race->flags, RF_NEVER_MOVE) &&
		!(race->freq_innate || race->freq_spell)) {
		hp /= 2;
		if (hp < 1)
			hp = 1;
	}

	/* Just assume healers have more staying power */
	if (rsf_has(race->spell_flags, RSF_HEAL)) hp = (hp * 6) / 5;

	/* Miscellaneous improvements */
	if (rf_has(race->flags, RF_REGENERATE)) {hp *= 10; hp /= 9;}
	if (rf_has(race->flags, RF_PASS_WALL)) {hp *= 3; hp /= 2;}

	/* Calculate hide bonus */
	if (rf_has(race->flags, RF_EMPTY_MIND))
		hide_bonus += 2;
	else {
		if (rf_has(race->flags, RF_COLD_BLOOD)) hide_bonus += 1;
		if (rf_has(race->flags, RF_WEIRD_MIND)) hide_bonus += 1;
	}

	/* Invisibility */
	if (rf_has(race->flags, RF_INVISIBLE))
		hp = (hp * (race->level + hide_bonus + 1)) / MAX(1, race->level);

	/* Monsters that can teleport are a hassle, and can easily run away */
	if (flags_test(race->spell_flags, RSF_SIZE, RSF_TPORT, RSF_TELE_AWAY,
				   RSF_TELE_LEVEL, FLAG_END))
		hp = (hp * 6) / 5;

	/* Monsters that multiply are tougher to kill */
	if (rf_has(race->flags, RF_MULTIPLY)) hp *= 2;

	/* Monsters with resistances are harder to kill.
	 * Therefore effective slays / brands against them are worth more. */
	if (rf_has(race->flags, RF_IM_ACID))
		resists += 2;
	if (rf_has(race->flags, RF_IM_FIRE))
		resists += 2;
	if (rf_has(race->flags, RF_IM_COLD))
		resists += 2;
	if (rf_has(race->flags, RF_IM_ELEC))
		resists += 2;
	if (rf_has(race->flags, RF_IM_POIS))
		resists += 2;

	/* Bonus for multiple basic resists and weapon resists */
	if (resists >= 12)
		resists *= 6;
	else if (resists >= 10)
		resists *= 4;
	else if (resists >= 8)
		resists *= 3;
	else if (resists >= 6)
		resists *= 2;

	/* If quite resistant, reduce resists by defense holes */
	if (resists >= 6) {
		if (rf_has(race->flags, RF_HURT_ROCK))
			resists -= 1;
		if (rf_has(race->flags, RF_HURT_LIGHT))
			resists -= 1;
		if (!rf_has(race->flags, RF_NO_SLEEP))
			resists -= 3;
		if (!rf_has(race->flags, RF_NO_FEAR))
			resists -= 2;
		if (!rf_has(race->flags, RF_NO_CONF))
			resists -= 2;
		if (!rf_has(race->flags, RF_NO_STUN))
			resists -= 1;

		if (resists < 5)
			resists = 5;
	}

	/* If quite resistant, bonus for high resists */
	if (resists >= 3) {
		if (rf_has(race->flags, RF_IM_WATER))
			resists += 1;
		if (rf_has(race->flags, RF_IM_NETHER))
			resists += 1;
		if (rf_has(race->flags, RF_IM_NEXUS))
			resists += 1;
		if (rf_has(race->flags, RF_IM_DISEN))
			resists += 1;
	}

	/* Scale resists */
	resists = resists * 25;

	/* Monster resistances */
	if (resists < (race->ac + resists) / 3)
		hp += (hp * resists) / (150 + race->level); 	
	else
		hp += (hp * (race->ac + resists) / 3) / (150 + race->level); 			

	/* Boundary control */
	if (hp < 1)
		hp = 1;

	return (hp);
}
Ejemplo n.º 28
0
/**
 * Choose a "safe" location near a monster for it to run toward.
 *
 * A location is "safe" if it can be reached quickly and the player
 * is not able to fire into it (it isn't a "clean shot").  So, this will
 * cause monsters to "duck" behind walls.  Hopefully, monsters will also
 * try to run towards corridor openings if they are in a room.
 *
 * This function may take lots of CPU time if lots of monsters are fleeing.
 *
 * Return true if a safe location is available.
 */
static bool find_safety(struct chunk *c, struct monster *mon)
{
	int fy = mon->fy;
	int fx = mon->fx;

	int py = player->py;
	int px = player->px;

	int i, y, x, dy, dx, d, dis;
	int gy = 0, gx = 0, gdis = 0;

	const int *y_offsets;
	const int *x_offsets;

	/* Start with adjacent locations, spread further */
	for (d = 1; d < 10; d++) {
		/* Get the lists of points with a distance d from (fx, fy) */
		y_offsets = dist_offsets_y[d];
		x_offsets = dist_offsets_x[d];

		/* Check the locations */
		for (i = 0, dx = x_offsets[0], dy = y_offsets[0];
		     dx != 0 || dy != 0;
		     i++, dx = x_offsets[i], dy = y_offsets[i]) {
			y = fy + dy;
			x = fx + dx;

			/* Skip illegal locations */
			if (!square_in_bounds_fully(c, y, x)) continue;

			/* Skip locations in a wall */
			if (!square_ispassable(c, y, x)) continue;

			/* Ignore grids very far from the player */
			if (c->squares[y][x].scent < c->squares[py][px].scent) continue;

			/* Ignore too-distant grids */
			if (c->squares[y][x].noise > c->squares[fy][fx].noise + 2 * d)
				continue;

			/* Ignore lava if they can't handle the heat */
			if (square_isfiery(c, y, x) &&
				!rf_has(mon->race->flags, RF_IM_FIRE))
				continue;

			/* Check for absence of shot (more or less) */
			if (!square_isview(c, y, x)) {
				/* Calculate distance from player */
				dis = distance(y, x, py, px);

				/* Remember if further than previous */
				if (dis > gdis) {
					gy = y;
					gx = x;
					gdis = dis;
				}
			}
		}

		/* Check for success */
		if (gdis > 0) {
			/* Good location */
			mon->ty = gy;
			mon->tx = gx;

			/* Found safe place */
			return (true);
		}
	}

	/* No safe place */
	return (false);
}
Ejemplo n.º 29
0
static long eval_max_dam(struct monster_race *race, int ridx)
{
	int rlev, i;
	int melee_dam = 0, atk_dam = 0, spell_dam = 0;
	int dam = 1;

	/* Extract the monster level, force 1 for town monsters */
	rlev = ((race->level >= 1) ? race->level : 1);

	/* Assume single resist for the elemental attacks */
	spell_dam = best_spell_power(race, 1);

	/* Hack - Apply over 10 rounds */
	spell_dam *= 10;

	/* Scale for frequency and availability of mana / ammo */
	if (spell_dam) {
		int freq = race->freq_spell;

		/* Hack -- always get 1 shot */
		if (freq < 10) freq = 10;

		/* Adjust for frequency */
		spell_dam = spell_dam * freq / 100;
	}

	/* Check attacks */
	for (i = 0; i < z_info->mon_blows_max; i++) {
		int effect, method;
		random_value dice;

		if (!race->blow) break;

		/* Extract the attack infomation */
		effect = race->blow[i].effect;
		method = race->blow[i].method;
		dice = race->blow[i].dice;

		/* Assume maximum damage */
		atk_dam = eval_blow_effect(effect, dice, race->level);

		/* Factor for dangerous side effects */
		if (monster_blow_method_stun(method)) {
			/* Stun definitely most dangerous*/
			atk_dam *= 4;
			atk_dam /= 3;
		} else if (monster_blow_method_stun(method)) {
			/* Cut */
			atk_dam *= 7;
			atk_dam /= 5;
		}

		/* Normal melee attack */
		if (!rf_has(race->flags, RF_NEVER_BLOW)) {
			/* Keep a running total */
			melee_dam += atk_dam;
		}
	}

	/* Apply damage over 10 rounds. We assume that the monster has to make
	 * contact first.
	 * Hack - speed has more impact on melee as has to stay in contact with
	 * player.
	 * Hack - this is except for pass wall and kill wall monsters which can
	 * always get to the player.
	 * Hack - use different values for huge monsters as they strike out to
	 * range 2. */
		if (flags_test(race->flags, RF_SIZE, RF_KILL_WALL, RF_PASS_WALL,
					   FLAG_END))
			melee_dam *= 10;
		else
			melee_dam = melee_dam * 3 + melee_dam * adj_energy(race) / 7;

		/* Scale based on attack accuracy. We make a massive number of
		 * assumptions here and just use monster level. */
		melee_dam = melee_dam * MIN(45 + rlev * 3, 95) / 100;

		/* Hack -- Monsters that multiply ignore the following reductions */
		if (!rf_has(race->flags, RF_MULTIPLY)) {
			/*Reduce damamge potential for monsters that move randomly */
			if (flags_test(race->flags, RF_SIZE, RF_RAND_25, RF_RAND_50,
						   FLAG_END)) {
				int reduce = 100;

				if (rf_has(race->flags, RF_RAND_25)) reduce -= 25;
				if (rf_has(race->flags, RF_RAND_50)) reduce -= 50;

				/* Even moving randomly one in 8 times will hit the player */
				reduce += (100 - reduce) / 8;

				/* Adjust the melee damage */
				melee_dam = (melee_dam * reduce) / 100;
			}

			/* Monsters who can't move are much less of a combat threat */
			if (rf_has(race->flags, RF_NEVER_MOVE)) {
				if (rsf_has(race->spell_flags, RSF_TELE_TO) ||
				    rsf_has(race->spell_flags, RSF_BLINK)) {
					/* Scale for frequency */
					melee_dam = melee_dam / 5 + 4 * melee_dam *
						race->freq_spell / 500;

					/* Incorporate spell failure chance */
					if (!rf_has(race->flags, RF_STUPID))
						melee_dam = melee_dam / 5 + 4 * melee_dam *
							MIN(75 + (rlev + 3) / 4, 100) / 500;
				}
				else if (rf_has(race->flags, RF_INVISIBLE))
					melee_dam /= 3;
				else
					melee_dam /= 5;
			}
		}

		/* But keep at a minimum */
		if (melee_dam < 1) melee_dam = 1;

	/* Combine spell and melee damage */
	dam = (spell_dam + melee_dam);

	highest_threat[ridx] = dam;
	final_spell_dam[ridx] = spell_dam;
	final_melee_dam[ridx] = melee_dam;

	/* Adjust for speed - monster at speed 120 will do double damage, monster
	 * at speed 100 will do half, etc.  Bonus for monsters who can haste self */
	dam = (dam * adj_energy(race)) / 10;

	/* Adjust threat for speed -- multipliers are more threatening. */
	if (rf_has(race->flags, RF_MULTIPLY))
		highest_threat[ridx] = (highest_threat[ridx] * adj_energy(race)) / 5;

	/* Adjust threat for friends, this can be improved, but is probably good
	 * enough for now. */
	if (race->friends)
		highest_threat[ridx] *= 2;
	else if (race->friends_base)
		/* Friends base is weaker, because they are <= monster level */
		highest_threat[ridx] = highest_threat[ridx] * 3 / 2;
		
	/* But keep at a minimum */
	if (dam < 1) dam  = 1;

	/* We're done */
	return (dam);
}
Ejemplo n.º 30
0
/**
 * Examine a grid, return a keypress.
 *
 * The "mode" argument contains the "TARGET_LOOK" bit flag, which
 * indicates that the "space" key should scan through the contents
 * of the grid, instead of simply returning immediately.  This lets
 * the "look" command get complete information, without making the
 * "target" command annoying.
 *
 * The "info" argument contains the "commands" which should be shown
 * inside the "[xxx]" text.  This string must never be empty, or grids
 * containing monsters will be displayed with an extra comma.
 *
 * Note that if a monster is in the grid, we update both the monster
 * recall info and the health bar info to track that monster.
 *
 * This function correctly handles multiple objects per grid, and objects
 * and terrain features in the same grid, though the latter never happens.
 *
 * This function must handle blindness/hallucination.
 */
static ui_event target_set_interactive_aux(int y, int x, int mode)
{
	struct object *obj = NULL;

	const char *s1, *s2, *s3;

	bool boring;

	int floor_max = z_info->floor_size;
	struct object **floor_list = mem_zalloc(floor_max * sizeof(*floor_list));
	int floor_num;

	ui_event press;

	char out_val[TARGET_OUT_VAL_SIZE];

	char coords[20];

	const char *name;

	/* Describe the square location */
	coords_desc(coords, sizeof(coords), y, x);

	/* Repeat forever */
	while (1) {
		/* Paranoia */
		press.type = EVT_KBRD;
		press.key.code = ' ';
		press.key.mods = 0;

		/* Assume boring */
		boring = TRUE;

		/* Default */
		s1 = "You see ";
		s2 = "";
		s3 = "";


		/* The player */
		if (cave->squares[y][x].mon < 0) {
			/* Description */
			s1 = "You are ";

			/* Preposition */
			s2 = "on ";
		}

		/* Hallucination messes things up */
		if (player->timed[TMD_IMAGE]) {
			const char *name = "something strange";

			/* Display a message */
			if (player->wizard)
				strnfmt(out_val, sizeof(out_val),
						"%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3,
						name, coords, y, x, (int)cave->squares[y][x].cost,
						(int)cave->squares[y][x].when);
			else
				strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.",
						s1, s2, s3, name, coords);

			prt(out_val, 0, 0);
			move_cursor_relative(y, x);

			press.key = inkey();

			/* Stop on everything but "return" */
			if (press.key.code == KC_ENTER)
				continue;

			mem_free(floor_list);
			return press;
		}

		/* Actual monsters */
		if (cave->squares[y][x].mon > 0) {
			monster_type *m_ptr = square_monster(cave, y, x);
			const monster_lore *l_ptr = get_lore(m_ptr->race);

			/* Visible */
			if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE) &&
				!mflag_has(m_ptr->mflag, MFLAG_UNAWARE)) {
				bool recall = FALSE;

				char m_name[80];

				/* Not boring */
				boring = FALSE;

				/* Get the monster name ("a kobold") */
				monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND_VIS);

				/* Hack -- track this monster race */
				monster_race_track(player->upkeep, m_ptr->race);

				/* Hack -- health bar for this monster */
				health_track(player->upkeep, m_ptr);

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

				/* Interact */
				while (1) {
					/* Recall or target */
					if (recall) {
						lore_show_interactive(m_ptr->race, l_ptr);
						press = inkey_m();
					} else {
						char buf[80];

						/* Describe the monster */
						look_mon_desc(buf, sizeof(buf),
									  cave->squares[y][x].mon);

						/* Describe, and prompt for recall */
						if (player->wizard) {
							strnfmt(out_val, sizeof(out_val),
									"%s%s%s%s (%s), %s (%d:%d, cost=%d, when=%d).",
									s1, s2, s3, m_name, buf, coords, y, x,
									(int)cave->squares[y][x].cost,
									(int)cave->squares[y][x].when);
						} else {
							strnfmt(out_val, sizeof(out_val),
									"%s%s%s%s (%s), %s.",
									s1, s2, s3, m_name, buf, coords);
						}

						prt(out_val, 0, 0);

						/* Place cursor */
						move_cursor_relative(y, x);

						/* Command */
						press = inkey_m();
					}

					/* Normal commands */
					if ((press.type == EVT_MOUSE) && (press.mouse.button == 1)
						&& (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y))
						recall = !recall;
					else
					if ((press.type == EVT_KBRD) && (press.key.code == 'r'))
						recall = !recall;
					else
						break;
				}

				if (press.type == EVT_MOUSE) {
					/* Stop on right click */
					if (press.mouse.button == 2)
						break;

					/* Sometimes stop at "space" key */
					if (press.mouse.button && !(mode & (TARGET_LOOK))) break;
				} else {
					/* Stop on everything but "return"/"space" */
					if (press.key.code != KC_ENTER && press.key.code != ' ')
						break;

					/* Sometimes stop at "space" key */
					if ((press.key.code == ' ') && !(mode & (TARGET_LOOK)))
						break;
				}

				/* Take account of gender */
				if (rf_has(m_ptr->race->flags, RF_FEMALE)) s1 = "She is ";
				else if (rf_has(m_ptr->race->flags, RF_MALE)) s1 = "He is ";
				else s1 = "It is ";

				/* Use a verb */
				s2 = "carrying ";

				/* Scan all objects being carried */
				for (obj = m_ptr->held_obj; obj; obj = obj->next) {
					char o_name[80];

					/* Obtain an object description */
					object_desc(o_name, sizeof(o_name), obj,
								ODESC_PREFIX | ODESC_FULL);

					/* Describe the object */
					if (player->wizard) {
						strnfmt(out_val, sizeof(out_val),
								"%s%s%s%s, %s (%d:%d, cost=%d, when=%d).",
								s1, s2, s3, o_name, coords, y, x,
								(int)cave->squares[y][x].cost,
								(int)cave->squares[y][x].when);
					}

					prt(out_val, 0, 0);
					move_cursor_relative(y, x);
					press = inkey_m();

					if (press.type == EVT_MOUSE) {
						/* Stop on right click */
						if (press.mouse.button == 2)
							break;

						/* Sometimes stop at "space" key */
						if (press.mouse.button && !(mode & (TARGET_LOOK)))
							break;
					} else {
						/* Stop on everything but "return"/"space" */
						if ((press.key.code != KC_ENTER) &&
							(press.key.code != ' '))
							break;

						/* Sometimes stop at "space" key */
						if ((press.key.code == ' ') && !(mode & (TARGET_LOOK)))
							break;
					}

					/* Change the intro */
					s2 = "also carrying ";
				}

				/* Double break */
				if (obj) break;

				/* Use a preposition */
				s2 = "on ";
			}
		}

		/* A trap */
		if (square_isvisibletrap(cave, y, x)) {
			struct trap *trap = cave->squares[y][x].trap;

			/* Not boring */
			boring = FALSE;

			/* Interact */
			while (1) {
				/* Change the intro */
				if (cave->squares[y][x].mon < 0) {
					s1 = "You are ";
					s2 = "on ";
				} else {
					s1 = "You see ";
					s2 = "";
				}

				/* Pick proper indefinite article */
				s3 = (is_a_vowel(trap->kind->desc[0])) ? "an " : "a ";

				/* Describe, and prompt for recall */
				if (player->wizard) {
					strnfmt(out_val, sizeof(out_val),
							"%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2,
							s3, trap->kind->name, coords, y, x,
							(int)cave->squares[y][x].cost,
							(int)cave->squares[y][x].when);
				} else {
					strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", 
							s1, s2, s3, trap->kind->desc, coords);
				}

				prt(out_val, 0, 0);

				/* Place cursor */
				move_cursor_relative(y, x);

				/* Command */
				press = inkey_m();
		
				/* Stop on everything but "return"/"space" */
				if ((press.key.code != KC_ENTER) && (press.key.code != ' '))
					break;
		
				/* Sometimes stop at "space" key */
				if ((press.key.code == ' ') && !(mode & (TARGET_LOOK)))
					break;
			}
		}
	
		/* Double break */
		if (square_isvisibletrap(cave, y, x))
			break;
	
		/* Assume not floored */
		floor_num = scan_floor(floor_list, floor_max, y, x, 0x0A, NULL);

		/* Scan all marked objects in the grid */
		if ((floor_num > 0) &&
		    (!(player->timed[TMD_BLIND]) ||
			 (y == player->py && x == player->px))) {
			/* Not boring */
			boring = FALSE;

			track_object(player->upkeep, floor_list[0]);
			handle_stuff(player);

			/* If there is more than one item... */
			if (floor_num > 1)
				while (1) {
					/* Describe the pile */
					if (player->wizard) {
						strnfmt(out_val, sizeof(out_val),
								"%s%s%sa pile of %d objects, %s (%d:%d, cost=%d, when=%d).",
								s1, s2, s3, floor_num, coords, y, x,
								(int)cave->squares[y][x].cost,
								(int)cave->squares[y][x].when);
					} else {
						strnfmt(out_val, sizeof(out_val),
								"%s%s%sa pile of %d objects, %s.",
								s1, s2, s3, floor_num, coords);
					}

					prt(out_val, 0, 0);
					move_cursor_relative(y, x);
					press = inkey_m();

					/* Display objects */
					if (((press.type == EVT_MOUSE) && (press.mouse.button == 1)
						 && (KEY_GRID_X(press) == x) && 
						 (KEY_GRID_Y(press) == y)) ||
						((press.type == EVT_KBRD) && (press.key.code == 'r'))) {
						int rdone = 0;
						int pos;
						while (!rdone) {
							/* Save screen */
							screen_save();

							/* Display */
							show_floor(floor_list, floor_num,
									   (OLIST_WEIGHT | OLIST_GOLD), NULL);

							/* Describe the pile */
							prt(out_val, 0, 0);
							press = inkey_m();

							/* Load screen */
							screen_load();

							if (press.type == EVT_MOUSE) {
								pos = press.mouse.y-1;
							} else {
								pos = press.key.code - 'a';
							}
							if (0 <= pos && pos < floor_num) {
								track_object(player->upkeep, floor_list[pos]);
								handle_stuff(player);
								continue;
							}
							rdone = 1;
						}

						/* Now that the user's done with the display loop,
						 * let's do the outer loop over again */
						continue;
					}

					/* Done */
					break;
				}
			/* Only one object to display */
			else {
				/* Get the single object in the list */
				struct object *obj = floor_list[0];

				/* Allow user to recall an object */
				press = target_recall_loop_object(obj, y, x, out_val, s1, s2,
												  s3, coords);

				/* Stop on everything but "return"/"space" */
				if ((press.key.code != KC_ENTER) && (press.key.code != ' '))
					break;

				/* Sometimes stop at "space" key */
				if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break;

				/* Plurals */
				s1 = VERB_AGREEMENT(obj->number, "It is ", "They are ");

				/* Preposition */
				s2 = "on ";
			}

		}

		/* Double break */
		if (obj) break;

		name = square_apparent_name(cave, player, y, x);

		/* Terrain feature if needed */
		if (boring || square_isinteresting(cave, y, x)) {
			/* Hack -- handle unknown grids */

			/* Pick a prefix */
			if (*s2 && square_isdoor(cave, y, x)) s2 = "in ";

			/* Pick proper indefinite article */
			s3 = (is_a_vowel(name[0])) ? "an " : "a ";

			/* Hack -- special introduction for store doors */
			if (square_isshop(cave, y, x))
				s3 = "the entrance to the ";

			/* Display a message */
			if (player->wizard) {
				strnfmt(out_val, sizeof(out_val),
						"%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3,
						name, coords, y, x, (int)cave->squares[y][x].cost,
						(int)cave->squares[y][x].when);
			} else {
				strnfmt(out_val, sizeof(out_val),
						"%s%s%s%s, %s.", s1, s2, s3, name, coords);
			}

			prt(out_val, 0, 0);
			move_cursor_relative(y, x);
			press = inkey_m();

			if (press.type == EVT_MOUSE) {
				/* Stop on right click */
				if (press.mouse.button == 2)
					break;
			} else {
				/* Stop on everything but "return"/"space" */
				if ((press.key.code != KC_ENTER) && (press.key.code != ' '))
					break;
			}
		}

		/* Stop on everything but "return" */
		if (press.type == EVT_MOUSE) {
				/* Stop on right click */
				if (press.mouse.button != 2)
					break;
		} else {
    			if (press.key.code != KC_ENTER) break;
		}
	}

	mem_free(floor_list);

	/* Keep going */
	return (press);
}