Beispiel #1
0
static void project_player_handler_NEXUS(project_player_handler_context_t *context)
{
	struct monster *mon = NULL;
	if (context->origin.what == SRC_MONSTER) {
		mon = cave_monster(cave, context->origin.which.monster);
	}

	if (player_resists(player, ELEM_NEXUS)) {
		msg("You resist the effect!");
		return;
	}

	/* Stat swap */
	if (randint0(100) < player->state.skills[SKILL_SAVE]) {
		msg("You avoid the effect!");
	} else {
		project_player_swap_stats();
	}

	if (one_in_(3) && mon) { /* Teleport to */
		effect_simple(EF_TELEPORT_TO, context->origin, "0", mon->fy, mon->fx, 0, NULL);
	} else if (one_in_(4)) { /* Teleport level */
		if (randint0(100) < player->state.skills[SKILL_SAVE]) {
			msg("You avoid the effect!");
			return;
		}
		effect_simple(EF_TELEPORT_LEVEL, context->origin, "0", 0, 0, 0, NULL);
	} else { /* Teleport */
		const char *miles = "200";
		effect_simple(EF_TELEPORT, context->origin, miles, 0, 1, 0, NULL);
	}
}
Beispiel #2
0
static void adjust_level(struct player *p, bool verbose)
{
	if (p->exp < 0)
		p->exp = 0;

	if (p->max_exp < 0)
		p->max_exp = 0;

	if (p->exp > PY_MAX_EXP)
		p->exp = PY_MAX_EXP;

	if (p->max_exp > PY_MAX_EXP)
		p->max_exp = PY_MAX_EXP;

	if (p->exp > p->max_exp)
		p->max_exp = p->exp;

	p->upkeep->redraw |= PR_EXP;

	handle_stuff(p);

	while ((p->lev > 1) &&
	       (p->exp < (player_exp[p->lev-2] * p->expfact / 100L)))
		p->lev--;


	while ((p->lev < PY_MAX_LEVEL) &&
	       (p->exp >= (player_exp[p->lev-1] * p->expfact / 100L))) {
		char buf[80];

		p->lev++;

		/* Save the highest level */
		if (p->lev > p->max_lev)
			p->max_lev = p->lev;

		if (verbose) {
			/* Log level updates */
			strnfmt(buf, sizeof(buf), "Reached level %d", p->lev);
			history_add(p, buf, HIST_GAIN_LEVEL);

			/* Message */
			msgt(MSG_LEVEL, "Welcome to level %d.",	p->lev);
		}

		effect_simple(EF_RESTORE_STAT, "0", STAT_STR, 1, 0, NULL);
		effect_simple(EF_RESTORE_STAT, "0", STAT_INT, 1, 0, NULL);
		effect_simple(EF_RESTORE_STAT, "0", STAT_WIS, 1, 0, NULL);
		effect_simple(EF_RESTORE_STAT, "0", STAT_DEX, 1, 0, NULL);
		effect_simple(EF_RESTORE_STAT, "0", STAT_CON, 1, 0, NULL);
	}

	while ((p->max_lev < PY_MAX_LEVEL) &&
	       (p->max_exp >= (player_exp[p->max_lev-1] * p->expfact / 100L)))
		p->max_lev++;

	p->upkeep->update |= (PU_BONUS | PU_HP | PU_SPELLS);
	p->upkeep->redraw |= (PR_LEV | PR_TITLE | PR_EXP | PR_STATS);
	handle_stuff(p);
}
Beispiel #3
0
/**
 * Melee effect handler: Drain all of the player's stats.
 */
static void melee_effect_handler_LOSE_ALL(melee_effect_handler_context_t *context)
{
	/* Take damage */
	take_hit(context->p, context->damage, context->ddesc);

	/* Damage (stats) */
	effect_simple(EF_DRAIN_STAT, "0", STAT_STR, 0, 0, &context->obvious);
	effect_simple(EF_DRAIN_STAT, "0", STAT_DEX, 0, 0, &context->obvious);
	effect_simple(EF_DRAIN_STAT, "0", STAT_CON, 0, 0, &context->obvious);
	effect_simple(EF_DRAIN_STAT, "0", STAT_INT, 0, 0, &context->obvious);
	effect_simple(EF_DRAIN_STAT, "0", STAT_WIS, 0, 0, &context->obvious);
}
/**
 * Chests have traps too.
 *
 * Exploding chest destroys contents (and traps).
 * Note that the chest itself is never destroyed.
 */
