/** * Allow monsters on a frozen persistent level to recover */ void restore_monsters(void) { int i; struct monster *mon; /* Get the number of turns that have passed */ int num_turns = turn - cave->turn; /* Process the monsters (backwards) */ for (i = cave_monster_max(cave) - 1; i >= 1; i--) { int status, status_red; /* Access the monster */ mon = cave_monster(cave, i); /* Regenerate */ regen_monster(mon, num_turns / 100); /* Handle timed effects */ status_red = num_turns * turn_energy(mon->mspeed) / z_info->move_energy; if (status_red > 0) { for (status = 0; status < MON_TMD_MAX; status++) { if (mon->m_timed[status]) { mon_dec_timed(mon, status, status_red, 0, false); } } } } }
/** * 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) { /* If the monster is asleep or just woke up, then it doesn't act */ if (mon->m_timed[MON_TMD_SLEEP]) { monster_reduce_sleep(c, mon); 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_HOLD]) mon_dec_timed(mon, MON_TMD_HOLD, 1, 0, false); if (mon->m_timed[MON_TMD_DISEN]) mon_dec_timed(mon, MON_TMD_DISEN, 1, 0, false); if (mon->m_timed[MON_TMD_STUN]) mon_dec_timed(mon, MON_TMD_STUN, 1, MON_TMD_FLG_NOTIFY, false); if (mon->m_timed[MON_TMD_CONF]) { mon_dec_timed(mon, MON_TMD_CONF, 1, 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); } /* One in __ chance of missing turn if stunned, always miss if held * or commanded */ if (mon->m_timed[MON_TMD_STUN]) { return randint0(STUN_MISS_CHANCE) == 1; } else if (mon->m_timed[MON_TMD_HOLD] || mon->m_timed[MON_TMD_COMMAND]) { return true; } else { return 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 *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; }
/** * 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); }