예제 #1
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;
}
예제 #2
0
/**
 * Called from project() to affect objects
 *
 * Called for projections with the PROJECT_ITEM flag set, which includes
 * beam, ball and breath effects.
 *
 * \param who is the monster list index of the caster
 * \param r is the distance from the centre of the effect
 * \param y the coordinates of the grid being handled
 * \param x the coordinates of the grid being handled
 * \param dam is the "damage" from the effect at distance r from the centre
 * \param typ is the projection (GF_) type
 * \param protected_obj is an object that should not be affected by the 
 *        projection, typically the object that created it
 * \return whether the effects were obvious
 *
 * Note that this function determines if the player can see anything that
 * happens by taking into account: blindness, line-of-sight, and illumination.
 *
 * Hack -- effects on objects which are memorized but not in view are also seen.
 */
bool project_o(int who, int r, int y, int x, int dam, int typ,
			   const struct object *protected_obj)
{
	struct object *obj = square_object(cave, y, x);
	bool obvious = false;

	/* Scan all objects in the grid */
	while (obj) {
		bool ignore = false;
		bool do_kill = false;
		const char *note_kill = NULL;
		struct object *next = obj->next;
		project_object_handler_context_t context = {
			who,
			r,
			y,
			x,
			dam,
			typ,
			obj,
			obvious,
			do_kill,
			ignore,
			note_kill,
		};
		project_object_handler_f object_handler = object_handlers[typ];

		if (object_handler != NULL)
			object_handler(&context);

		obvious = context.obvious;
		do_kill = context.do_kill && (obj != protected_obj);
		ignore = context.ignore;
		note_kill = context.note_kill;

		/* Attempt to destroy the object */
		if (do_kill) {
			char o_name[80];

			/* Effect observed */
			if (obj->known && !ignore_item_ok(obj) &&
				square_isseen(cave, y, x)) {
				obvious = true;
				object_desc(o_name, sizeof(o_name), obj, ODESC_BASE);
			}

			/* Artifacts, and other objects, get to resist */
			if (obj->artifact || ignore) {
				/* Observe the resist */
				if (obvious && obj->known && !ignore_item_ok(obj))
					msg("The %s %s unaffected!", o_name,
						VERB_AGREEMENT(obj->number, "is", "are"));
			} else if (obj->mimicking_m_idx) {
				/* Reveal mimics */
				if (obvious)
					become_aware(cave_monster(cave, obj->mimicking_m_idx));
			} else {
				/* Describe if needed */
				if (obvious && obj->known && note_kill && !ignore_item_ok(obj))
					msgt(MSG_DESTROY, "The %s %s!", o_name, note_kill);

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

				/* Redraw */
				square_note_spot(cave, y, x);
				square_light_spot(cave, y, x);
			}
		}

		/* Next object */
		obj = next;
	}

	/* Return "Anything seen?" */
	return (obvious);
}
예제 #3
0
/**
 * Move player in the given direction.
 *
 * This routine should only be called when energy has been expended.
 *
 * Note that this routine handles monsters in the destination grid,
 * and also handles attempting to move into walls/doors/rubble/etc.
 */
