static long eval_max_dam(struct monster_race *race, int ridx)
{
	int rlev, i;
	int melee_dam = 0, atk_dam = 0, spell_dam = 0;
	int dam = 1;

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

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

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

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

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

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

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

		if (!race->blow) break;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	/* We're done */
	return (dam);
}
Beispiel #2
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);
}