Esempio n. 1
0
/**
 * Helper function used with ranged_helper by do_cmd_fire.
 */
static struct attack_result make_ranged_shot(struct object *ammo, int y, int x)
{
	char *hit_verb = mem_alloc(20 * sizeof(char));
	struct attack_result result = {FALSE, 0, 0, hit_verb};
	struct object *bow = equipped_item_by_slot_name(player, "shooting");
	struct monster *mon = square_monster(cave, y, x);
	int chance = chance_of_missile_hit(player, ammo, bow, y, x);
	int multiplier = player->state.ammo_mult;
	const struct brand *b = NULL;
	const struct slay *s = NULL;

	my_strcpy(hit_verb, "hits", sizeof(hit_verb));

	/* Did we hit it (penalize distance travelled) */
	if (!test_hit(chance, mon->race->ac, mflag_has(mon->mflag, MFLAG_VISIBLE)))
		return result;

	result.success = TRUE;

	improve_attack_modifier(ammo, mon, &b, &s, result.hit_verb, TRUE, TRUE,
							FALSE);
	improve_attack_modifier(bow, mon, &b, &s, result.hit_verb, TRUE, TRUE,
							FALSE);

	result.dmg = ranged_damage(ammo, bow, b, s, multiplier);
	result.dmg = critical_shot(ammo->weight, ammo->to_h, result.dmg,
							   &result.msg_type);

	object_notice_attack_plusses(bow);

	return result;
}
Esempio n. 2
0
/* Make doors */
static void project_feature_handler_MAKE_DOOR(project_feature_handler_context_t *context)
{
	const int x = context->x;
	const int y = context->y;

	/* Require a grid without monsters */
	if (square_monster(cave, y, x)) return;

	/* Require a floor grid */
	if (!square_isfloor(cave, y, x)) return;

	/* Push objects off the grid */
	if (square_object(cave, y, x))
		push_object(y,x);

	/* Create closed door */
	square_add_door(cave, y, x, true);

	/* Observe */
	if (square_isknown(cave, y, x))
		context->obvious = true;

	/* Update the visuals */
	player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS);
}
Esempio n. 3
0
/**
 * Helper function used with ranged_helper by do_cmd_throw.
 */
static struct attack_result make_ranged_throw(struct object *obj, int y, int x)
{
	char *hit_verb = mem_alloc(20*sizeof(char));
	struct attack_result result = {FALSE, 0, 0, hit_verb};
	struct monster *mon = square_monster(cave, y, x);
	int chance = chance_of_missile_hit(player, obj, NULL, y, x);
	int multiplier = 1;
	const struct brand *b = NULL;
	const struct slay *s = NULL;

	my_strcpy(hit_verb, "hits", sizeof(hit_verb));

	/* If we missed then we're done */
	if (!test_hit(chance, mon->race->ac, mflag_has(mon->mflag, MFLAG_VISIBLE)))
		return result;

	result.success = TRUE;

	improve_attack_modifier(obj, mon, &b, &s, result.hit_verb, TRUE, TRUE,
							FALSE);

	result.dmg = ranged_damage(obj, NULL, b, s, multiplier);
	result.dmg = critical_norm(obj->weight, obj->to_h, result.dmg,
							   &result.msg_type);

	return result;
}
Esempio n. 4
0
/**
 * Attack the monster at the given location
 *
 * We get blows until energy drops below that required for another blow, or
 * until the target monster dies. Each blow is handled by py_attack_real().
 * We don't allow @ to spend more than 100 energy in one go, to avoid slower
 * monsters getting double moves.
 */
void py_attack(int y, int x)
{
	int blow_energy = 100 * z_info->move_energy / player->state.num_blows;
	int blows = 0;
	bool fear = FALSE;
	struct monster *mon = square_monster(cave, y, x);
	
	/* disturb the player */
	disturb(player, 0);

	/* Initialize the energy used */
	player->upkeep->energy_use = 0;

	/* Attack until energy runs out or enemy dies. We limit energy use to 100
	 * to avoid giving monsters a possible double move. */
	while (player->energy >= blow_energy * (blows + 1)) {
		bool stop = py_attack_real(y, x, &fear);
		player->upkeep->energy_use += blow_energy;
		if (player->upkeep->energy_use + blow_energy > z_info->move_energy ||
			stop) break;
		blows++;
	}
	
	/* Hack - delay fear messages */
	if (fear && mflag_has(mon->mflag, MFLAG_VISIBLE)) {
		char m_name[80];
		/* Don't set monster_desc flags, since add_monster_message does string
		 * processing on m_name */
		monster_desc(m_name, sizeof(m_name), mon, MDESC_DEFAULT);
		add_monster_message(m_name, mon, MON_MSG_FLEE_IN_TERROR, TRUE);
	}
}
Esempio n. 5
0
/**
 * Attempts to place a copy of the given monster at the given position in
 * the dungeon.
 *
 * All of the monster placement routines eventually call this function. This
 * is what actually puts the monster in the dungeon (i.e., it notifies the cave
 * and sets the monsters position). The dungeon loading code also calls this
 * function directly.
 *
 * `origin` is the item origin to use for any monster drops (e.g. ORIGIN_DROP,
 * ORIGIN_DROP_PIT, etc.) The dungeon loading code calls this with origin = 0,
 * which prevents the monster's drops from being generated again.
 *
 * Returns the m_idx of the newly copied monster, or 0 if the placement fails.
 */
s16b place_monster(struct chunk *c, int y, int x, struct monster *mon,
				   byte origin)
{
	s16b m_idx;
	struct monster *new_mon;

	assert(square_in_bounds(c, y, x));
	assert(!square_monster(c, y, x));

	/* Get a new record */
	m_idx = mon_pop(c);
	if (!m_idx) return 0;

	/* Copy the monster */
	new_mon = cave_monster(c, m_idx);
	memcpy(new_mon, mon, sizeof(struct monster));

	/* Set the ID */
	new_mon->midx = m_idx;

	/* Set the location */
	c->squares[y][x].mon = new_mon->midx;
	new_mon->fy = y;
	new_mon->fx = x;
	assert(square_monster(c, y, x) == new_mon);

	update_mon(new_mon, c, true);

	/* Hack -- Count the number of "reproducers" */
	if (rf_has(new_mon->race->flags, RF_MULTIPLY)) num_repro++;

	/* Count racial occurrences */
	new_mon->race->cur_num++;

	/* Create the monster's drop, if any */
	if (origin)
		(void)mon_create_drop(c, new_mon, origin);

	/* Make mimics start mimicking */
	if (origin && new_mon->race->mimic_kinds) {
		mon_create_mimicked_object(c, new_mon, m_idx);
	}

	/* Result */
	return m_idx;
}
Esempio n. 6
0
/**
 * This routine will Perma-Light all grids in the set passed in.
 *
 * This routine is used (only) by "light_room()"
 *
 * Dark grids are illuminated.
 *
 * Also, process all affected monsters.
 *
 * SMART monsters always wake up when illuminated
 * NORMAL monsters wake up 1/4 the time when illuminated
 * STUPID monsters wake up 1/10 the time when illuminated
 */
static void cave_light(struct point_set *ps)
{
	int i;

	/* Apply flag changes */
	for (i = 0; i < ps->n; i++)
	{
		int y = ps->pts[i].y;
		int x = ps->pts[i].x;

		/* Perma-Light */
		sqinfo_on(cave->squares[y][x].info, SQUARE_GLOW);
	}

	/* Fully update the visuals */
	player->upkeep->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS);

	/* Update stuff */
	update_stuff(player);

	/* Process the grids */
	for (i = 0; i < ps->n; i++)
	{
		int y = ps->pts[i].y;
		int x = ps->pts[i].x;

		/* Redraw the grid */
		square_light_spot(cave, y, x);

		/* Process affected monsters */
		if (cave->squares[y][x].mon > 0)
		{
			int chance = 25;

			monster_type *m_ptr = square_monster(cave, y, x);

			/* Stupid monsters rarely wake up */
			if (rf_has(m_ptr->race->flags, RF_STUPID)) chance = 10;

			/* Smart monsters always wake up */
			if (rf_has(m_ptr->race->flags, RF_SMART)) chance = 100;

			/* Sometimes monsters wake up */
			if (m_ptr->m_timed[MON_TMD_SLEEP] && (randint0(100) < chance))
			{
				/* Wake up! */
				mon_clear_timed(m_ptr, MON_TMD_SLEEP,
					MON_TMD_FLG_NOTIFY, FALSE);

			}
		}
	}
}
Esempio n. 7
0
/**
 * Apply blow after effects
 */