void move_player(int dir, bool disarm)
{
	int y = player->py + ddy[dir];
	int x = player->px + ddx[dir];

	int m_idx = cave->squares[y][x].mon;
	struct monster *mon = cave_monster(cave, m_idx);
	bool alterable = (square_isknowntrap(cave, y, x) ||
					  square_iscloseddoor(cave, y, x));

	/* Attack monsters, alter traps/doors on movement, hit obstacles or move */
	if (m_idx > 0) {
		/* Mimics surprise the player */
		if (is_mimicking(mon)) {
			become_aware(mon);

			/* Mimic wakes up */
			mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false);

		} else {
			py_attack(y, x);
		}
	} else if (disarm && square_isknown(cave, y, x) && alterable) {
		/* Auto-repeat if not already repeating */
		if (cmd_get_nrepeats() == 0)
			cmd_set_repeat(99);

		do_cmd_alter_aux(dir);
	} else if (player->upkeep->running && square_isknowntrap(cave, y, x)) {
		/* Stop running before known traps */
		disturb(player, 0);
	} else if (!square_ispassable(cave, y, x)) {
		disturb(player, 0);

		/* Notice unknown obstacles, mention known obstacles */
		if (!square_isknown(cave, y, x)) {
			if (square_isrubble(cave, y, x)) {
				msgt(MSG_HITWALL,
					 "You feel a pile of rubble blocking your way.");
				square_memorize(cave, y, x);
				square_light_spot(cave, y, x);
			} else if (square_iscloseddoor(cave, y, x)) {
				msgt(MSG_HITWALL, "You feel a door blocking your way.");
				square_memorize(cave, y, x);
				square_light_spot(cave, y, x);
			} else {
				msgt(MSG_HITWALL, "You feel a wall blocking your way.");
				square_memorize(cave, y, x);
				square_light_spot(cave, y, x);
			}
		} else {
			if (square_isrubble(cave, y, x))
				msgt(MSG_HITWALL,
					 "There is a pile of rubble blocking your way.");
			else if (square_iscloseddoor(cave, y, x))
				msgt(MSG_HITWALL, "There is a door blocking your way.");
			else
				msgt(MSG_HITWALL, "There is a wall blocking your way.");
		}
	} else {
		/* Move player */
		monster_swap(player->py, player->px, y, x);

		/* Handle store doors, or notice objects */
		if (square_isshop(cave, y, x)) {
			disturb(player, 0);
			event_signal(EVENT_ENTER_STORE);
			event_remove_handler_type(EVENT_ENTER_STORE);
			event_signal(EVENT_USE_STORE);
			event_remove_handler_type(EVENT_USE_STORE);
			event_signal(EVENT_LEAVE_STORE);
			event_remove_handler_type(EVENT_LEAVE_STORE);
		} else {
			square_know_pile(cave, y, x);
			cmdq_push(CMD_AUTOPICKUP);
		}

		/* Discover invisible traps, set off visible ones */
		if (square_issecrettrap(cave, y, x)) {
			disturb(player, 0);
			hit_trap(y, x);
		} else if (square_isknowntrap(cave, y, x)) {
			disturb(player, 0);
			hit_trap(y, x);
		}

		/* Update view and search */
		update_view(cave, player);
		search();
	}

	player->upkeep->running_firststep = false;
}
예제 #4
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);
}
예제 #5
0
파일: cmd1.c 프로젝트: tonberrytoby/angband
/*
 * Move player in the given direction.
 *
 * This routine should only be called when energy has been expended.
 *
 * Note that this routine handles monsters in the destination grid,
 * and also handles attempting to move into walls/doors/rubble/etc.
 */
