Exemplo n.º 1
0
static void project_feature_handler_MISSILE(project_feature_handler_context_t *context)
{
	/* Grid is in line of sight and player is not blind */
	if (square_isview(cave, context->y, context->x) &&
		!player->timed[TMD_BLIND]) {
		/* Observe */
		context->obvious = true;
	}
}
Exemplo n.º 2
0
/* Darken the grid */
static void project_feature_handler_DARK_WEAK(project_feature_handler_context_t *context)
{
	const int x = context->x;
	const int y = context->y;

	if (player->depth != 0 || !is_daytime())
		/* Turn off the light */
		sqinfo_off(cave->squares[y][x].info, SQUARE_GLOW);

	/* Grid is in line of sight */
	if (square_isview(cave, y, x)) {
		/* Observe */
		context->obvious = true;

		/* Fully update the visuals */
		player->upkeep->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS);
	}
}
Exemplo n.º 3
0
/* Light up the grid */
static void project_feature_handler_LIGHT_WEAK(project_feature_handler_context_t *context)
{
	const int x = context->x;
	const int y = context->y;

	/* Turn on the light */
	sqinfo_on(cave->squares[y][x].info, SQUARE_GLOW);

	/* Grid is in line of sight */
	if (square_isview(cave, y, x)) {
		if (!player->timed[TMD_BLIND]) {
			/* Observe */
			context->obvious = true;
		}

		/* Fully update the visuals */
		player->upkeep->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS);
	}
}
Exemplo n.º 4
0
/**
 * Determine whether a monster is active or passive
 */
static bool monster_check_active(struct chunk *c, struct monster *mon)
{
	/* Character is inside scanning range */
	if (mon->cdis <= mon->race->aaf)
		mflag_on(mon->mflag, MFLAG_ACTIVE);

	/* Monster is hurt */
	else if (mon->hp < mon->maxhp)
		mflag_on(mon->mflag, MFLAG_ACTIVE);

	/* Monster can "see" the player (checked backwards) */
	else if (square_isview(c, mon->fy, mon->fx))
		mflag_on(mon->mflag, MFLAG_ACTIVE);

	/* Monster can "smell" the player from far away (flow) */
	else if (monster_can_flow(c, mon))
		mflag_on(mon->mflag, MFLAG_ACTIVE);

	/* Otherwise go passive */
	else
		mflag_off(mon->mflag, MFLAG_ACTIVE);

	return mflag_has(mon->mflag, MFLAG_ACTIVE) ? true : false;
}
Exemplo n.º 5
0
/**
 * Determine whether a monster is active or passive
 */
static bool monster_check_active(struct chunk *c, struct monster *mon)
{
	if ((mon->cdis <= mon->race->hearing) && monster_passes_walls(mon)) {
		/* Character is inside scanning range, monster can go straight there */
		mflag_on(mon->mflag, MFLAG_ACTIVE);
	} else if (mon->hp < mon->maxhp) {
		/* Monster is hurt */
		mflag_on(mon->mflag, MFLAG_ACTIVE);
	} else if (square_isview(c, mon->fy, mon->fx)) {
		/* Monster can "see" the player (checked backwards) */
		mflag_on(mon->mflag, MFLAG_ACTIVE);
	} else if (monster_can_hear(c, mon)) {
		/* Monster can hear the player */
		mflag_on(mon->mflag, MFLAG_ACTIVE);
	} else if (monster_can_smell(c, mon)) {
		/* Monster can smell the player */
		mflag_on(mon->mflag, MFLAG_ACTIVE);
	} else {
		/* Otherwise go passive */
		mflag_off(mon->mflag, MFLAG_ACTIVE);
	}

	return mflag_has(mon->mflag, MFLAG_ACTIVE) ? true : false;
}
Exemplo n.º 6
0
/**
 * Choose a good hiding place near a monster for it to run toward.
 *
 * Pack monsters will use this to "ambush" the player and lure him out
 * of corridors into open space so they can swarm him.
 *
 * Return true if a good location is available.
 */