static bool blow_after_effects(int y, int x, bool quake)
{
	/* Apply earthquake brand */
	if (quake) {
		effect_simple(EF_EARTHQUAKE, "0", 0, 10, 0, NULL);

		/* Monster may be dead or moved */
		if (!square_monster(cave, y, x))
			return TRUE;
	}

	return FALSE;
}
Esempio n. 8
0
/**
 * Check if the monster can move any monster on the relevant grid
 */
static bool monster_can_move(struct chunk *c, struct monster *mon, int y, int x)
{
	struct monster *mon1 = square_monster(c, y, x);

	/* No monster */
	if (!mon1) return true;

	if (rf_has(mon->race->flags, RF_MOVE_BODY) &&
		compare_monsters(mon, mon1) > 0) {
		return true;
	}

	return false;
}
Esempio n. 9
0
/**
 * Try to push past / kill another monster.  Returns true on success.
 */
static bool process_monster_try_push(struct chunk *c, struct monster *mon,
									 const char *m_name, int nx, int ny)
{
	struct monster *mon1 = square_monster(c, ny, nx);
	struct monster_lore *lore = get_lore(mon->race);

	/* Kill weaker monsters */
	int kill_ok = rf_has(mon->race->flags, RF_KILL_BODY);

	/* Move weaker monsters if they can swap places */
	/* (not in a wall) */
	int move_ok = (rf_has(mon->race->flags, RF_MOVE_BODY) &&
				   square_ispassable(c, mon->fy, mon->fx));

	if (compare_monsters(mon, mon1) > 0) {
		/* Learn about pushing and shoving */
		if (mflag_has(mon->mflag, MFLAG_VISIBLE)) {
			rf_on(lore->flags, RF_KILL_BODY);
			rf_on(lore->flags, RF_MOVE_BODY);
		}

		if (kill_ok || move_ok) {
			/* Get the names of the monsters involved */
			char n_name[80];
			monster_desc(n_name, sizeof(n_name), mon1, MDESC_IND_HID);

			/* Reveal mimics */
			if (is_mimicking(mon1))
				become_aware(mon1);

			/* Note if visible */
			if (mflag_has(mon->mflag, MFLAG_VISIBLE) &&
				mflag_has(mon->mflag, MFLAG_VIEW))
				msg("%s %s %s.", m_name,
					kill_ok ? "tramples over" : "pushes past", n_name);

			/* Monster ate another monster */
			if (kill_ok)
				delete_monster(ny, nx);

			monster_swap(mon->fy, mon->fx, ny, nx);
			return true;
		} 
	}

	return false;
}
Esempio n. 10
0
/**
 * Set target to closest monster.
 */
bool target_set_closest(int mode)
{
	int y, x;
	struct monster *mon;
	char m_name[80];
	struct point_set *targets;

	/* Cancel old target */
	target_set_monster(0);

	/* Get ready to do targetting */
	targets = target_get_monsters(mode);

	/* If nothing was prepared, then return */
	if (point_set_size(targets) < 1) {
		msg("No Available Target.");
		point_set_dispose(targets);
		return false;
	}

	/* Find the first monster in the queue */
	y = targets->pts[0].y;
	x = targets->pts[0].x;
	mon = square_monster(cave, y, x);
	
	/* Target the monster, if possible */
	if (!target_able(mon)) {
		msg("No Available Target.");
		point_set_dispose(targets);
		return false;
	}

	/* Target the monster */
	monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL);
	if (!(mode & TARGET_QUIET))
		msg("%s is targeted.", m_name);

	/* Set up target information */
	monster_race_track(player->upkeep, mon->race);
	health_track(player->upkeep, mon);
	target_set_monster(mon);

	point_set_dispose(targets);
	return true;
}
Esempio n. 11
0
/**
 * Return a target set of target_able monsters.
 */
struct point_set *target_get_monsters(int mode, monster_predicate pred)
{
	int y, x;
	int min_y, min_x, max_y, max_x;
	struct point_set *targets = point_set_new(TS_INITIAL_SIZE);

	/* Get the current panel */
	get_panel(&min_y, &min_x, &max_y, &max_x);

	/* Scan for targets */
	for (y = min_y; y < max_y; y++) {
		for (x = min_x; x < max_x; x++) {
			struct loc grid = loc(x, y);

			/* Check bounds */
			if (!square_in_bounds_fully(cave, grid)) continue;

			/* Require "interesting" contents */
			if (!target_accept(y, x)) continue;

			/* Special mode */
			if (mode & (TARGET_KILL)) {
				struct monster *mon = square_monster(cave, grid);

				/* Must contain a monster */
				if (mon == NULL) continue;

				/* Must be a targettable monster */
				if (!target_able(mon)) continue;

				/* Must be the right sort of monster */
				if (pred && !pred(mon)) continue;
			}

			/* Save the location */
			add_to_point_set(targets, grid);
		}
	}

	sort(targets->pts, point_set_size(targets), sizeof(*(targets->pts)),
		 cmp_distance);
	return targets;
}
Esempio n. 12
0
/**
 * This routine will Perma-Light all grids in the set passed in.
 *
 * This routine is used (only) by "light_room()"
 *
 * Dark grids are illuminated.
 *
 * Also, process all affected monsters.
 *
 * SMART monsters always wake up when illuminated
 * NORMAL monsters wake up 1/4 the time when illuminated
 * STUPID monsters wake up 1/10 the time when illuminated
 */
static void cave_light(struct point_set *ps)
{
	int i;

	/* Apply flag changes */
	for (i = 0; i < ps->n; i++)	{
		int y = ps->pts[i].y;
		int x = ps->pts[i].x;

		/* Perma-Light */
		sqinfo_on(cave->squares[y][x].info, SQUARE_GLOW);
	}

	/* Process the grids */
	for (i = 0; i < ps->n; i++)	{
		int y = ps->pts[i].y;
		int x = ps->pts[i].x;

		/* Redraw the grid */
		square_light_spot(cave, y, x);

		/* Process affected monsters */
		if (cave->squares[y][x].mon > 0) {
			int chance = 25;

			struct monster *mon = square_monster(cave, y, x);

			/* Stupid monsters rarely wake up */
			if (monster_is_stupid(mon)) chance = 10;

			/* Smart monsters always wake up */
			if (monster_is_smart(mon)) chance = 100;

			/* Sometimes monsters wake up */
			if (mon->m_timed[MON_TMD_SLEEP] && (randint0(100) < chance)) {
				mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOTIFY, false);
			}
		}
	}
}
Esempio n. 13
0
/**
 * Set the target to a location
 */
void target_set_location(int y, int x)
{
	/* Legal target */
	if (square_in_bounds_fully(cave, y, x)) {
		struct monster *mon = square_monster(cave, y, x);

		/* Save target info */
		target_set = TRUE;
		target_who = NULL;
		if (mon && target_able(mon))
			target_who = mon;
		target_y = y;
		target_x = x;
		return;
	}

	/* Reset target info */
	target_set = FALSE;
	target_who = 0;
	target_y = 0;
	target_x = 0;
}
Esempio n. 14
0
/**
 * Determine if a given location is "interesting"
 */