static void chest_trap(int y, int x, struct object *obj)
{
	int trap;

	/* Ignore disarmed chests */
	if (obj->pval <= 0) return;

	/* Obtain the traps */
	trap = chest_traps[obj->pval];

	/* Lose strength */
	if (trap & (CHEST_LOSE_STR)) {
		msg("A small needle has pricked you!");
		take_hit(player, damroll(1, 4), "a poison needle");
		effect_simple(EF_DRAIN_STAT, "0", STAT_STR, 0, 0, NULL);
	}

	/* Lose constitution */
	if (trap & (CHEST_LOSE_CON)) {
		msg("A small needle has pricked you!");
		take_hit(player, damroll(1, 4), "a poison needle");
		effect_simple(EF_DRAIN_STAT, "0", STAT_CON, 0, 0, NULL);
	}

	/* Poison */
	if (trap & (CHEST_POISON)) {
		msg("A puff of green gas surrounds you!");
		effect_simple(EF_TIMED_INC, "10+1d20", TMD_POISONED, 0, 0, NULL);
	}

	/* Paralyze */
	if (trap & (CHEST_PARALYZE)) {
		msg("A puff of yellow gas surrounds you!");
		effect_simple(EF_TIMED_INC, "10+1d20", TMD_PARALYZED, 0, 0, NULL);
	}

	/* Summon monsters */
	if (trap & (CHEST_SUMMON)) {
		msg("You are enveloped in a cloud of smoke!");
		effect_simple(EF_SUMMON, "2+1d3", 0, 0, 0, NULL);
	}

	/* Explode */
	if (trap & (CHEST_EXPLODE)) {
		msg("There is a sudden explosion!");
		msg("Everything inside the chest is destroyed!");
		obj->pval = 0;
		take_hit(player, damroll(5, 8), "an exploding chest");
	}
}
/**
 * Melee effect handler: Cause an earthquake around the player.
 */
static void melee_effect_handler_SHATTER(melee_effect_handler_context_t *context)
{
	/* Obvious */
	context->obvious = TRUE;

	/* Hack -- Reduce damage based on the player armor class */
	context->damage = adjust_dam_armor(context->damage, context->ac);

	/* Take damage */
	take_hit(context->p, context->damage, context->ddesc);

	/* Player is dead */
	if (context->p->is_dead)
		return;

	/* Radius 8 earthquake centered at the monster */
	if (context->damage > 23) {
		int px_old = context->p->px;
		int py_old = context->p->py;

		effect_simple(EF_EARTHQUAKE, "0", 0, 8, 0, NULL);

		/* Stop the blows if the player is pushed away */
		if ((px_old != context->p->px) ||
			(py_old != context->p->py))
			context->do_break = TRUE;
	}
}
Beispiel #6
0
/**
 * Do damage as the result of a melee attack that drains a stat.
 *
 * \param context is the information for the current attack.
 * \param stat is the STAT_ constant for the desired stat.
 */
static void melee_effect_stat(melee_effect_handler_context_t *context, int stat)
{
	/* Take damage */
	take_hit(context->p, context->damage, context->ddesc);

	/* Damage (stat) */
	effect_simple(EF_DRAIN_STAT, "0", stat, 0, 0, &context->obvious);
}
Beispiel #7
0
static void project_player_handler_DISEN(project_player_handler_context_t *context)
{
	if (player_resists(player, ELEM_DISEN)) {
		msg("You resist the effect!");
		return;
	}

	/* Disenchant gear */
	effect_simple(EF_DISENCHANT, context->origin, "0", 0, 0, 0, NULL);
}
Beispiel #8
0
static void project_player_handler_FORCE(project_player_handler_context_t *context)
{
	char grids_away[5];

	/* Stun */
	(void)player_inc_timed(player, TMD_STUN, randint1(20), true, true);

	/* Thrust player away. */
	strnfmt(grids_away, sizeof(grids_away), "%d", 3 + context->dam / 20);
	effect_simple(EF_THRUST_AWAY, context->origin, grids_away, context->y, context->x, 0, NULL);
}
Beispiel #9
0
/**
 * Melee effect handler: Disenchant the player.
 */