void move_player(int dir, bool disarm)
{
	int py = p_ptr->py;
	int px = p_ptr->px;

	int y = py + ddy[dir];
	int x = px + ddx[dir];
	
	int m_idx = cave->m_idx[y][x];

	/* Attack monsters */
	if (m_idx > 0) {
		/* Mimics surprise the player */
		if (is_mimicking(m_idx)) {
			become_aware(m_idx);

			/* Mimic wakes up */
			mon_clear_timed(m_idx, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE);

		} else {
			py_attack(y, x);
		}
	}

	/* Optionally alter traps/doors on movement */
	else if (disarm && (cave->info[y][x] & CAVE_MARK) &&
			(cave_isknowntrap(cave, y, x) ||
			cave_iscloseddoor(cave, y, x)))
	{
		/* Auto-repeat if not already repeating */
		if (cmd_get_nrepeats() == 0)
			cmd_set_repeat(99);

		do_cmd_alter_aux(dir);
	}

	/* Cannot walk through walls */
	else if (!cave_floor_bold(y, x))
	{
		/* Disturb the player */
		disturb(p_ptr, 0, 0);

		/* Notice unknown obstacles */
		if (!(cave->info[y][x] & CAVE_MARK))
		{
			/* Rubble */
			if (cave->feat[y][x] == FEAT_RUBBLE)
			{
				msgt(MSG_HITWALL, "You feel a pile of rubble blocking your way.");
				cave->info[y][x] |= (CAVE_MARK);
				cave_light_spot(cave, y, x);
			}

			/* Closed door */
			else if (cave->feat[y][x] < FEAT_SECRET)
			{
				msgt(MSG_HITWALL, "You feel a door blocking your way.");
				cave->info[y][x] |= (CAVE_MARK);
				cave_light_spot(cave, y, x);
			}

			/* Wall (or secret door) */
			else
			{
				msgt(MSG_HITWALL, "You feel a wall blocking your way.");
				cave->info[y][x] |= (CAVE_MARK);
				cave_light_spot(cave, y, x);
			}
		}

		/* Mention known obstacles */
		else
		{
			if (cave->feat[y][x] == FEAT_RUBBLE)
				msgt(MSG_HITWALL, "There is a pile of rubble blocking your way.");
			else if (cave->feat[y][x] < FEAT_SECRET)
				msgt(MSG_HITWALL, "There is a door blocking your way.");
			else
				msgt(MSG_HITWALL, "There is a wall blocking your way.");
		}
	}

	/* Normal movement */
	else
	{
		/* See if trap detection status will change */
		bool old_dtrap = ((cave->info2[py][px] & (CAVE2_DTRAP)) != 0);
		bool new_dtrap = ((cave->info2[y][x] & (CAVE2_DTRAP)) != 0);

		/* Note the change in the detect status */
		if (old_dtrap != new_dtrap)
			p_ptr->redraw |= (PR_DTRAP);

		/* Disturb player if the player is about to leave the area */
		if (OPT(disturb_detect) && p_ptr->running && 
			!p_ptr->running_firststep && old_dtrap && !new_dtrap)
		{
			disturb(p_ptr, 0, 0);
			return;
		}

		/* Move player */
		monster_swap(py, px, y, x);
  
		/* New location */
		y = py = p_ptr->py;
		x = px = p_ptr->px;

		/* Searching */
		if (p_ptr->searching ||
				(p_ptr->state.skills[SKILL_SEARCH_FREQUENCY] >= 50) ||
				one_in_(50 - p_ptr->state.skills[SKILL_SEARCH_FREQUENCY]))
			search(FALSE);

		/* Handle "store doors" */
		if ((cave->feat[p_ptr->py][p_ptr->px] >= FEAT_SHOP_HEAD) &&
			(cave->feat[p_ptr->py][p_ptr->px] <= FEAT_SHOP_TAIL))
		{
			/* Disturb */
			disturb(p_ptr, 0, 0);
			cmd_insert(CMD_ENTER_STORE);
		}

		/* All other grids (including traps) */
		else
		{
			/* Handle objects (later) */
			p_ptr->notice |= (PN_PICKUP);
		}


		/* Discover invisible traps */
		if (cave->feat[y][x] == FEAT_INVIS)
		{
			/* Disturb */
			disturb(p_ptr, 0, 0);

			/* Message */
			msg("You found a trap!");

			/* Pick a trap */
			pick_trap(y, x);

			/* Hit the trap */
			hit_trap(y, x);
		}

		/* Set off an visible trap */
		else if (cave_isknowntrap(cave, y, x))
		{
			/* Disturb */
			disturb(p_ptr, 0, 0);

			/* Hit the trap */
			hit_trap(y, x);
		}
	}

	p_ptr->running_firststep = FALSE;
}
예제 #6
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);
}
예제 #7
0
파일: cmd2.c 프로젝트: qwerty16/angband
/*
 * Open a closed/locked/jammed door or a closed/locked chest.
 *
 * Unlocking a locked door/chest is worth one experience point.
 */
void do_cmd_open(cmd_code code, cmd_arg args[])
{
	int y, x, dir;

	s16b o_idx;

	bool more = FALSE;

	dir = args[0].direction;

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

	/* Check for chests */
	o_idx = chest_check(y, x);


	/* Verify legality */
	if (!o_idx && !do_cmd_open_test(y, x))
	{
		/* Cancel repeat */
		disturb(p_ptr, 0, 0);
		return;
	}

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

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

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


	/* Monster */
	if (cave->m_idx[y][x] > 0)
	{
		int m_idx = cave->m_idx[y][x];

		/* Mimics surprise the player */
		if (is_mimicking(m_idx)) {
			become_aware(m_idx);

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

			/* Attack */
			py_attack(y, x);
		}
	}

	/* Chest */
	else if (o_idx)
	{
		/* Open the chest */
		more = do_cmd_open_chest(y, x, o_idx);
	}

	/* Door */
	else
	{
		/* Open the door */
		more = do_cmd_open_aux(y, x);
	}

	/* Cancel repeat unless we may continue */
	if (!more) disturb(p_ptr, 0, 0);
}
예제 #8
0
파일: mon-make.c 프로젝트: seebs/angband
/**
 * Decreases a monster's hit points by `dam` and handle monster death.
 *
 * Hack -- we "delay" fear messages by passing around a "fear" flag.
 *
 * We announce monster death (using an optional "death message" (`note`)
 * if given, and a otherwise a generic killed/destroyed message).
 * 
 * Returns TRUE if the monster has been killed (and deleted).
 *
 * TODO: Consider decreasing monster experience over time, say, by using
 * "(m_exp * m_lev * (m_lev)) / (p_lev * (m_lev + n_killed))" instead
 * of simply "(m_exp * m_lev) / (p_lev)", to make the first monster
 * worth more than subsequent monsters.  This would also need to
 * induce changes in the monster recall code.  XXX XXX XXX
 **/