bool target_accept(int y, int x)
{
	struct loc grid = loc(x, y);
	struct object *obj;

	/* Player grids are always interesting */
	if (square(cave, grid).mon < 0) return true;

	/* Handle hallucination */
	if (player->timed[TMD_IMAGE]) return false;

	/* Obvious monsters */
	if (square(cave, grid).mon > 0) {
		struct monster *mon = square_monster(cave, grid);
		if (monster_is_obvious(mon)) {
			return true;
		}
	}

	/* Traps */
	if (square_isvisibletrap(cave, grid)) return true;

	/* Scan all objects in the grid */
	for (obj = square_object(player->cave, grid); obj; obj = obj->next) {
		/* Memorized object */
		if ((obj->kind == unknown_item_kind) || !ignore_known_item_ok(obj)) {
			return true;
		}
	}

	/* Interesting memorized features */
	if (square_isknown(cave, grid) && square_isinteresting(cave, grid)) {
		return true;
	}

	/* Nope */
	return false;
}
Esempio n. 15
0
/**
 * Determine if a given location is "interesting"
 */
bool target_accept(int y, int x)
{
	struct object *obj;

	/* Player grids are always interesting */
	if (cave->squares[y][x].mon < 0) return (true);

	/* Handle hallucination */
	if (player->timed[TMD_IMAGE]) return (false);

	/* Visible monsters */
	if (cave->squares[y][x].mon > 0) {
		struct monster *mon = square_monster(cave, y, x);

		/* Visible monsters */
		if (mflag_has(mon->mflag, MFLAG_VISIBLE) &&
			!mflag_has(mon->mflag, MFLAG_UNAWARE))
			return (true);
	}

	/* Traps */
	if (square_isvisibletrap(cave, y, x))
		return(true);

	/* Scan all objects in the grid */
	for (obj = square_object(cave_k, y, x); obj; obj = obj->next)
		/* Memorized object */
		if (!ignore_known_item_ok(obj)) return (true);

	/* Interesting memorized features */
	if (square_isknown(cave, y, x) && square_isinteresting(cave, y, x))
		return (true);

	/* Nope */
	return (false);
}
Esempio n. 16
0
/**
 * Determine if a given location is "interesting"
 */
bool target_accept(int y, int x)
{
	object_type *obj;

	/* Player grids are always interesting */
	if (cave->squares[y][x].mon < 0) return (TRUE);

	/* Handle hallucination */
	if (player->timed[TMD_IMAGE]) return (FALSE);

	/* Visible monsters */
	if (cave->squares[y][x].mon > 0) {
		monster_type *m_ptr = square_monster(cave, y, x);

		/* Visible monsters */
		if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE) &&
			!mflag_has(m_ptr->mflag, MFLAG_UNAWARE))
			return (TRUE);
	}

    /* Traps */
    if (square_isvisibletrap(cave, y, x))
		return(TRUE);

	/* Scan all objects in the grid */
	for (obj = square_object(cave, y, x); obj; obj = obj->next)
		/* Memorized object */
		if (obj->marked && !ignore_item_ok(obj)) return (TRUE);

	/* Interesting memorized features */
	if (square_ismark(cave, y, x) && !square_isboring(cave, y, x))
		return (TRUE);

	/* Nope */
	return (FALSE);
}
Esempio n. 17
0
/**
 * Return a target set of target_able monsters.
 */
struct point_set *target_get_monsters(int mode)
{
	int y, x;
	int min_y, min_x, max_y, max_x;
	struct point_set *targets = point_set_new(TS_INITIAL_SIZE);

	/* Get the current panel */
	get_panel(&min_y, &min_x, &max_y, &max_x);

	/* Scan for targets */
	for (y = min_y; y < max_y; y++) {
		for (x = min_x; x < max_x; x++) {
			/* Check bounds */
			if (!square_in_bounds_fully(cave, y, x)) continue;

			/* Require "interesting" contents */
			if (!target_accept(y, x)) continue;

			/* Special mode */
			if (mode & (TARGET_KILL)) {
				/* Must contain a monster */
				if (!(cave->squares[y][x].mon > 0)) continue;

				/* Must be a targettable monster */
			 	if (!target_able(square_monster(cave, y, x))) continue;
			}

			/* Save the location */
			add_to_point_set(targets, y, x);
		}
	}

	sort(targets->pts, point_set_size(targets), sizeof(*(targets->pts)),
		 cmp_distance);
	return targets;
}
Esempio n. 18
0
/**
 * Take one step along the current "run" path
 *
 * Called with a real direction to begin a new run, and with zero
 * to continue a run in progress.
 */
void run_step(int dir)
{
	/* Start or continue run */
	if (dir) {
		/* Initialize */
		run_init(dir);

		/* Hack -- Set the run counter */
		player->upkeep->running = 1000;

		/* Calculate torch radius */
		player->upkeep->update |= (PU_TORCH);
	} else {
		/* Continue running */
		if (!player->upkeep->running_withpathfind) {
			/* Update regular running */
			if (run_test()) {
				/* Disturb */
				disturb(player, 0);
				return;
			}
		} else if (pf_result_index < 0) {
			/* Pathfinding, and the path is finished */
			disturb(player, 0);
			player->upkeep->running_withpathfind = false;
			return;
		} else {
			int y = player->py + ddy[pf_result[pf_result_index] - '0'];
			int x = player->px + ddx[pf_result[pf_result_index] - '0'];

			if (pf_result_index == 0) {
				/* Known wall */
				if (square_isknown(cave, y, x) &&
					!square_ispassable(cave, y, x)) {
					disturb(player, 0);
					player->upkeep->running_withpathfind = false;
					return;
				}
			} else if (pf_result_index > 0) {
				struct object *obj;

				/* If the player has computed a path that is going to end up
				 * in a wall, we notice this and convert to a normal run. This
				 * allows us to click on unknown areas to explore the map.
				 *
				 * We have to look ahead two, otherwise we don't know which is
				 * the last direction moved and don't initialise the run
				 * properly. */
				y = player->py + ddy[pf_result[pf_result_index] - '0'];
				x = player->px + ddx[pf_result[pf_result_index] - '0'];

				/* Known wall */
				if (square_isknown(cave, y, x) &&
					!square_ispassable(cave, y, x)) {
					disturb(player, 0);
					player->upkeep->running_withpathfind = false;
					return;
				}

				/* Visible monsters abort running */
				if (cave->squares[y][x].mon > 0) {
					struct monster *mon = square_monster(cave, y, x);

					/* Visible monster */
					if (mflag_has(mon->mflag, MFLAG_VISIBLE)) {
						disturb(player, 0);
						player->upkeep->running_withpathfind = false;
						return;
					}
				}

				/* Visible objects abort running */
				for (obj = square_object(cave, y, x); obj; obj = obj->next)
					/* Visible object */
					if (obj->known && !ignore_item_ok(obj)) {
					disturb(player, 0);
					player->upkeep->running_withpathfind = false;
					return;
				}

				/* Get step after */
				y = y + ddy[pf_result[pf_result_index - 1] - '0'];
				x = x + ddx[pf_result[pf_result_index - 1] - '0'];

				/* Known wall, so run the direction we were going */
				if (square_isknown(cave, y, x) &&
					!square_ispassable(cave, y, x)) {
					player->upkeep->running_withpathfind = false;
					run_init(pf_result[pf_result_index] - '0');
				}
			}

			/* Now actually run the step if we're still going */
			run_cur_dir = pf_result[pf_result_index--] - '0';
		}
	}

	/* Decrease counter if it hasn't been cancelled */
	if (player->upkeep->running)
		player->upkeep->running--;
	else if (!player->upkeep->running_withpathfind)
		return;

	/* Take time */
	player->upkeep->energy_use = z_info->move_energy;

	/* Move the player; running straight into a trap == trying to disarm */
	move_player(run_cur_dir, dir ? true : false);

	/* Prepare the next step */
	if (player->upkeep->running) {
		cmdq_push(CMD_RUN);
		cmd_set_arg_direction(cmdq_peek(), "direction", 0);
	}
}
Esempio n. 19
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);
}
Esempio n. 20
0
/**
 * Places a monster (of the specified "type") near the given
 * location.  Return the siummoned monster's level iff a monster was
 * actually summoned.
 *
 * We will attempt to place the monster up to 10 times before giving up.
 *
 * This function takes the "monster level"
 * of the summoning monster as a parameter, and use that, along with
 * the current dungeon level, to help determine the level of the
 * desired monster.  Note that this is an upper bound, and also
 * tends to "prefer" monsters of that level.  Currently, we use
 * the average of the dungeon and monster levels, and then add
 * five to allow slight increases in monster power.
 *
 * Note that we use the new "monster allocation table" creation code
 * to restrict the "get_mon_num()" function to the set of "legal"
 * monsters, making this function much faster and more reliable.
 *
 * Note that this function may not succeed, though this is very rare.
 */
