/** * Wake a monster or reduce its depth of sleep * * Chance of waking up is dependent only on the player's stealth, but the * amount of sleep reduction takes into account the monster's distance from * the player. Currently straight line distance is used; possibly this * should take into account dungeon structure. */ static void monster_reduce_sleep(struct chunk *c, struct monster *mon) { bool woke_up = false; int stealth = player->state.skills[SKILL_STEALTH]; int player_noise = 1 << (30 - stealth); int notice = randint0(1024); struct monster_lore *lore = get_lore(mon->race); /* Aggravation */ if (player_of_has(player, OF_AGGRAVATE)) { char m_name[80]; /* Wake the monster */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); /* Get the monster name */ monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL | MDESC_IND_HID); /* Notify the player if aware */ if (monster_is_obvious(mon)) msg("%s wakes up.", m_name); woke_up = true; } else if ((notice * notice * notice) <= player_noise) { int sleep_reduction = 1; int local_noise = c->noise.grids[mon->fy][mon->fx]; /* Test - wake up faster in hearing distance of the player * Note no dependence on stealth for now */ if ((local_noise > 0) && (local_noise < 50)) { sleep_reduction = (100 / local_noise); } /* Note a complete wakeup */ if (mon->m_timed[MON_TMD_SLEEP] <= sleep_reduction) { woke_up = true; } /* Monster wakes up a bit */ mon_dec_timed(mon, MON_TMD_SLEEP, sleep_reduction, MON_TMD_FLG_NOTIFY, false); /* Update knowledge */ if (monster_is_obvious(mon)) { if (!woke_up && lore->ignore < UCHAR_MAX) lore->ignore++; else if (woke_up && lore->wake < UCHAR_MAX) lore->wake++; lore_update(mon->race, lore); } } }
/** * Process a monster's timed effects, e.g. decrease them. * * Returns true if the monster is skipping its turn. */ static bool process_monster_timed(struct chunk *c, struct monster *mon) { struct monster_lore *lore = get_lore(mon->race); /* Handle "sleep" */ if (mon->m_timed[MON_TMD_SLEEP]) { bool woke_up = false; /* Anti-stealth */ int notice = randint0(1024); /* Aggravation */ if (player_of_has(player, OF_AGGRAVATE)) { char m_name[80]; /* Wake the monster */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); /* Get the monster name */ monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL | MDESC_IND_HID); /* Notify the player if aware */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) && !mflag_has(mon->mflag, MFLAG_UNAWARE)) msg("%s wakes up.", m_name); woke_up = true; } else if ((notice * notice * notice) <= player->state.noise) { /* See if monster "notices" player */ int d = 1; /* Wake up faster near the player */ if (mon->cdis < 50) d = (100 / mon->cdis); /* Note a complete wakeup */ if (mon->m_timed[MON_TMD_SLEEP] <= d) woke_up = true; /* Monster wakes up a bit */ mon_dec_timed(mon, MON_TMD_SLEEP, d, MON_TMD_FLG_NOTIFY, false); /* Update knowledge */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) && !mflag_has(mon->mflag, MFLAG_UNAWARE)) { if (!woke_up && lore->ignore < UCHAR_MAX) lore->ignore++; else if (woke_up && lore->wake < UCHAR_MAX) lore->wake++; lore_update(mon->race, lore); } } /* Sleeping monsters don't recover in any other ways */ /* If the monster just woke up, then it doesn't act */ return true; } if (mon->m_timed[MON_TMD_FAST]) mon_dec_timed(mon, MON_TMD_FAST, 1, 0, false); if (mon->m_timed[MON_TMD_SLOW]) mon_dec_timed(mon, MON_TMD_SLOW, 1, 0, false); if (mon->m_timed[MON_TMD_STUN]) { int d = 1; /* Make a "saving throw" against stun */ if (randint0(5000) <= mon->race->level * mon->race->level) /* Recover fully */ d = mon->m_timed[MON_TMD_STUN]; /* Hack -- Recover from stun */ mon_dec_timed(mon, MON_TMD_STUN, d, MON_TMD_FLG_NOTIFY, false); } if (mon->m_timed[MON_TMD_CONF]) { int d = randint1(mon->race->level / 10 + 1); mon_dec_timed(mon, MON_TMD_CONF, d, MON_TMD_FLG_NOTIFY, false); } if (mon->m_timed[MON_TMD_FEAR]) { int d = randint1(mon->race->level / 10 + 1); mon_dec_timed(mon, MON_TMD_FEAR, d, MON_TMD_FLG_NOTIFY, false); } /* Don't do anything if stunned */ return mon->m_timed[MON_TMD_STUN] ? true : false; }
/** * Process a monster's timed effects, e.g. decrease them. * * Returns TRUE if the monster is skipping its turn. */ static bool process_monster_timed(struct chunk *c, struct monster *m_ptr) { monster_lore *l_ptr = get_lore(m_ptr->race); /* Handle "sleep" */ if (m_ptr->m_timed[MON_TMD_SLEEP]) { bool woke_up = FALSE; /* Anti-stealth */ int notice = randint0(1024); /* Aggravation */ if (player_of_has(player, OF_AGGRAVATE)) { /* Wake the monster and notify player */ mon_clear_timed(m_ptr, MON_TMD_SLEEP, MON_TMD_FLG_NOTIFY, FALSE); woke_up = TRUE; /* Hack See if monster "notices" player */ } else if ((notice * notice * notice) <= player->state.noise) { int d = 1; /* Wake up faster near the player */ if (m_ptr->cdis < 50) d = (100 / m_ptr->cdis); /* Note a complete wakeup */ if (m_ptr->m_timed[MON_TMD_SLEEP] <= d) woke_up = TRUE; /* Monster wakes up a bit */ mon_dec_timed(m_ptr, MON_TMD_SLEEP, d, MON_TMD_FLG_NOTIFY, FALSE); /* Update knowledge */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && !mflag_has(m_ptr->mflag, MFLAG_UNAWARE)) { if (!woke_up && l_ptr->ignore < MAX_UCHAR) l_ptr->ignore++; else if (woke_up && l_ptr->wake < MAX_UCHAR) l_ptr->wake++; lore_update(m_ptr->race, l_ptr); } } /* Update the health bar */ if (woke_up && mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && !mflag_has(m_ptr->mflag, MFLAG_UNAWARE) && player->upkeep->health_who == m_ptr) player->upkeep->redraw |= (PR_HEALTH); /* Sleeping monsters don't recover in any other ways */ /* If the monster just woke up, then it doesn't act */ return TRUE; } if (m_ptr->m_timed[MON_TMD_FAST]) mon_dec_timed(m_ptr, MON_TMD_FAST, 1, 0, FALSE); if (m_ptr->m_timed[MON_TMD_SLOW]) mon_dec_timed(m_ptr, MON_TMD_SLOW, 1, 0, FALSE); if (m_ptr->m_timed[MON_TMD_STUN]) { int d = 1; /* Make a "saving throw" against stun */ if (randint0(5000) <= m_ptr->race->level * m_ptr->race->level) /* Recover fully */ d = m_ptr->m_timed[MON_TMD_STUN]; /* Hack -- Recover from stun */ mon_dec_timed(m_ptr, MON_TMD_STUN, d, MON_TMD_FLG_NOTIFY, FALSE); } if (m_ptr->m_timed[MON_TMD_CONF]) { int d = randint1(m_ptr->race->level / 10 + 1); mon_dec_timed(m_ptr, MON_TMD_CONF, d, MON_TMD_FLG_NOTIFY, FALSE); } if (m_ptr->m_timed[MON_TMD_FEAR]) { int d = randint1(m_ptr->race->level / 10 + 1); mon_dec_timed(m_ptr, MON_TMD_FEAR, d, MON_TMD_FLG_NOTIFY, FALSE); } /* Don't do anything if stunned */ return m_ptr->m_timed[MON_TMD_STUN] ? TRUE : FALSE; }
/** * 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); }
/** * 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; }
/** * 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 *mon, int dam, bool *fear, const char *note) { s32b div, new_exp, new_exp_frac; struct monster_lore *lore = get_lore(mon->race); /* Redraw (later) if needed */ if (player->upkeep->health_who == mon) player->upkeep->redraw |= (PR_HEALTH); /* Wake it up */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); /* Become aware of its presence */ if (mflag_has(mon->mflag, MFLAG_UNAWARE)) become_aware(mon); /* Hurt it */ mon->hp -= dam; /* It is dead now */ if (mon->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(mon->race->flags, RF_UNIQUE)) { if (mon->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), mon, MDESC_DEFAULT); /* Death message */ if (note) { if (strlen(note) <= 1) { /* Death by Spell attack - messages handled by project_m() */ } else { char *str = format("%s%s", m_name, note); my_strcap(str); /* Make sure to flush any monster messages first */ notice_stuff(player); /* Death by Missile attack */ msgt(soundfx, "%s", str); } } else { /* Make sure to flush any monster messages first */ notice_stuff(player); if (!mflag_has(mon->mflag, MFLAG_VISIBLE)) /* Death by physical attack -- invisible monster */ msgt(soundfx, "You have killed %s.", m_name); else if (monster_is_unusual(mon->race)) /* Death by Physical attack -- non-living monster */ msgt(soundfx, "You have destroyed %s.", m_name); else /* Death by Physical attack -- living monster */ msgt(soundfx, "You have slain %s.", m_name); } /* Player level */ div = player->lev; /* Give some experience for the kill */ new_exp = ((long)mon->race->mexp * mon->race->level) / div; /* Handle fractional experience */ new_exp_frac = ((((long)mon->race->mexp * mon->race->level) % div) * 0x10000L / div) + player->exp_frac; /* Keep track of experience */ if (new_exp_frac >= 0x10000L) { new_exp++; player->exp_frac = (u16b)(new_exp_frac - 0x10000L); } else player->exp_frac = (u16b)new_exp_frac; /* When the player kills a Unique, it stays dead */ if (rf_has(mon->race->flags, RF_UNIQUE)) { char unique_name[80]; mon->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), mon, MDESC_DIED_FROM); /* Log the slaying of a unique */ strnfmt(buf, sizeof(buf), "Killed %s", unique_name); history_add(buf, HIST_SLAY_UNIQUE, 0); } /* Gain experience */ player_exp_gain(player, new_exp); /* Generate treasure */ monster_death(mon, false); /* Recall even invisible uniques or winners */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) || rf_has(mon->race->flags, RF_UNIQUE)) { /* Count kills this life */ if (lore->pkills < SHRT_MAX) lore->pkills++; /* Count kills in all lives */ if (lore->tkills < SHRT_MAX) lore->tkills++; /* Update lore and tracking */ lore_update(mon->race, lore); monster_race_track(player->upkeep, mon->race); } /* Delete the monster */ delete_monster_idx(mon->midx); /* Not afraid */ (*fear) = false; /* Monster is dead */ return (true); } /* Mega-Hack -- Pain cancels fear */ if (!(*fear) && mon->m_timed[MON_TMD_FEAR] && (dam > 0)) { int tmp = randint1(dam); /* Cure a little or all fear */ if (tmp < mon->m_timed[MON_TMD_FEAR]) { /* Reduce fear */ mon_dec_timed(mon, MON_TMD_FEAR, tmp, MON_TMD_FLG_NOMESSAGE, false); } else { /* Cure fear */ mon_clear_timed(mon, MON_TMD_FEAR, MON_TMD_FLG_NOMESSAGE, false); /* No more fear */ (*fear) = false; } } /* Sometimes a monster gets scared by damage */ if (!mon->m_timed[MON_TMD_FEAR] && !rf_has(mon->race->flags, RF_NO_FEAR) && dam > 0) { int percentage; /* Percentage of fully healthy */ percentage = (100L * mon->hp) / mon->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 >= mon->hp) && (randint0(100) < 80))) { int timer = randint1(10) + (((dam >= mon->hp) && (percentage > 7)) ? 20 : ((11 - percentage) * 5)); /* Hack -- note fear */ (*fear) = true; mon_inc_timed(mon, MON_TMD_FEAR, timer, MON_TMD_FLG_NOMESSAGE | MON_TMD_FLG_NOFAIL, false); } } /* Not dead yet */ return (false); }