bool mon_take_hit(struct monster *m_ptr, int dam, bool *fear, const char *note)
{
	s32b div, new_exp, new_exp_frac;
	monster_lore *l_ptr = get_lore(m_ptr->race);


	/* Redraw (later) if needed */
	if (p_ptr->health_who == m_ptr) p_ptr->redraw |= (PR_HEALTH);

	/* Wake it up */
	mon_clear_timed(m_ptr, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, FALSE);

	/* Become aware of its presence */
	if (m_ptr->unaware)
		become_aware(m_ptr);

	/* Hurt it */
	m_ptr->hp -= dam;

	/* It is dead now */
	if (m_ptr->hp < 0) {
		char m_name[80];
		char buf[80];

		/* Assume normal death sound */
		int soundfx = MSG_KILL;

		/* Play a special sound if the monster was unique */
		if (rf_has(m_ptr->race->flags, RF_UNIQUE)) {
			if (m_ptr->race->base == lookup_monster_base("Morgoth"))
				soundfx = MSG_KILL_KING;
			else
				soundfx = MSG_KILL_UNIQUE;
		}

		/* Extract monster name */
		monster_desc(m_name, sizeof(m_name), m_ptr, 0);

		/* Death by Missile/Spell attack */
		if (note) {
			/* Hack -- allow message suppression */
			if (strlen(note) <= 1) {
				/* Be silent */
			} else {
				char *str = format("%s%s", m_name, note);
				my_strcap(str);
				msgt(soundfx, "%s", str);
			}
		}

		/* Death by physical attack -- invisible monster */
		else if (!m_ptr->ml)
			msgt(soundfx, "You have killed %s.", m_name);

		/* Death by Physical attack -- non-living monster */
		else if (monster_is_unusual(m_ptr->race))
			msgt(soundfx, "You have destroyed %s.", m_name);

		/* Death by Physical attack -- living monster */
		else
			msgt(soundfx, "You have slain %s.", m_name);

		/* Player level */
		div = p_ptr->lev;

		/* Give some experience for the kill */
		new_exp = ((long)m_ptr->race->mexp * m_ptr->race->level) / div;

		/* Handle fractional experience */
		new_exp_frac = ((((long)m_ptr->race->mexp * m_ptr->race->level) % div)
		                * 0x10000L / div) + p_ptr->exp_frac;

		/* Keep track of experience */
		if (new_exp_frac >= 0x10000L) {
			new_exp++;
			p_ptr->exp_frac = (u16b)(new_exp_frac - 0x10000L);
		}
		else
			p_ptr->exp_frac = (u16b)new_exp_frac;

		/* When the player kills a Unique, it stays dead */
		if (rf_has(m_ptr->race->flags, RF_UNIQUE)) {
			char unique_name[80];
			m_ptr->race->max_num = 0;

			/* 
			 * This gets the correct name if we slay an invisible 
			 * unique and don't have See Invisible.
			 */
			monster_desc(unique_name, sizeof(unique_name), m_ptr, 
					MDESC_SHOW | MDESC_IND2);

			/* Log the slaying of a unique */
			strnfmt(buf, sizeof(buf), "Killed %s", unique_name);
			history_add(buf, HISTORY_SLAY_UNIQUE, 0);
		}

		/* Gain experience */
		player_exp_gain(p_ptr, new_exp);

		/* Generate treasure */
		monster_death(m_ptr, FALSE);

		/* Recall even invisible uniques or winners */
		if (m_ptr->ml || rf_has(m_ptr->race->flags, RF_UNIQUE)) {
			/* Count kills this life */
			if (l_ptr->pkills < MAX_SHORT) l_ptr->pkills++;

			/* Count kills in all lives */
			if (l_ptr->tkills < MAX_SHORT) l_ptr->tkills++;

			/* Hack -- Auto-recall */
			monster_race_track(m_ptr->race);
		}

		/* Delete the monster */
		delete_monster_idx(m_ptr->midx);

		/* Not afraid */
		(*fear) = FALSE;

		/* Monster is dead */
		return (TRUE);
	}


	/* Mega-Hack -- Pain cancels fear */
	if (!(*fear) && m_ptr->m_timed[MON_TMD_FEAR] && (dam > 0)) {
		int tmp = randint1(dam);

		/* Cure a little fear */
		if (tmp < m_ptr->m_timed[MON_TMD_FEAR]) {
			/* Reduce fear */
			mon_dec_timed(m_ptr, MON_TMD_FEAR, tmp, MON_TMD_FLG_NOMESSAGE,
				FALSE);
		}

		/* Cure all the fear */
		else {
			/* Cure fear */
			mon_clear_timed(m_ptr, MON_TMD_FEAR, MON_TMD_FLG_NOMESSAGE, FALSE);

			/* No more fear */
			(*fear) = FALSE;
		}
	}

	/* Sometimes a monster gets scared by damage */
	if (!m_ptr->m_timed[MON_TMD_FEAR] && !rf_has(m_ptr->race->flags, RF_NO_FEAR) &&
		dam > 0) {
		int percentage;

		/* Percentage of fully healthy */
		percentage = (100L * m_ptr->hp) / m_ptr->maxhp;

		/*
		 * Run (sometimes) if at 10% or less of max hit points,
		 * or (usually) when hit for half its current hit points
		 */
		if ((randint1(10) >= percentage) ||
		    ((dam >= m_ptr->hp) && (randint0(100) < 80)))
		{
			int timer = randint1(10) + (((dam >= m_ptr->hp) && (percentage > 7)) ?
	                   20 : ((11 - percentage) * 5));

			/* Hack -- note fear */
			(*fear) = TRUE;

			mon_inc_timed(m_ptr, MON_TMD_FEAR, timer,
					MON_TMD_FLG_NOMESSAGE | MON_TMD_FLG_NOFAIL, FALSE);
		}
	}


	/* Not dead yet */
	return (FALSE);
}
예제 #9
0
/**
 * Creatures can cast spells, shoot missiles, and breathe.
 *
 * Returns "true" if a spell (or whatever) was (successfully) cast.
 *
 * XXX XXX XXX This function could use some work, but remember to
 * keep it as optimized as possible, while retaining generic code.
 *
 * Verify the various "blind-ness" checks in the code.
 *
 * XXX XXX XXX Note that several effects should really not be "seen"
 * if the player is blind.
 *
 * Perhaps monsters should breathe at locations *near* the player,
 * since this would allow them to inflict "partial" damage.
 *
 * Perhaps smart monsters should decline to use "bolt" spells if
 * there is a monster in the way, unless they wish to kill it.
 *
 * It will not be possible to "correctly" handle the case in which a
 * monster attempts to attack a location which is thought to contain
 * the player, but which in fact is nowhere near the player, since this
 * might induce all sorts of messages about the attack itself, and about
 * the effects of the attack, which the player might or might not be in
 * a position to observe.  Thus, for simplicity, it is probably best to
 * only allow "faulty" attacks by a monster if one of the important grids
 * (probably the initial or final grid) is in fact in view of the player.
 * It may be necessary to actually prevent spell attacks except when the
 * monster actually has line of sight to the player.  Note that a monster
 * could be left in a bizarre situation after the player ducked behind a
 * pillar and then teleported away, for example.
 *
 * Note that this function attempts to optimize the use of spells for the
 * cases in which the monster has no spells, or has spells but cannot use
 * them, or has spells but they will have no "useful" effect.  Note that
 * this function has been an efficiency bottleneck in the past.
 *
 * Note the special "MFLAG_NICE" flag, which prevents a monster from using
 * any spell attacks until the player has had a single chance to move.
 */