int summon_specific(int y1, int x1, int lev, int type, bool delay, bool call)
{
	int i, x = 0, y = 0;

	struct monster *mon;
	struct monster_race *race;

	/* Look for a location, allow up to 4 squares away */
	for (i = 0; i < 60; ++i) {
		/* Pick a distance */
		int d = (i / 15) + 1;

		/* Pick a location */
		scatter(cave, &y, &x, y1, x1, d, true);

		/* Require "empty" floor grid */
		if (!square_isempty(cave, y, x)) continue;

		/* No summon on glyphs */
		if (square_iswarded(cave, y, x) || square_isdecoyed(cave, y, x)) {
			continue;
		}

		/* Okay */
		break;
	}

	/* Failure */
	if (i == 60) return (0);

	/* Save the "summon" type */
	summon_specific_type = type;

	/* Use the new calling scheme if requested */
	if (call && (type != summon_name_to_idx("UNIQUE")) &&
		(type != summon_name_to_idx("WRAITH"))) {
		return (call_monster(y, x));
	}

	/* Prepare allocation table */
	get_mon_num_prep(summon_specific_okay);

	/* Pick a monster, using the level calculation */
	race = get_mon_num((player->depth + lev) / 2 + 5);

	/* Prepare allocation table */
	get_mon_num_prep(NULL);

	/* Handle failure */
	if (!race) return (0);

	/* Attempt to place the monster (awake, don't allow groups) */
	if (!place_new_monster(cave, y, x, race, false, false, ORIGIN_DROP_SUMMON))
		return (0);

	/* Success, return the level of the monster */
	mon = square_monster(cave, y, x);

	/* If delay, try to let the player act before the summoned monsters,
	 * including slowing down faster monsters for one turn */
	/* XXX should this now be hold monster for a turn? */
	if (delay) {
		mon->energy = 0;
		if (mon->race->speed > player->state.speed)
			mon_inc_timed(mon, MON_TMD_SLOW, 1,	MON_TMD_FLG_NOMESSAGE, false);
	}

	return (mon->race->level);
}
Esempio n. 21
0
/**
 * Attack the monster at the given location with a single blow.
 */
static bool py_attack_real(int y, int x, bool *fear)
{
	size_t i;

	/* Information about the target of the attack */
	struct monster *mon = square_monster(cave, y, x);
	char m_name[80];
	bool stop = FALSE;

	/* The weapon used */
	struct object *obj = equipped_item_by_slot_name(player, "weapon");

	/* Information about the attack */
	int chance = py_attack_hit_chance(obj);
	bool do_quake = FALSE;
	bool success = FALSE;

	/* Default to punching for one damage */
	char verb[20];
	int dmg = 1;
	u32b msg_type = MSG_HIT;

	/* Default to punching for one damage */
	my_strcpy(verb, "punch", sizeof(verb));

	/* Extract monster name (or "it") */
	monster_desc(m_name, sizeof(m_name), mon, 
				 MDESC_OBJE | MDESC_IND_HID | MDESC_PRO_HID);

	/* Auto-Recall if possible and visible */
	if (mflag_has(mon->mflag, MFLAG_VISIBLE))
		monster_race_track(player->upkeep, mon->race);

	/* Track a new monster */
	if (mflag_has(mon->mflag, MFLAG_VISIBLE))
		health_track(player->upkeep, mon);

	/* Handle player fear (only for invisible monsters) */
	if (player_of_has(player, OF_AFRAID)) {
		msgt(MSG_AFRAID, "You are too afraid to attack %s!", m_name);
		return FALSE;
	}

	/* Disturb the monster */
	mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, FALSE);

	/* See if the player hit */
	success = test_hit(chance, mon->race->ac,
					   mflag_has(mon->mflag, MFLAG_VISIBLE));

	/* If a miss, skip this hit */
	if (!success) {
		msgt(MSG_MISS, "You miss %s.", m_name);
		return FALSE;
	}

	/* Handle normal weapon */
	if (obj) {
		int j;
		const struct brand *b = NULL;
		const struct slay *s = NULL;

		my_strcpy(verb, "hit", sizeof(verb));

		/* Get the best attack from all slays or
		 * brands on all non-launcher equipment */
		for (j = 2; j < player->body.count; j++) {
			struct object *obj = slot_object(player, j);
			if (obj)
				improve_attack_modifier(obj, mon, &b, &s, verb, FALSE, TRUE,
										FALSE);
		}

		improve_attack_modifier(obj, mon, &b, &s, verb, FALSE, TRUE, FALSE);

		dmg = melee_damage(obj, b, s);
		dmg = critical_norm(obj->weight, obj->to_h, dmg, &msg_type);

		/* Learn by use for the weapon */
		object_notice_attack_plusses(obj);

		if (player_of_has(player, OF_IMPACT) && dmg > 50) {
			do_quake = TRUE;
			equip_notice_flag(player, OF_IMPACT);
		}
	}

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

	/* Apply the player damage bonuses */
	dmg += player_damage_bonus(&player->state);

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

	for (i = 0; i < N_ELEMENTS(melee_hit_types); i++) {
		const char *dmg_text = "";

		if (msg_type != melee_hit_types[i].msg)
			continue;

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

		if (melee_hit_types[i].text)
			msgt(msg_type, "You %s %s%s. %s", verb, m_name, dmg_text,
					melee_hit_types[i].text);
		else
			msgt(msg_type, "You %s %s%s.", verb, m_name, dmg_text);
	}

	/* Pre-damage side effects */
	blow_side_effects(player, mon);

	/* Damage, check for fear and death */
	stop = mon_take_hit(mon, dmg, fear, NULL);

	if (stop)
		(*fear) = FALSE;

	/* Post-damage effects */
	if (blow_after_effects(y, x, do_quake))
		stop = TRUE;

	return stop;
}
Esempio n. 22
0
/**
 * This is a helper function used by do_cmd_throw and do_cmd_fire.
 *
 * It abstracts out the projectile path, display code, identify and clean up
 * logic, while using the 'attack' parameter to do work particular to each
 * kind of attack.
 */