static void melee_effect_handler_DISENCHANT(melee_effect_handler_context_t *context)
{
	/* Take damage */
	take_hit(context->p, context->damage, context->ddesc);

	/* Apply disenchantmen if no resist */
	if (!player_resists(context->p, ELEM_DISEN))
		effect_simple(EF_DISENCHANT, "0", 0, 0, 0, &context->obvious);

	/* Learn about the player */
	update_smart_learn(context->m_ptr, context->p, 0, 0, ELEM_DISEN);
}
Beispiel #10
0
/**
 * Melee effect handler: Absorb the player's light.
 */
static void melee_effect_handler_EAT_LIGHT(melee_effect_handler_context_t *context)
{
	/* Take damage */
	take_hit(context->p, context->damage, context->ddesc);

	/* Player is dead */
	if (context->p->is_dead)
		return;

	/* Drain the light source */
	effect_simple(EF_DRAIN_LIGHT, "250+1d250", 0, 0, 0, &context->obvious);
}
Beispiel #11
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;
}
static void project_player_handler_GRAVITY(project_player_handler_context_t *context)
{
    msg("Gravity warps around you.");

    /* Blink */
    if (randint1(127) > player->lev) {
        const char *five = "5";
        effect_simple(EF_TELEPORT, five, 0, 1, 0, NULL);
    }

    /* Slow */
    (void)player_inc_timed(player, TMD_SLOW, 4 + randint0(4), TRUE, FALSE);

    /* Stun */
    if (!player_of_has(player, OF_PROT_STUN)) {
        int duration = 5 + randint1(context->dam / 3);
        if (duration > 35) duration = 35;
        (void)player_inc_timed(player, TMD_STUN, duration, TRUE, TRUE);
    }
}
Beispiel #13
0
/**
 * Process player commands from the command queue, finishing when there is a
 * command using energy (any regular game command), or we run out of commands
 * and need another from the user, or the character changes level or dies, or
 * the game is stopped.
 *
 * Notice the annoying code to handle "pack overflow", which
 * must come first just in case somebody manages to corrupt
 * the savefiles by clever use of menu commands or something. (Can go? NRM)
 *
 * Notice the annoying code to handle "monster memory" changes,
 * which allows us to avoid having to update the window flags
 * every time we change any internal monster memory field, and
 * also reduces the number of times that the recall window must
 * be redrawn.
 */
void process_player(void)
{
	/* Check for interrupts */
	player_resting_complete_special(player);
	event_signal(EVENT_CHECK_INTERRUPT);

	/* Repeat until energy is reduced */
	do {
		/* Refresh */
		notice_stuff(player);
		handle_stuff(player);
		event_signal(EVENT_REFRESH);

		/* Hack -- Pack Overflow */
		pack_overflow(NULL);

		/* Assume free turn */
		player->upkeep->energy_use = 0;

		/* Dwarves detect treasure */
		if (player_has(player, PF_SEE_ORE)) {
			/* Only if they are in good shape */
			if (!player->timed[TMD_IMAGE] &&
				!player->timed[TMD_CONFUSED] &&
				!player->timed[TMD_AMNESIA] &&
				!player->timed[TMD_STUN] &&
				!player->timed[TMD_PARALYZED] &&
				!player->timed[TMD_TERROR] &&
				!player->timed[TMD_AFRAID])
				effect_simple(EF_DETECT_GOLD, "3d3", 1, 0, 0, NULL);
		}

		/* Paralyzed or Knocked Out player gets no turn */
		if ((player->timed[TMD_PARALYZED]) || (player->timed[TMD_STUN] >= 100))
			cmdq_push(CMD_SLEEP);

		/* Prepare for the next command */
		if (cmd_get_nrepeats() > 0)
			event_signal(EVENT_COMMAND_REPEAT);
		else {
			/* Check monster recall */
			if (player->upkeep->monster_race)
				player->upkeep->redraw |= (PR_MONSTER);

			/* Place cursor on player/target */
			event_signal(EVENT_REFRESH);
		}

		/* Get a command from the queue if there is one */
		if (!cmdq_pop(CMD_GAME))
			break;

		if (!player->upkeep->playing)
			break;

		process_player_cleanup();
	} while (!player->upkeep->energy_use &&
			 !player->is_dead &&
			 !player->upkeep->generate_level);

	/* Notice stuff (if needed) */
	notice_stuff(player);
}
Beispiel #14
0
/**
 * Handle things that need updating once every 10 game turns
 */