bool make_attack_spell(struct monster *mon)
{
	int chance, thrown_spell, rlev, failrate;

	bitflag f[RSF_SIZE];

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

	char m_name[80], m_poss[80], ddesc[80];

	/* Player position */
	int px = player->px;
	int py = player->py;

	/* Extract the blind-ness */
	bool blind = (player->timed[TMD_BLIND] ? true : false);

	/* Extract the "see-able-ness" */
	bool seen = (!blind && mflag_has(mon->mflag, MFLAG_VISIBLE));

	/* Assume "normal" target */
	bool normal = true;

	/* Cannot cast spells when confused */
	if (mon->m_timed[MON_TMD_CONF]) return (false);

	/* Cannot cast spells when nice */
	if (mflag_has(mon->mflag, MFLAG_NICE)) return false;

	/* Hack -- Extract the spell probability */
	chance = (mon->race->freq_innate + mon->race->freq_spell) / 2;

	/* Not allowed to cast spells */
	if (!chance) return false;

	/* Only do spells occasionally */
	if (randint0(100) >= chance) return false;

	/* Hack -- require projectable player */
	if (normal) {
		/* Check range */
		if (mon->cdis > z_info->max_range)
			return false;

		/* Check path */
		if (!projectable(cave, mon->fy, mon->fx, py, px, PROJECT_NONE))
			return false;
	}

	/* Extract the monster level */
	rlev = ((mon->race->level >= 1) ? mon->race->level : 1);

	/* Extract the racial spell flags */
	rsf_copy(f, mon->race->spell_flags);

	/* Allow "desperate" spells */
	if (rf_has(mon->race->flags, RF_SMART) &&
	    mon->hp < mon->maxhp / 10 &&
	    randint0(100) < 50)

		/* Require intelligent spells */
		ignore_spells(f, RST_BOLT | RST_BALL | RST_BREATH | RST_ATTACK | RST_INNATE);

	/* Remove the "ineffective" spells */
	remove_bad_spells(mon, f);

	/* Check whether summons and bolts are worth it. */
	if (!rf_has(mon->race->flags, RF_STUPID)) {
		/* Check for a clean bolt shot */
		if (test_spells(f, RST_BOLT) &&
			!projectable(cave, mon->fy, mon->fx, py, px, PROJECT_STOP))

			/* Remove spells that will only hurt friends */
			ignore_spells(f, RST_BOLT);

		/* Check for a possible summon */
		if (!(summon_possible(mon->fy, mon->fx)))

			/* Remove summoning spells */
			ignore_spells(f, RST_SUMMON);
	}

	/* No spells left */
	if (rsf_is_empty(f)) return false;

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

	/* Get the monster possessive ("his"/"her"/"its") */
	monster_desc(m_poss, sizeof(m_poss), mon, MDESC_PRO_VIS | MDESC_POSS);

	/* Get the "died from" name */
	monster_desc(ddesc, sizeof(ddesc), mon, MDESC_DIED_FROM);

	/* Choose a spell to cast */
	thrown_spell = choose_attack_spell(f);

	/* Abort if no spell was chosen */
	if (!thrown_spell) return false;

	/* If we see an unaware monster try to cast a spell, become aware of it */
	if (mflag_has(mon->mflag, MFLAG_UNAWARE))
		become_aware(mon);

	/* Calculate spell failure rate */
	failrate = 25 - (rlev + 3) / 4;
	if (mon->m_timed[MON_TMD_FEAR])
		failrate += 20;

	/* Stupid monsters will never fail (for jellies and such) */
	if (rf_has(mon->race->flags, RF_STUPID))
		failrate = 0;

	/* Check for spell failure (innate attacks never fail) */
	if (!mon_spell_is_innate(thrown_spell) && (randint0(100) < failrate)) {
		/* Message */
		msg("%s tries to cast a spell, but fails.", m_name);

		return true;
	}

	/* Cast the spell. */
	disturb(player, 1);
	do_mon_spell(thrown_spell, mon, seen);

	/* Remember what the monster did to us */
	if (seen) {
		rsf_on(lore->spell_flags, thrown_spell);

		/* Innate spell */
		if (mon_spell_is_innate(thrown_spell)) {
			if (lore->cast_innate < UCHAR_MAX)
				lore->cast_innate++;
		} else {
			/* Bolt or Ball, or Special spell */
			if (lore->cast_spell < UCHAR_MAX)
				lore->cast_spell++;
		}
	}

	/* Always take note of monsters that kill you */
	if (player->is_dead && (lore->deaths < SHRT_MAX)) {
		lore->deaths++;
	}

	/* Record any new info */
	lore_update(mon->race, lore);

	/* A spell was cast */
	return true;
}
예제 #10
0
파일: cmd-cave.c 프로젝트: Chillbon/angband
/**
 * Move player in the given direction.
 *
 * This routine should only be called when energy has been expended.
 *
 * Note that this routine handles monsters in the destination grid,
 * and also handles attempting to move into walls/doors/rubble/etc.
 */