static void ranged_helper(struct object *obj, int dir, int range, int shots,
						  ranged_attack attack)
{
	int i, j;

	char o_name[80];

	int path_n;
	struct loc path_g[256];

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

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

	bool hit_target = FALSE;
	bool none_left = FALSE;

	struct object *missile;

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

	/* Sound */
	sound(MSG_SHOOT);

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

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

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

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

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

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

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

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

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

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

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

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

			if (result.success) {
				hit_target = TRUE;

				object_notice_attack_plusses(obj);

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

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

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

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

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

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

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

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

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

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

	/* Drop (or break) near that location */
	drop_near(cave, missile, breakage_chance(missile, hit_target), y, x, TRUE);
}
Esempio n. 23
0
bool findpath(int y, int x)
{
	int i, j, k;
	int dir = 10;
	bool try_again;
	int cur_distance;

	fill_terrain_info();

	terrain[player->py - oy][player->px - ox] = 1;

	if ((x >= ox) && (x < ex) && (y >= oy) && (y < ey)) {
		if ((cave->squares[y][x].mon > 0) &&
			mflag_has(square_monster(cave, y, x)->mflag, MFLAG_VISIBLE))
			terrain[y - oy][x - ox] = MAX_PF_LENGTH;

		terrain[y - oy][x - ox] = MAX_PF_LENGTH;
	} else {
		bell("Target out of range.");
		return (FALSE);
	}

	/* 
	 * And now starts the very naive and very 
	 * inefficient pathfinding algorithm
	 */
	do {
		try_again = FALSE;

		for (j = oy + 1; j < ey - 1; j++) {
			for (i = ox + 1; i < ex - 1; i++) {
				cur_distance = terrain[j - oy][i - ox] + 1;

				if ((cur_distance > 0) && (cur_distance < MAX_PF_LENGTH)) {
					for (dir = 1; dir < 10; dir++) {
						int next_y, next_x;
						if (dir == 5)
							dir++;
						next_y = j - oy + ddy[dir];
						next_x = i - ox + ddx[dir];

						MARK_DISTANCE(terrain[next_y][next_x], cur_distance);
					}
				}
			}
		}

		if (terrain[y - oy][x - ox] < MAX_PF_LENGTH)
			try_again = FALSE;

	}
	while (try_again) ;

	/* Failure */
	if (terrain[y - oy][x - ox] == MAX_PF_LENGTH) {
		bell("Target space unreachable.");
		return (FALSE);
	}

	/* Success */
	i = x;
	j = y;

	pf_result_index = 0;

	while ((i != player->px) || (j != player->py)) {
		cur_distance = terrain[j - oy][i - ox] - 1;
		for (k = 0; k < 8; k++) {
			dir = dir_search[k];
			if (terrain[j - oy + ddy[dir]][i - ox + ddx[dir]] == cur_distance)
				break;
		}

		/* Should never happen */
		assert ((dir != 10) && (dir != 5));

		pf_result[pf_result_index++] = '0' + (char)(10 - dir);
		i += ddx[dir];
		j += ddy[dir];
	}

	pf_result_index--;

	return (TRUE);
}
Esempio n. 24
0
/**
 * Handle "target" and "look".
 *
 * Note that this code can be called from "get_aim_dir()".
 *
 * Currently, when "flag" is true, that is, when
 * "interesting" grids are being used, and a directional key is used, we
 * only scroll by a single panel, in the direction requested, and check
 * for any interesting grids on that panel.  The "correct" solution would
 * actually involve scanning a larger set of grids, including ones in
 * panels which are adjacent to the one currently scanned, but this is
 * overkill for this function.  XXX XXX
 *
 * Hack -- targetting/observing an "outer border grid" may induce
 * problems, so this is not currently allowed.
 *
 * The player can use the direction keys to move among "interesting"
 * grids in a heuristic manner, or the "space", "+", and "-" keys to
 * move through the "interesting" grids in a sequential manner, or
 * can enter "location" mode, and use the direction keys to move one
 * grid at a time in any direction.  The "t" (set target) command will
 * only target a monster (as opposed to a location) if the monster is
 * target_able and the "interesting" mode is being used.
 *
 * The current grid is described using the "look" method above, and
 * a new command may be entered at any time, but note that if the
 * "TARGET_LOOK" bit flag is set (or if we are in "location" mode,
 * where "space" has no obvious meaning) then "space" will scan
 * through the description of the current grid until done, instead
 * of immediately jumping to the next "interesting" grid.  This
 * allows the "target" command to retain its old semantics.
 *
 * The "*", "+", and "-" keys may always be used to jump immediately
 * to the next (or previous) interesting grid, in the proper mode.
 *
 * The "return" key may always be used to scan through a complete
 * grid description (forever).
 *
 * This command will cancel any old target, even if used from
 * inside the "look" command.
 *
 *
 * 'mode' is one of TARGET_LOOK or TARGET_KILL.
 * 'x' and 'y' are the initial position of the target to be highlighted,
 * or -1 if no location is specified.
 * Returns TRUE if a target has been successfully set, FALSE otherwise.
 */
bool target_set_interactive(int mode, int x, int y)
{
	int py = player->py;
	int px = player->px;

	int path_n;
	struct loc path_g[256];

	int i, d, m, t, bd;
	int wid, hgt, help_prompt_loc;

	bool done = FALSE;
	bool flag = TRUE;
	bool help = FALSE;

	ui_event press;

	/* These are used for displaying the path to the target */
	wchar_t *path_char = mem_zalloc(z_info->max_range * sizeof(wchar_t));
	int *path_attr = mem_zalloc(z_info->max_range * sizeof(int));
	struct point_set *targets;

	/* If we haven't been given an initial location, start on the
	   player, otherwise  honour it by going into "free targetting" mode. */
	if (x == -1 || y == -1) {
		x = player->px;
		y = player->py;
	} else {
		flag = FALSE;
	}

	/* Cancel target */
	target_set_monster(0);

	/* Calculate the window location for the help prompt */
	Term_get_size(&wid, &hgt);
	help_prompt_loc = hgt - 1;
	
	/* Display the help prompt */
	prt("Press '?' for help.", help_prompt_loc, 0);

	/* Prepare the target set */
	targets = target_get_monsters(mode);

	/* Start near the player */
	m = 0;

	/* Interact */
	while (!done) {
		bool path_drawn = FALSE;
		
		/* Interesting grids if chosen and there are any, otherwise arbitrary */
		if (flag && point_set_size(targets)) {
			y = targets->pts[m].y;
			x = targets->pts[m].x;

			/* Adjust panel if needed */
			if (adjust_panel_help(y, x, help)) handle_stuff(player);
		
			/* Update help */
			if (help) {
				bool good_target = target_able(square_monster(cave, y, x));
				target_display_help(good_target,
									!(flag && point_set_size(targets)));
			}

			/* Find the path. */
			path_n = project_path(path_g, z_info->max_range, py, px, y, x,
								  PROJECT_THRU);

			/* Draw the path in "target" mode. If there is one */
			if (mode & (TARGET_KILL))
				path_drawn = draw_path(path_n, path_g, path_char, path_attr,
									   py, px);

			/* Describe and Prompt */
			press = target_set_interactive_aux(y, x, mode);

			/* Remove the path */
			if (path_drawn) load_path(path_n, path_g, path_char, path_attr);

			/* Assume no "direction" */
			d = 0;


			/* Analyze */
			if (press.type == EVT_MOUSE) {
				if (press.mouse.button == 3) {
					/* give the target selection command */
					press.mouse.button = 2;
					press.mouse.mods = KC_MOD_CONTROL;
				}
				if (press.mouse.button == 2) {
					y = KEY_GRID_Y(press);
					x = KEY_GRID_X(press);
					if (press.mouse.mods & KC_MOD_CONTROL) {
						/* same as keyboard target selection command below */
						struct monster *m = square_monster(cave, y, x);

						if (target_able(m)) {
							/* Set up target information */
							monster_race_track(player->upkeep, m->race);
							health_track(player->upkeep, m);
							target_set_monster(m);
							done = TRUE;
						} else {
							bell("Illegal target!");
						}
					} else if (press.mouse.mods & KC_MOD_ALT) {
						/* go to spot - same as 'g' command below */
						cmdq_push(CMD_PATHFIND);
						cmd_set_arg_point(cmdq_peek(), "point", y, x);
						done = TRUE;
					} else {
						/* cancel look mode */
						done = TRUE;
					}
				} else {
					y = KEY_GRID_Y(press);
					x = KEY_GRID_X(press);
					if (square_monster(cave, y, x) ||
						square_object(cave, y, x)) {
							/* reset the flag, to make sure we stay in this
							 * mode if something is actually there */
						flag = FALSE;
						/* scan the interesting list and see if there is
						 * anything here */
						for (i = 0; i < point_set_size(targets); i++) {
							if ((y == targets->pts[i].y) &&
								(x == targets->pts[i].x)) {
								m = i;
								flag = TRUE;
								break;
							}
						}
					} else {
						flag = FALSE;
					}
				}
			} else
				switch (press.key.code)
				{
					case ESCAPE:
					case 'q':
					{
						done = TRUE;
						break;
					}

					case ' ':
					case '*':
					case '+':
					{
						if (++m == point_set_size(targets))
							m = 0;

						break;
					}

					case '-':
					{
						if (m-- == 0)
							m = point_set_size(targets) - 1;

						break;
					}

					case 'p':
					{
						/* Recenter around player */
						verify_panel();

						/* Handle stuff */
						handle_stuff(player);

						y = player->py;
						x = player->px;
					}

					case 'o':
					{
						flag = FALSE;
						break;
					}

					case 'm':
					{
						break;
					}

					case 't':
					case '5':
					case '0':
					case '.':
					{
						struct monster *m = square_monster(cave, y, x);

						if (target_able(m)) {
							health_track(player->upkeep, m);
							target_set_monster(m);
							done = TRUE;
						} else {
							bell("Illegal target!");
						}
						break;
					}

					case 'g':
					{
						cmdq_push(CMD_PATHFIND);
						cmd_set_arg_point(cmdq_peek(), "point", y, x);
						done = TRUE;
						break;
					}
				
					case '?':
					{
						help = !help;
					
						/* Redraw main window */
						player->upkeep->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP);
						Term_clear();
						handle_stuff(player);
						if (!help)
							prt("Press '?' for help.", help_prompt_loc, 0);
					
						break;
					}

					default:
					{
						/* Extract direction */
						d = target_dir(press.key);

						/* Oops */
						if (!d) bell("Illegal command for target mode!");

						break;
					}
				}

			/* Hack -- move around */
			if (d) {
				int old_y = targets->pts[m].y;
				int old_x = targets->pts[m].x;

				/* Find a new monster */
				i = target_pick(old_y, old_x, ddy[d], ddx[d], targets);

				/* Scroll to find interesting grid */
				if (i < 0) {
					int old_wy = Term->offset_y;
					int old_wx = Term->offset_x;

					/* Change if legal */
					if (change_panel(d)) {
						/* Recalculate interesting grids */
						point_set_dispose(targets);
						targets = target_get_monsters(mode);

						/* Find a new monster */
						i = target_pick(old_y, old_x, ddy[d], ddx[d], targets);

						/* Restore panel if needed */
						if ((i < 0) && modify_panel(Term, old_wy, old_wx)) {
							/* Recalculate interesting grids */
							point_set_dispose(targets);
							targets = target_get_monsters(mode);
						}

						/* Handle stuff */
						handle_stuff(player);
					}
				}

				/* Use interesting grid if found */
				if (i >= 0) m = i;
			}
		} else {
			/* Update help */
			if (help) {
				bool good_target = target_able(square_monster(cave, y, x));
				target_display_help(good_target,
									!(flag && point_set_size(targets)));
			}

			/* Find the path. */
			path_n = project_path(path_g, z_info->max_range, py, px, y, x,
								  PROJECT_THRU);

			/* Draw the path in "target" mode. If there is one */
			if (mode & (TARGET_KILL))
				path_drawn = draw_path (path_n, path_g, path_char, path_attr,
										py, px);

			/* Describe and Prompt (enable "TARGET_LOOK") */
			press = target_set_interactive_aux(y, x, mode | TARGET_LOOK);

			/* Remove the path */
			if (path_drawn)  load_path(path_n, path_g, path_char, path_attr);

			/* Assume no direction */
			d = 0;

			/* Analyze the keypress */
			if (press.type == EVT_MOUSE) {
				if (press.mouse.button == 3) {
					/* give the target selection command */
					press.mouse.button = 2;
					press.mouse.mods = KC_MOD_CONTROL;
				}
				if (press.mouse.button == 2) {
					if (mode & (TARGET_KILL)) {
						if ((y == KEY_GRID_Y(press)) 
								&& (x == KEY_GRID_X(press))) {
							d = -1;
						}
					}
					y = KEY_GRID_Y(press);
					x = KEY_GRID_X(press);
					if (press.mouse.mods & KC_MOD_CONTROL) {
						/* same as keyboard target selection command below */
						target_set_location(y, x);
						done = TRUE;
					} else if (press.mouse.mods & KC_MOD_ALT) {
						/* go to spot - same as 'g' command below */
						cmdq_push(CMD_PATHFIND);
						cmd_set_arg_point(cmdq_peek(), "point", y, x);
						done = TRUE;
					} else {
						/* cancel look mode */
						done = TRUE;
						if (d == -1) {
							target_set_location(y, x);
							d = 0;
						}
					}
				} else {
					int dungeon_hgt = cave->height;
					int dungeon_wid = cave->width;

					y = KEY_GRID_Y(press);
					x = KEY_GRID_X(press);
				  
					if (press.mouse.y <= 1) {
						/* move the screen north */
						y--;
					} else if (press.mouse.y >= (Term->hgt - 2)) {
						/* move the screen south */
						y++;
					} else if (press.mouse.x <= COL_MAP) {
						/* move the screen in west */
						x--;
					} else if (press.mouse.x >= (Term->wid - 2)) {
						/* move the screen east */
						x++;
					}
          
					if (y < 0) y = 0;
					if (x < 0) x = 0;
					if (y >= dungeon_hgt-1) y = dungeon_hgt-1;
					if (x >= dungeon_wid-1) x = dungeon_wid-1;

					/* Adjust panel if needed */
					if (adjust_panel_help(y, x, help)) {
						/* Handle stuff */
						handle_stuff(player);

						/* Recalculate interesting grids */
						point_set_dispose(targets);
						targets = target_get_monsters(mode);
					}

					if (square_monster(cave, y, x) ||
						square_object(cave, y, x)) {
						/* scan the interesting list and see if there in
						 * anything here */
						for (i = 0; i < point_set_size(targets); i++) {
							if ((y == targets->pts[i].y) &&
								(x == targets->pts[i].x)) {
								m = i;
								flag = TRUE;
								break;
							}
						}
					} else {
						flag = FALSE;
					}
				}
			} else
				switch (press.key.code)
				{
					case ESCAPE:
					case 'q':
					{
						done = TRUE;
						break;
					}

					case ' ':
					case '*':
					case '+':
					case '-':
					{
						break;
					}

					case 'p':
					{
						/* Recenter around player */
						verify_panel();

						/* Handle stuff */
						handle_stuff(player);

						y = player->py;
						x = player->px;
					}

					case 'o':
					{
						break;
					}

					case 'm':
					{
						flag = TRUE;

						m = 0;
						bd = 999;

						/* Pick a nearby monster */
						for (i = 0; i < point_set_size(targets); i++) {
							t = distance(y, x, targets->pts[i].y,
										 targets->pts[i].x);

							/* Pick closest */
							if (t < bd) {
								m = i;
								bd = t;
							}
						}

						/* Nothing interesting */
						if (bd == 999) flag = FALSE;

						break;
					}

					case 't':
					case '5':
					case '0':
					case '.':
					{
						target_set_location(y, x);
						done = TRUE;
						break;
					}

					case 'g':
					{
						cmdq_push(CMD_PATHFIND);
						cmd_set_arg_point(cmdq_peek(), "point", y, x);
						done = TRUE;
						break;
					}

					case '?':
					{
						help = !help;

						/* Redraw main window */
						player->upkeep->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP);
						Term_clear();
						handle_stuff(player);
						if (!help)
							prt("Press '?' for help.", help_prompt_loc, 0);
					
						break;
					}

					default:
					{
						/* Extract a direction */
						d = target_dir(press.key);

						/* Oops */
						if (!d) bell("Illegal command for target mode!");

						break;
					}
				}

			/* Handle "direction" */
			if (d) {
				int dungeon_hgt = cave->height;
				int dungeon_wid = cave->width;

				/* Move */
				x += ddx[d];
				y += ddy[d];

				/* Slide into legality */
				if (x >= dungeon_wid - 1) x--;
				else if (x <= 0) x++;

				/* Slide into legality */
				if (y >= dungeon_hgt - 1) y--;
				else if (y <= 0) y++;

				/* Adjust panel if needed */
				if (adjust_panel_help(y, x, help)) {
					/* Handle stuff */
					handle_stuff(player);

					/* Recalculate interesting grids */
					point_set_dispose(targets);
					targets = target_get_monsters(mode);
				}
			}
		}
	}

	/* Forget */
	point_set_dispose(targets);

	/* Redraw as necessary */
	if (help) {
		player->upkeep->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP);
		Term_clear();
	} else {
		prt("", 0, 0);
		prt("", help_prompt_loc, 0);
		player->upkeep->redraw |= (PR_DEPTH | PR_STATUS);
	}

	/* Recenter around player */
	verify_panel();

	/* Handle stuff */
	handle_stuff(player);

	mem_free(path_attr);
	mem_free(path_char);

	/* Failure to set target */
	if (!target_is_set()) return (FALSE);

	/* Success */
	return (TRUE);
}
Esempio n. 25
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;
}
Esempio n. 26
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);
}
Esempio n. 27
0
/**
 * Attempts to place a monster of the given race at the given location.
 *
 * If `sleep` is true, the monster is placed with its default sleep value,
 * which is given in monster.txt.
 *
 * `origin` is the item origin to use for any monster drops (e.g. ORIGIN_DROP,
 * ORIGIN_DROP_PIT, etc.)
 *
 * To give the player a sporting chance, some especially dangerous
 * monsters are marked as "FORCE_SLEEP" in monster.txt, which will
 * cause them to be placed with low energy. This helps ensure that
 * if such a monster suddenly appears in line-of-sight (due to a
 * summon, for instance), the player gets a chance to move before
 * they do.
 *
 * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters.
 *
 * This is the only function which may place a monster in the dungeon,
 * except for the savefile loading code, which calls place_monster()
 * directly.
 */