void process_world(struct chunk *c)
{
	int i, y, x;

	/* Compact the monster list if we're approaching the limit */
	if (cave_monster_count(cave) + 32 > z_info->level_monster_max)
		compact_monsters(64);

	/* Too many holes in the monster list - compress */
	if (cave_monster_count(cave) + 32 < cave_monster_max(cave))
		compact_monsters(0);

	/*** Check the Time ***/

	/* Play an ambient sound at regular intervals. */
	if (!(turn % ((10L * z_info->day_length) / 4)))
		play_ambient_sound();

	/*** Handle stores and sunshine ***/

	if (!player->depth) {
		/* Daybreak/Nighfall in town */
		if (!(turn % ((10L * z_info->day_length) / 2))) {
			bool dawn;

			/* Check for dawn */
			dawn = (!(turn % (10L * z_info->day_length)));

			/* Day breaks */
			if (dawn)
				msg("The sun has risen.");

			/* Night falls */
			else
				msg("The sun has fallen.");

			/* Illuminate */
			cave_illuminate(c, dawn);
		}
	} else {
		/* Update the stores once a day (while in the dungeon).
		   The changes are not actually made until return to town,
		   to avoid giving details away in the knowledge menu. */
		if (!(turn % (10L * z_info->store_turns))) daycount++;
	}


	/* Check for creature generation */
	if (one_in_(z_info->alloc_monster_chance))
		(void)pick_and_place_distant_monster(cave, player,
											 z_info->max_sight + 5, true,
											 player->depth);

	/*** Damage over Time ***/

	/* Take damage from poison */
	if (player->timed[TMD_POISONED])
		take_hit(player, 1, "poison");

	/* Take damage from cuts */
	if (player->timed[TMD_CUT]) {
		/* Mortal wound or Deep Gash */
		if (player->timed[TMD_CUT] > TMD_CUT_SEVERE)
			i = 3;

		/* Severe cut */
		else if (player->timed[TMD_CUT] > TMD_CUT_NASTY)
			i = 2;

		/* Other cuts */
		else
			i = 1;

		/* Take damage */
		take_hit(player, i, "a fatal wound");
	}


	/*** Check the Food, and Regenerate ***/

	/* Digest normally */
	if (!(turn % 100)) {
		/* Basic digestion rate based on speed */
		i = turn_energy(player->state.speed) * 2;

		/* Regeneration takes more food */
		if (player_of_has(player, OF_REGEN)) i += 30;

		/* Slow digestion takes less food */
		if (player_of_has(player, OF_SLOW_DIGEST)) i /= 5;

		/* Minimal digestion */
		if (i < 1) i = 1;

		/* Digest some food */
		player_set_food(player, player->food - i);
	}

	/* Getting Faint */
	if (player->food < PY_FOOD_FAINT) {
		/* Faint occasionally */
		if (!player->timed[TMD_PARALYZED] && one_in_(10)) {
			/* Message */
			msg("You faint from the lack of food.");
			disturb(player, 1);

			/* Faint (bypass free action) */
			(void)player_inc_timed(player, TMD_PARALYZED, 1 + randint0(5),
								   true, false);
		}
	}

	/* Starve to death (slowly) */
	if (player->food < PY_FOOD_STARVE) {
		/* Calculate damage */
		i = (PY_FOOD_STARVE - player->food) / 10;

		/* Take damage */
		take_hit(player, i, "starvation");
	}

	/* Regenerate Hit Points if needed */
	if (player->chp < player->mhp)
		player_regen_hp(player);

	/* Regenerate mana if needed */
	if (player->csp < player->msp)
		player_regen_mana(player);

	/* Timeout various things */
	decrease_timeouts();

	/* Process light */
	player_update_light(player);


	/*** Process Inventory ***/

	/* Handle experience draining */
	if (player_of_has(player, OF_DRAIN_EXP)) {
		if ((player->exp > 0) && one_in_(10)) {
			s32b d = damroll(10, 6) +
				(player->exp / 100) * z_info->life_drain_percent;
			player_exp_lose(player, d / 10, false);
		}

		equip_learn_flag(player, OF_DRAIN_EXP);
	}

	/* Recharge activatable objects and rods */
	recharge_objects();

	/* Notice things after time */
	if (!(turn % 100))
		equip_learn_after_time(player);

	/* Decrease trap timeouts */
	for (y = 0; y < cave->height; y++) {
		for (x = 0; x < cave->width; x++) {
			struct trap *trap = cave->squares[y][x].trap;
			while (trap) {
				if (trap->timeout) {
					trap->timeout--;
					if (!trap->timeout)
						square_light_spot(cave, y, x);
				}
				trap = trap->next;
			}
		}
	}


	/*** Involuntary Movement ***/

	/* Delayed Word-of-Recall */
	if (player->word_recall) {
		/* Count down towards recall */
		player->word_recall--;

		/* Activate the recall */
		if (!player->word_recall) {
			/* Disturbing! */
			disturb(player, 0);

			/* Determine the level */
			if (player->depth) {
				msgt(MSG_TPLEVEL, "You feel yourself yanked upwards!");
				dungeon_change_level(player, 0);
			} else {
				msgt(MSG_TPLEVEL, "You feel yourself yanked downwards!");
                
                /* Force descent to a lower level if allowed */
                if (OPT(player, birth_force_descend) &&
					player->max_depth < z_info->max_depth - 1 &&
					!is_quest(player->max_depth)) {
                    player->max_depth = dungeon_get_next_level(player->max_depth, 1);
                }

				/* New depth - back to max depth or 1, whichever is deeper */
				dungeon_change_level(player, player->max_depth < 1 ? 1: player->max_depth);
			}
		}
	}

	/* Delayed Deep Descent */
	if (player->deep_descent) {
		/* Count down towards recall */
		player->deep_descent--;

		/* Activate the recall */
		if (player->deep_descent == 0) {
			int target_increment;
			int target_depth = player->max_depth;

			/* Calculate target depth */
			target_increment = (4 / z_info->stair_skip) + 1;
			target_depth = dungeon_get_next_level(player->max_depth, target_increment);

			disturb(player, 0);

			/* Determine the level */
			if (target_depth > player->depth) {
				msgt(MSG_TPLEVEL, "The floor opens beneath you!");
				dungeon_change_level(player, target_depth);
			} else {
				/* Otherwise do something disastrous */
				msgt(MSG_TPLEVEL, "You are thrown back in an explosion!");
				effect_simple(EF_DESTRUCTION, "0", 0, 5, 0, NULL);
			}		
		}
	}
}
Beispiel #15
0
/**
 * Attack the player via physical attacks.
 */