void move_player(int dir, bool disarm)
{
	int py = player->py;
	int px = player->px;

	int y = py + ddy[dir];
	int x = px + ddx[dir];

	int m_idx = cave->squares[y][x].mon;
	struct monster *mon = cave_monster(cave, m_idx);
	bool alterable = (square_isknowntrap(cave, y, x) ||
					  square_iscloseddoor(cave, y, x));

	/* Attack monsters, alter traps/doors on movement, hit obstacles or move */
	if (m_idx > 0) {
		/* Mimics surprise the player */
		if (is_mimicking(mon)) {
			become_aware(mon);

			/* Mimic wakes up */
			mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false);

		} else {
			py_attack(y, x);
		}
	} else if (disarm && square_isknown(cave, y, x) && alterable) {
		/* Auto-repeat if not already repeating */
		if (cmd_get_nrepeats() == 0)
			cmd_set_repeat(99);

		do_cmd_alter_aux(dir);
	} else if (player->upkeep->running && square_isknowntrap(cave, y, x)) {
		/* Stop running before known traps */
		disturb(player, 0);
	} else if (!square_ispassable(cave, y, x)) {
		/* Disturb the player */
		disturb(player, 0);

		/* Notice unknown obstacles, mention known obstacles */
		if (!square_isknown(cave, y, x)) {
			if (square_isrubble(cave, y, x)) {
				msgt(MSG_HITWALL,
					 "You feel a pile of rubble blocking your way.");
				square_memorize(cave, y, x);
				square_light_spot(cave, y, x);
			} else if (square_iscloseddoor(cave, y, x)) {
				msgt(MSG_HITWALL, "You feel a door blocking your way.");
				square_memorize(cave, y, x);
				square_light_spot(cave, y, x);
			} else {
				msgt(MSG_HITWALL, "You feel a wall blocking your way.");
				square_memorize(cave, y, x);
				square_light_spot(cave, y, x);
			}
		} else {
			if (square_isrubble(cave, y, x))
				msgt(MSG_HITWALL,
					 "There is a pile of rubble blocking your way.");
			else if (square_iscloseddoor(cave, y, x))
				msgt(MSG_HITWALL, "There is a door blocking your way.");
			else
				msgt(MSG_HITWALL, "There is a wall blocking your way.");
		}
	} else {
		/* See if trap detection status will change */
		bool old_dtrap = square_isdtrap(cave, py, px);
		bool new_dtrap = square_isdtrap(cave, y, x);

		/* Note the change in the detect status */
		if (old_dtrap != new_dtrap)
			player->upkeep->redraw |= (PR_DTRAP);

		/* Disturb player if the player is about to leave the area */
		if (player->upkeep->running && !player->upkeep->running_firststep && 
			old_dtrap && !new_dtrap) {
			disturb(player, 0);
			return;
		}

		/* Move player */
		monster_swap(py, px, y, x);

		/* New location */
		y = py = player->py;
		x = px = player->px;

		/* Searching */
		if (player->searching ||
				(player->state.skills[SKILL_SEARCH_FREQUENCY] >= 50) ||
				one_in_(50 - player->state.skills[SKILL_SEARCH_FREQUENCY]))
			search(false);

		/* Handle store doors, or notice objects */
		if (square_isshop(cave, player->py, player->px)) {
			/* Disturb */
			disturb(player, 0);
			event_signal(EVENT_ENTER_STORE);
			event_remove_handler_type(EVENT_ENTER_STORE);
			event_signal(EVENT_USE_STORE);
			event_remove_handler_type(EVENT_USE_STORE);
			event_signal(EVENT_LEAVE_STORE);
			event_remove_handler_type(EVENT_LEAVE_STORE);
		} else {
			/* Know objects, queue autopickup */
			floor_pile_know(cave, player->py, player->px);
			cmdq_push(CMD_AUTOPICKUP);
		}


		/* Discover invisible traps, set off visible ones */
		if (square_issecrettrap(cave, y, x)) {
			/* Disturb */
			disturb(player, 0);

			/* Hit the trap. */
			hit_trap(y, x);
		} else if (square_isknowntrap(cave, y, x)) {
			/* Disturb */
			disturb(player, 0);

			/* Hit the trap */
			hit_trap(y, x);
		}
	}

	player->upkeep->running_firststep = false;
}