static bool place_new_monster_one(struct chunk *c, int y, int x,
								  struct monster_race *race, bool sleep,
								  byte origin)
{
	int i;

	struct monster *mon;
	struct monster monster_body;

	assert(square_in_bounds(c, y, x));
	assert(race && race->name);

	/* Not where monsters already are */
	if (square_monster(c, y, x))
		return false;

	/* Not where the player already is */
	if ((player->py == y) && (player->px == x))
		return false;

	/* Prevent monsters from being placed where they cannot walk, but allow other feature types */
	if (!square_is_monster_walkable(c, y, x))
		return false;

	/* No creation on glyph of warding */
	if (square_iswarded(c, y, x)) return false;

	/* "unique" monsters must be "unique" */
	if (rf_has(race->flags, RF_UNIQUE) && race->cur_num >= race->max_num)
		return (false);

	/* Depth monsters may NOT be created out of depth */
	if (rf_has(race->flags, RF_FORCE_DEPTH) && player->depth < race->level)
		return (false);

	/* Add to level feeling, note uniques for cheaters */
	c->mon_rating += race->level * race->level;

	/* Check out-of-depth-ness */
	if (race->level > c->depth) {
		if (rf_has(race->flags, RF_UNIQUE)) { /* OOD unique */
			if (OPT(player, cheat_hear))
				msg("Deep unique (%s).", race->name);
		} else { /* Normal monsters but OOD */
			if (OPT(player, cheat_hear))
				msg("Deep monster (%s).", race->name);
		}
		/* Boost rating by power per 10 levels OOD */
		c->mon_rating += (race->level - c->depth) * race->level * race->level;
	} else if (rf_has(race->flags, RF_UNIQUE) && OPT(player, cheat_hear))
		msg("Unique (%s).", race->name);

	/* Get local monster */
	mon = &monster_body;

	/* Clean out the monster */
	memset(mon, 0, sizeof(struct monster));

	/* Save the race */
	mon->race = race;

	/* Enforce sleeping if needed */
	if (sleep && race->sleep) {
		int val = race->sleep;
		mon->m_timed[MON_TMD_SLEEP] = ((val * 2) + randint1(val * 10));
	}

	/* Uniques get a fixed amount of HP */
	if (rf_has(race->flags, RF_UNIQUE))
		mon->maxhp = race->avg_hp;
	else {
		mon->maxhp = mon_hp(race, RANDOMISE);
		mon->maxhp = MAX(mon->maxhp, 1);
	}

	/* And start out fully healthy */
	mon->hp = mon->maxhp;

	/* Extract the monster base speed */
	mon->mspeed = race->speed;

	/* Hack -- small racial variety */
	if (!rf_has(race->flags, RF_UNIQUE)) {
		/* Allow some small variation per monster */
		i = turn_energy(race->speed) / 10;
		if (i) mon->mspeed += rand_spread(0, i);
	}

	/* Give a random starting energy */
	mon->energy = (byte)randint0(50);

	/* Force monster to wait for player */
	if (rf_has(race->flags, RF_FORCE_SLEEP))
		mflag_on(mon->mflag, MFLAG_NICE);

	/* Radiate light? */
	if (rf_has(race->flags, RF_HAS_LIGHT))
		player->upkeep->update |= PU_UPDATE_VIEW;

	/* Is this obviously a monster? (Mimics etc. aren't) */
	if (rf_has(race->flags, RF_UNAWARE))
		mflag_on(mon->mflag, MFLAG_CAMOUFLAGE);
	else
		mflag_off(mon->mflag, MFLAG_CAMOUFLAGE);

	/* Set the color if necessary */
	if (rf_has(race->flags, RF_ATTR_RAND))
		mon->attr = randint1(BASIC_COLORS - 1);

	/* Place the monster in the dungeon */
	if (!place_monster(c, y, x, mon, origin))
		return (false);

	/* Success */
	return (true);
}
Esempio n. 28
0
/**
 * Update the current "run" path
 *
 * Return TRUE if the running should be stopped
 */