bool make_attack_normal(struct monster *mon, struct player *p)
{
	struct monster_lore *lore = get_lore(mon->race);
	int ap_cnt;
	int k, tmp, ac, rlev;
	char m_name[80];
	char ddesc[80];
	bool blinked;

	/* Not allowed to attack */
	if (rf_has(mon->race->flags, RF_NEVER_BLOW)) return (false);

	/* Total armor */
	ac = p->state.ac + p->state.to_a;

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


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

	/* Get the "died from" information (i.e. "a kobold") */
	monster_desc(ddesc, sizeof(ddesc), mon, MDESC_SHOW | MDESC_IND_VIS);

	/* Assume no blink */
	blinked = false;

	/* Scan through all blows */
	for (ap_cnt = 0; ap_cnt < z_info->mon_blows_max; ap_cnt++) {
		bool visible = false;
		bool obvious = false;
		bool do_break = false;

		int power = 0;
		int damage = 0;
		int do_cut = 0;
		int do_stun = 0;
		int sound_msg = MSG_GENERIC;

		const char *act = NULL;

		/* Extract the attack infomation */
		int effect = mon->race->blow[ap_cnt].effect;
		int method = mon->race->blow[ap_cnt].method;
		random_value dice = mon->race->blow[ap_cnt].dice;

		/* Hack -- no more attacks */
		if (!method) break;

		/* Handle "leaving" */
		if (p->is_dead || p->upkeep->generate_level) break;

		/* Extract visibility (before blink) */
		if (mflag_has(mon->mflag, MFLAG_VISIBLE)) visible = true;

		/* Extract visibility from carrying light */
		if (rf_has(mon->race->flags, RF_HAS_LIGHT)) visible = true;

		/* Extract the attack "power" */
		power = monster_blow_effect_power(effect);

		/* Monster hits player */
		if (!effect || check_hit(p, power, rlev)) {
			melee_effect_handler_f effect_handler;

			/* Always disturbing */
			disturb(p, 1);

			/* Hack -- Apply "protection from evil" */
			if (p->timed[TMD_PROTEVIL] > 0) {
				/* Learn about the evil flag */
				if (mflag_has(mon->mflag, MFLAG_VISIBLE))
					rf_on(lore->flags, RF_EVIL);

				if (rf_has(mon->race->flags, RF_EVIL) && p->lev >= rlev &&
				    randint0(100) + p->lev > 50) {
					/* Message */
					msg("%s is repelled.", m_name);

					/* Hack -- Next attack */
					continue;
				}
			}

			/* Describe the attack method */
			act = monster_blow_method_action(method);
			do_cut = monster_blow_method_cut(method);
			do_stun = monster_blow_method_stun(method);
			sound_msg = monster_blow_method_message(method);

			/* Message */
			if (act)
				msgt(sound_msg, "%s %s", m_name, act);

			/* Hack -- assume all attacks are obvious */
			obvious = true;

			/* Roll dice */
			damage = randcalc(dice, rlev, RANDOMISE);

			/* Perform the actual effect. */
			effect_handler = melee_handler_for_blow_effect(effect);
			if (effect_handler != NULL) {
				melee_effect_handler_context_t context = {
					p,
					mon,
					rlev,
					method,
					ac,
					ddesc,
					obvious,
					blinked,
					do_break,
					damage,
				};

				effect_handler(&context);

				/* Save any changes made in the handler for later use. */
				obvious = context.obvious;
				blinked = context.blinked;
				damage = context.damage;
				do_break = context.do_break;
			} else {
				msg("ERROR: Effect handler not found for %d.", effect);
			}

			/* Don't cut or stun if player is dead */
			if (p->is_dead) {
				do_cut = false;
				do_stun = false;
			}

			/* Hack -- only one of cut or stun */
			if (do_cut && do_stun) {
				/* Cancel cut */
				if (randint0(100) < 50)
					do_cut = 0;

				/* Cancel stun */
				else
					do_stun = 0;
			}

			/* Handle cut */
			if (do_cut) {
				/* Critical hit (zero if non-critical) */
				tmp = monster_critical(dice, rlev, damage);

				/* Roll for damage */
				switch (tmp) {
					case 0: k = 0; break;
					case 1: k = randint1(5); break;
					case 2: k = randint1(5) + 5; break;
					case 3: k = randint1(20) + 20; break;
					case 4: k = randint1(50) + 50; break;
					case 5: k = randint1(100) + 100; break;
					case 6: k = 300; break;
					default: k = 500; break;
				}

				/* Apply the cut */
				if (k) (void)player_inc_timed(p, TMD_CUT, k, true, true);
			}

			/* Handle stun */
			if (do_stun) {
				/* Critical hit (zero if non-critical) */
				tmp = monster_critical(dice, rlev, damage);

				/* Roll for damage */
				switch (tmp) {
					case 0: k = 0; break;
					case 1: k = randint1(5); break;
					case 2: k = randint1(10) + 10; break;
					case 3: k = randint1(20) + 20; break;
					case 4: k = randint1(30) + 30; break;
					case 5: k = randint1(40) + 40; break;
					case 6: k = 100; break;
					default: k = 200; break;
				}

				/* Apply the stun */
				if (k)
					(void)player_inc_timed(p, TMD_STUN, k, true, true);
			}
		} else {
			/* Visible monster missed player, so notify if appropriate. */
			if (mflag_has(mon->mflag, MFLAG_VISIBLE) &&
				monster_blow_method_miss(method)) {
				/* Disturbing */
				disturb(p, 1);
				msg("%s misses you.", m_name);
			}
		}

		/* Analyze "visible" monsters only */
		if (visible) {
			/* Count "obvious" attacks (and ones that cause damage) */
			if (obvious || damage || (lore->blows[ap_cnt].times_seen > 10)) {
				/* Count attacks of this type */
				if (lore->blows[ap_cnt].times_seen < UCHAR_MAX)
					lore->blows[ap_cnt].times_seen++;
			}
		}

		/* Skip the other blows if necessary */
		if (do_break) break;
	}

	/* Blink away */
	if (blinked) {
		char dice[5];
		msg("There is a puff of smoke!");
		strnfmt(dice, sizeof(dice), "%d", z_info->max_sight * 2 + 5);
		effect_simple(EF_TELEPORT, dice, 0, 0, 0, NULL);
	}

	/* Always notice cause of death */
	if (p->is_dead && (lore->deaths < SHRT_MAX))
		lore->deaths++;

	/* Learn lore */
	lore_update(mon->race, lore);

	/* Assume we attacked */
	return (true);
}