static bool find_hiding(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 = 999, min;

	const int *y_offsets, *x_offsets;

	/* Closest distance to get */
	min = distance(py, px, fy, fx) * 3 / 4 + 2;

	/* 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 occupied locations */
			if (!square_isempty(c, y, x)) continue;

			/* Check for hidden, available grid */
			if (!square_isview(c, y, x) &&
				projectable(c, fy, fx, y, x, PROJECT_STOP)) {
				/* Calculate distance from player */
				dis = distance(y, x, py, px);

				/* Remember if closer than previous */
				if (dis < gdis && dis >= min) {
					gy = y;
					gx = x;
					gdis = dis;
				}
			}
		}

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

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

	/* No good place */
	return (false);
}
Exemplo n.º 7
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);
}
Exemplo n.º 8
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;
}
Exemplo n.º 9
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;
	}
}
Exemplo n.º 10
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;
}
Exemplo n.º 11
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 monster_turn_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);

	/* Dangerous terrain in the way */
	if (monster_hates_grid(c, mon, ny, nx)) {
		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 (monster_is_visible(mon)) {
		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);

		return true;
	} else if (square_iscloseddoor(c, ny, nx) ||
			   square_issecretdoor(c, ny, nx)) {
		bool can_open = rf_has(mon->race->flags, RF_OPEN_DOOR);
		bool can_bash = rf_has(mon->race->flags, RF_BASH_DOOR);
		bool will_bash = false;

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

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

		/* If creature can open or bash doors, make a choice */
		if (can_open) {
			/* Sometimes bash anyway (impatient) */
			if (can_bash) {
				will_bash = one_in_(2) ? true : false;
			}
		} else if (can_bash) {
			/* Only choice */
			will_bash = true;
		} else {
			/* Door is an insurmountable obstacle */
			return false;
		}

		/* Now outcome depends on type of door */
		if (square_islockeddoor(c, ny, nx)) {
			/* Locked door -- test monster strength against door strength */
			int k = square_door_power(c, ny, nx);
			if (randint0(mon->hp / 10) > k) {
				if (will_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 {
			/* Closed or secret door -- always open or bash */
			if (square_isview(c, ny, nx))
				player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS);

			if (will_bash) {
				square_smash_door(c, ny, nx);

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

				/* Fall into doorway */
				return true;
			} else {
				square_open_door(c, ny, nx);
			}
		}
	}

	return false;
}
Exemplo n.º 12
0
/**
 * Choose "logical" directions for monster movement
 */
static bool get_move(struct chunk *c, struct monster *mon, int *dir, bool *good)
{
	struct loc decoy = cave_find_decoy(c);
	struct loc target = (decoy.y && decoy.x) ? decoy :
		loc(player->px, player->py);

	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 */
	get_move_find_range(mon);

	/* Assume we're heading towards the player */
	if (get_move_advance(c, mon)) {
		/* Extract the "pseudo-direction" */
		y = mon->target.grid.y - mon->fy;
		x = mon->target.grid.x - mon->fx;
		*good = true;
	} else {
		/* Head blindly straight for the "player" if there's no better idea */
		y = target.y - mon->fy;
		x = target.x - 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 = target.y + ddy_ddd[i];
			int rx = target.x + 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 < 5) && (player->chp > player->mhp / 2)) {
			/* Find hiding place */
			if (get_move_find_hiding(c, mon)) {
				done = true;
				y = mon->target.grid.y - mon->fy;
				x = mon->target.grid.x - mon->fx;
			}
		}
	}

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

		done = true;
	}

	/* Monster groups try to surround the player */
	if (!done && rf_has(mon->race->flags, RF_GROUP_AI) &&
		square_isview(c, mon->fy, mon->fx)) {
		int i, yy = mon->target.grid.y, xx = mon->target.grid.x;

		/* 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 = target.y + ddy_ddd[(tmp + i) & 7];
				xx = target.x + ddx_ddd[(tmp + i) & 7];

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

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

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

	/* Check if the monster has already reached its target */
	if (!x && !y) return (false);

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

	/* Want to move */
	return (true);
}
Exemplo n.º 13
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 get_move_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 too-distant grids */
			if (c->noise.grids[y][x] > c->noise.grids[fy][fx] + 2 * d)
				continue;

			/* Ignore damaging terrain if they can't handle it */
			if (square_isdamaging(c, y, x) &&
				!rf_has(mon->race->flags, square_feat(c, y, x)->resist_flag))
				continue;

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

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

		/* Check for success */
		if (gdis > 0) {
			/* Good location */
			mon->target.grid = loc(gx, gy);
			return (true);
		}
	}

	/* No safe place */
	return (false);
}
Exemplo n.º 14
0
/**
 * Choose the best direction to advance toward the player, using sound or scent.
 *
 * Note that ghosts and rock-eaters generally just head straight for the player.
 *
 * Monsters first try to use current sound information as saved in
 * c->noise.grids[y][x].  Failing that, they'll try using scent, saved in 
 * c->scent.grids[y][x].
 *
 * Note that this function assumes the monster is moving to an adjacent grid,
 * and so the noise can be louder by at most 1.
 *
 * 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_move_advance(struct chunk *c, struct monster *mon)
{
	int i;
	struct loc decoy = cave_find_decoy(c);
	struct loc target = (decoy.y && decoy.x) ? decoy :
		loc(player->px, player->py);
	int best_scent = 0;
	bool found_dir = false;
	int my = mon->fy, mx = mon->fx;
	int base_hearing = mon->race->hearing
		- player->state.skills[SKILL_STEALTH] / 3;
	int current_noise = base_hearing - c->noise.grids[my][mx];
	int best_dir = 8;
	int backup_dir = -1;

	/* If the monster can pass through nearby walls, do that */
	if (monster_passes_walls(mon) && !monster_near_permwall(mon, c)) {
		mon->target.grid = target;
		return true;
	}

	/* If the player can see monster, set target and run towards them */
	if (square_isview(c, my, mx)) {
		mon->target.grid = target;
		return true;
	}

	/* Check nearby sound, giving preference to the cardinal directions */
	for (i = 0; i < 8; i++) {
		/* Get the location */
		int y = my + ddy_ddd[i];
		int x = mx + ddx_ddd[i];
		int heard_noise = base_hearing - c->noise.grids[y][x];

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

		/* Must be some noise */
		if (c->noise.grids[y][x] == 0) {
			continue;
		}

		/* There's a monster blocking that we can't deal with */
		if (!monster_can_kill(c, mon, y, x) && !monster_can_move(c, mon, y, x)){
			continue;
		}

		/* There's damaging terrain */
		if (monster_hates_grid(c, mon, y, x)) {
			continue;
		}

		/* If it's better than the current noise, choose this direction */
		if (heard_noise > current_noise) {
			best_dir = i;
			found_dir = true;
			break;
		} else if (heard_noise == current_noise) {
			/* Possible move if we can't actually get closer */
			backup_dir = i;
			continue;
		}
	}

	/* If no good sound, use scent */
	if (!found_dir) {
		for (i = 0; i < 8; i++) {
			/* Get the location */
			int y = my + ddy_ddd[i];
			int x = mx + ddx_ddd[i];
			int smelled_scent;

			/* If no good sound yet, use scent */
			smelled_scent = mon->race->smell - c->scent.grids[y][x];
			if ((smelled_scent > best_scent) && (c->scent.grids[y][x] != 0)) {
				best_scent = smelled_scent;
				best_dir = i;
				found_dir = true;
			}
		}
	}

	/* Set the target */
	if (found_dir) {
		mon->target.grid = loc(mx + ddx_ddd[best_dir], my + ddy_ddd[best_dir]);
		return true;
	} else if (backup_dir >= 0) {
		/* Move around to try and improve position */
		mon->target.grid = loc(mx + ddx_ddd[backup_dir],
							   my + ddy_ddd[backup_dir]);
		return true;
	}

	/* No reason to advance */
	return false;
}
Exemplo n.º 15
0
/**
 * Process a monster's turn
 *
 * 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 monster_turn(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;
	bool tracking = 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 (monster_turn_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 (!monster_turn_should_stagger(mon)) {
		if (!get_move(c, mon, &dir, &tracking)) 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];

		/* Tracking monsters have their best direction, don't change */
		if ((i > 0) && !stagger && !square_isview(c, oy, ox) && tracking) {
			break;
		}

		/* Check if we can move */
		if (!monster_turn_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) &&
			!monster_turn_glyph(c, mon, nx, ny))
			continue;

		/* Break a decoy if there is one */
		if (square_isdecoyed(c, ny, nx)) {
			/* Learn about if the monster attacks */
			if (monster_is_visible(mon))
				rf_on(lore->flags, RF_NEVER_BLOW);

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

			/* Wait a minute... */
			square_destroy_decoy(c, ny, nx);
			did_something = true;
			break;
		}

		/* The player is in the way. */
		if (square_isplayer(c, ny, nx)) {
			/* Learn about if the monster attacks */
			if (monster_is_visible(mon))
				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 (monster_is_visible(mon))
					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 = monster_turn_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);
			monster_turn_grab_objects(c, mon, m_name, nx, ny);
		}
	}

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

		/* Possible disturb */
		if (monster_is_visible(mon) && monster_is_in_view(mon) && 
			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 && monster_is_camouflaged(mon))
		become_aware(mon);
}