static bool run_test(void)
{
	int py = player->py;
	int px = player->px;

	int prev_dir;
	int new_dir;

	int row, col;
	int i, max, inv;
	int option, option2;


	/* No options yet */
	option = 0;
	option2 = 0;

	/* Where we came from */
	prev_dir = run_old_dir;


	/* Range of newly adjacent grids */
	max = (prev_dir & 0x01) + 1;


	/* Look at every newly adjacent square. */
	for (i = -max; i <= max; i++) {
		struct object *obj;

		/* New direction */
		new_dir = cycle[chome[prev_dir] + i];

		/* New location */
		row = py + ddy[new_dir];
		col = px + ddx[new_dir];


		/* Visible monsters abort running */
		if (cave->squares[row][col].mon > 0) {
			monster_type *m_ptr = square_monster(cave, row, col);

			/* Visible monster */
			if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) return (TRUE);
		}

		/* Visible objects abort running */
		for (obj = square_object(cave, row, col); obj; obj = obj->next)
			/* Visible object */
			if (obj->marked && !ignore_item_ok(obj)) return (TRUE);

		/* Assume unknown */
		inv = TRUE;

		/* Check memorized grids */
		if (square_ismark(cave, row, col)) {
			bool notice = square_noticeable(cave, row, col);

			/* Interesting feature */
			if (notice) return (TRUE);

			/* The grid is "visible" */
			inv = FALSE;
		}

		/* Analyze unknown grids and floors */
		if (inv || square_ispassable(cave, row, col)) {
			/* Looking for open area */
			if (run_open_area) {
				/* Nothing */
			} else if (!option) {
				/* The first new direction. */
				option = new_dir;
			} else if (option2) {
				/* Three new directions. Stop running. */
				return (TRUE);
			} else if (option != cycle[chome[prev_dir] + i - 1]) {
				/* Two non-adjacent new directions.  Stop running. */
				return (TRUE);
			} else if (new_dir & 0x01) {
				/* Two new (adjacent) directions (case 1) */
				option2 = new_dir;
			} else {
				/* Two new (adjacent) directions (case 2) */
				option2 = option;
				option = new_dir;
			}
		} else { /* Obstacle, while looking for open area */
			if (run_open_area) {
				if (i < 0) {
					/* Break to the right */
					run_break_right = TRUE;
				} else if (i > 0) {
					/* Break to the left */
					run_break_left = TRUE;
				}
			}
		}
	}


	/* Look at every soon to be newly adjacent square. */
	for (i = -max; i <= max; i++) {		
		/* New direction */
		new_dir = cycle[chome[prev_dir] + i];
		
		/* New location */
		row = py + ddy[prev_dir] + ddy[new_dir];
		col = px + ddx[prev_dir] + ddx[new_dir];
		
		/* HACK: Ugh. Sometimes we come up with illegal bounds. This will
		 * treat the symptom but not the disease. */
		if (row >= cave->height || col >= cave->width) continue;
		if (row < 0 || col < 0) continue;

		/* Visible monsters abort running */
		if (cave->squares[row][col].mon > 0) {
			monster_type *m_ptr = square_monster(cave, row, col);
			
			/* Visible monster */
			if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && !is_mimicking(m_ptr))
				return (TRUE);
		}
	}

	/* Looking for open area */
	if (run_open_area) {
		/* Hack -- look again */
		for (i = -max; i < 0; i++) {
			new_dir = cycle[chome[prev_dir] + i];

			row = py + ddy[new_dir];
			col = px + ddx[new_dir];

			/* Unknown grid or non-wall */
			/* Was: square_ispassable(cave, row, col) */
			if (!square_ismark(cave, row, col) ||
			    (square_ispassable(cave, row, col))) {
				/* Looking to break right */
				if (run_break_right) {
					return (TRUE);
				}
			} else { /* Obstacle */
				/* Looking to break left */
				if (run_break_left) {
					return (TRUE);
				}
			}
		}

		/* Hack -- look again */
		for (i = max; i > 0; i--) {
			new_dir = cycle[chome[prev_dir] + i];

			row = py + ddy[new_dir];
			col = px + ddx[new_dir];

			/* Unknown grid or non-wall */
			/* Was: square_ispassable(cave, row, col) */
			if (!square_ismark(cave, row, col) ||
			    (square_ispassable(cave, row, col))) {
				/* Looking to break left */
				if (run_break_left) {
					return (TRUE);
				}
			} else { /* Obstacle */
				/* Looking to break right */
				if (run_break_right) {
					return (TRUE);
				}
			}
		}
	} else { /* Not looking for open area */
		/* No options */
		if (!option) {
			return (TRUE);
		} else if (!option2) { /* One option */
			/* Primary option */
			run_cur_dir = option;

			/* No other options */
			run_old_dir = option;
		} else { /* Two options, examining corners */
			/* Primary option */
			run_cur_dir = option;

			/* Hack -- allow curving */
			run_old_dir = option2;
		}
	}


	/* About to hit a known wall, stop */
	if (see_wall(run_cur_dir, py, px))
		return (TRUE);


	/* Failure */
	return (FALSE);
}
Esempio n. 29
0
/**
 * Open a closed/locked/jammed door or a closed/locked chest.
 *
 * Unlocking a locked chest is worth one experience point; since doors are
 * player lockable, there is no experience for unlocking doors.
 */
void do_cmd_open(struct command *cmd)
{
	int y, x, dir;
	struct object *obj;
	bool more = false;
	int err;
	struct monster *m;

	/* Get arguments */
	err = cmd_get_arg_direction(cmd, "direction", &dir);
	if (err || dir == DIR_UNKNOWN) {
		int y2, x2;
		int n_closed_doors, n_locked_chests;

		n_closed_doors = count_feats(&y2, &x2, square_iscloseddoor, false);
		n_locked_chests = count_chests(&y2, &x2, CHEST_OPENABLE);

		if (n_closed_doors + n_locked_chests == 1) {
			dir = coords_to_dir(y2, x2);
			cmd_set_arg_direction(cmd, "direction", dir);
		} else if (cmd_get_direction(cmd, "direction", &dir, false)) {
			return;
		}
	}

	/* Get location */
	y = player->py + ddy[dir];
	x = player->px + ddx[dir];

	/* Check for chest */
	obj = chest_check(y, x, CHEST_OPENABLE);

	/* Check for door */
	if (!obj && !do_cmd_open_test(y, x)) {
		/* Cancel repeat */
		disturb(player, 0);
		return;
	}

	/* Take a turn */
	player->upkeep->energy_use = z_info->move_energy;

	/* Apply confusion */
	if (player_confuse_dir(player, &dir, false)) {
		/* Get location */
		y = player->py + ddy[dir];
		x = player->px + ddx[dir];

		/* Check for chest */
		obj = chest_check(y, x, CHEST_OPENABLE);
	}

	/* Monster */
	m = square_monster(cave, y, x);
	if (m) {
		/* Mimics surprise the player */
		if (is_mimicking(m)) {
			become_aware(m);

			/* Mimic wakes up */
			mon_clear_timed(m, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false);
		} else {
			/* Message */
			msg("There is a monster in the way!");

			/* Attack */
			py_attack(y, x);
		}
	} else if (obj) {
		/* Chest */
		more = do_cmd_open_chest(y, x, obj);
	} else {
		/* Door */
		more = do_cmd_open_aux(y, x);
	}

	/* Cancel repeat unless we may continue */
	if (!more) disturb(player, 0);
}
Esempio n. 30
0
/**
 * Attempts to place a copy of the given monster at the given position in
 * the dungeon.
 *
 * All of the monster placement routines eventually call this function. This
 * is what actually puts the monster in the dungeon (i.e., it notifies the cave
 * and sets the monsters position). The dungeon loading code also calls this
 * function directly.
 *
 * `origin` is the item origin to use for any monster drops (e.g. ORIGIN_DROP,
 * ORIGIN_DROP_PIT, etc.) The dungeon loading code calls this with origin = 0,
 * which prevents the monster's drops from being generated again.
 *
 * Returns the m_idx of the newly copied monster, or 0 if the placement fails.
 */
s16b place_monster(struct chunk *c, int y, int x, struct monster *mon,
				   byte origin)
{
	s16b m_idx;
	struct monster *new_mon;

	assert(square_in_bounds(c, y, x));
	assert(!square_monster(c, y, x));

	/* Get a new record */
	m_idx = mon_pop(c);
	if (!m_idx) return 0;

	/* Copy the monster */
	new_mon = cave_monster(c, m_idx);
	memcpy(new_mon, mon, sizeof(struct monster));

	/* Set the ID */
	new_mon->midx = m_idx;

	/* Set the location */
	c->squares[y][x].mon = new_mon->midx;
	new_mon->fy = y;
	new_mon->fx = x;
	assert(square_monster(c, y, x) == new_mon);

	update_mon(new_mon, c, true);

	/* Hack -- Count the number of "reproducers" */
	if (rf_has(new_mon->race->flags, RF_MULTIPLY)) num_repro++;

	/* Count racial occurrences */
	new_mon->race->cur_num++;

	/* Create the monster's drop, if any */
	if (origin)
		(void)mon_create_drop(c, new_mon, origin);

	/* Make mimics start mimicking */
	if (origin && new_mon->race->mimic_kinds) {
		struct object *obj;
		struct object_kind *kind = new_mon->race->mimic_kinds->kind;
		struct monster_mimic *mimic_kind;
		int i = 1;

		/* Pick a random object kind to mimic */
		for (mimic_kind = new_mon->race->mimic_kinds; mimic_kind; 
				mimic_kind = mimic_kind->next, i++) {
			if (one_in_(i)) kind = mimic_kind->kind;
		}

		if (tval_is_money_k(kind)) {
			obj = make_gold(player->depth, kind->name);
		} else {
			obj = object_new();
			object_prep(obj, kind, new_mon->race->level, RANDOMISE);
			apply_magic(obj, new_mon->race->level, true, false, false, false);
			obj->number = 1;
			obj->origin = ORIGIN_DROP_MIMIC;
			obj->origin_depth = player->depth;
		}

		obj->mimicking_m_idx = m_idx;
		new_mon->mimicked_obj = obj;

		/* Put the object on the floor if it goes, otherwise no mimicry */
		if (floor_carry(c, y, x, obj, false)) {
			list_object(c, obj);
		} else {
			/* Clear the mimicry */
			obj->mimicking_m_idx = 0;
			new_mon->mimicked_obj = NULL;

			/* Give the object to the monster if appropriate */
			if (rf_has(new_mon->race->flags, RF_MIMIC_INV)) {
				monster_carry(c, new_mon, obj);
			} else {
				/* Otherwise delete the mimicked object */
				object_delete(&obj);
			}
		}
	}

	/* Result */
	return m_idx;
}