1 #include "monster/monster-status.h"
2 #include "autopick/autopick-pref-processor.h"
3 #include "core/player-update-types.h"
4 #include "core/speed-table.h"
5 #include "floor/cave.h"
6 #include "floor/geometry.h"
7 #include "game-option/birth-options.h"
8 #include "game-option/text-display-options.h"
10 #include "monster-race/monster-kind-mask.h"
11 #include "monster-race/monster-race.h"
12 #include "monster-race/race-flags-resistance.h"
13 #include "monster-race/race-flags1.h"
14 #include "monster-race/race-flags2.h"
15 #include "monster-race/race-flags3.h"
16 #include "monster/monster-describer.h"
17 #include "monster/monster-info.h"
18 #include "monster/monster-list.h"
19 #include "monster/monster-status-setter.h" //!< @todo 相互依存. 後で何とかする.
20 #include "monster/monster-update.h"
21 #include "system/floor-type-definition.h"
22 #include "system/monster-race-definition.h"
23 #include "system/monster-type-definition.h"
24 #include "system/player-type-definition.h"
25 #include "timed-effect/player-hallucination.h"
26 #include "timed-effect/timed-effects.h"
27 #include "util/bit-flags-calculator.h"
28 #include "view/display-messages.h"
29 #include "world/world.h"
33 #include "monster/monster-description-types.h"
36 static uint32_t csleep_noise;
39 * @brief モンスターIDからPOWERFULフラグの有無を取得する /
40 * @param floor_ptr 現在フロアへの参照ポインタ
41 * @param m_idx モンスターID
42 * @return POWERFULフラグがあればTRUE、なければFALSEを返す。
44 bool monster_is_powerful(floor_type *floor_ptr, MONSTER_IDX m_idx)
46 auto *m_ptr = &floor_ptr->m_list[m_idx];
47 auto *r_ptr = &r_info[m_ptr->r_idx];
48 return any_bits(r_ptr->flags2, RF2_POWERFUL);
52 * @brief モンスターIDからモンスターのレベルを取得する(ただし最低1を保証する) /
53 * @param m_idx モンスターID
56 DEPTH monster_level_idx(floor_type *floor_ptr, MONSTER_IDX m_idx)
58 auto *m_ptr = &floor_ptr->m_list[m_idx];
59 auto *r_ptr = &r_info[m_ptr->r_idx];
60 return (r_ptr->level >= 1) ? r_ptr->level : 1;
64 * @brief モンスターに与えたダメージの修正処理 /
65 * Modify the physical damage done to the monster.
66 * @param player_ptr プレイヤーへの参照ポインタ
67 * @param m_ptr ダメージを受けるモンスターの構造体参照ポインタ
69 * @param is_psy_spear 攻撃手段が光の剣ならばTRUE
70 * @return 修正を行った結果のダメージ量
71 * @details RES_ALL持ちはAC軽減後のダメージを1/100に補正する. 光の剣は無敵を無効化する. 一定確率で無敵は貫通できる.
73 int mon_damage_mod(PlayerType *player_ptr, monster_type *m_ptr, int dam, bool is_psy_spear)
75 auto *r_ptr = &r_info[m_ptr->r_idx];
76 if (r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_ALL) && dam > 0) {
78 if ((dam == 0) && one_in_(3)) {
83 if (!monster_invulner_remaining(m_ptr)) {
88 if (!player_ptr->blind && is_seen(player_ptr, m_ptr)) {
89 msg_print(_("バリアを切り裂いた!", "The barrier is penetrated!"));
95 return one_in_(PENETRATE_INVULNERABILITY) ? dam : 0;
99 * @brief モンスターの時限ステータスを取得する
100 * @param floor_ptr 現在フロアへの参照ポインタ
101 * @return m_idx モンスターの参照ID
102 * @return mproc_type モンスターの時限ステータスID
105 int get_mproc_idx(floor_type *floor_ptr, MONSTER_IDX m_idx, int mproc_type)
107 const auto &cur_mproc_list = floor_ptr->mproc_list[mproc_type];
108 for (int i = floor_ptr->mproc_max[mproc_type] - 1; i >= 0; i--) {
109 if (cur_mproc_list[i] == m_idx) {
118 * @brief モンスターの時限ステータスリストを追加する
119 * @param floor_ptr 現在フロアへの参照ポインタ
120 * @return m_idx モンスターの参照ID
121 * @return mproc_type 追加したいモンスターの時限ステータスID
123 void mproc_add(floor_type *floor_ptr, MONSTER_IDX m_idx, int mproc_type)
125 if (floor_ptr->mproc_max[mproc_type] < w_ptr->max_m_idx) {
126 floor_ptr->mproc_list[mproc_type][floor_ptr->mproc_max[mproc_type]++] = (int16_t)m_idx;
131 * @brief モンスターの時限ステータスリストを初期化する / Initialize monster process
132 * @param floor_ptr 現在フロアへの参照ポインタ
134 void mproc_init(floor_type *floor_ptr)
136 /* Reset "player_ptr->current_floor_ptr->mproc_max[]" */
137 for (int i = 0; i < MAX_MTIMED; i++) {
138 floor_ptr->mproc_max[i] = 0;
141 /* Process the monsters (backwards) */
142 for (MONSTER_IDX i = floor_ptr->m_max - 1; i >= 1; i--) {
143 auto *m_ptr = &floor_ptr->m_list[i];
145 /* Ignore "dead" monsters */
146 if (!monster_is_valid(m_ptr)) {
150 for (int cmi = 0; cmi < MAX_MTIMED; cmi++) {
151 if (m_ptr->mtimed[cmi]) {
152 mproc_add(floor_ptr, i, cmi);
159 * @brief モンスターの各種状態値を時間経過により更新するサブルーチン
160 * @param floor_ptr 現在フロアへの参照ポインタ
161 * @param m_idx モンスター参照ID
162 * @param mtimed_idx 更新するモンスターの時限ステータスID
164 static void process_monsters_mtimed_aux(PlayerType *player_ptr, MONSTER_IDX m_idx, int mtimed_idx)
166 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
167 switch (mtimed_idx) {
168 case MTIMED_CSLEEP: {
169 auto *r_ptr = &r_info[m_ptr->r_idx];
170 auto is_wakeup = false;
171 if (m_ptr->cdis < AAF_LIMIT) {
172 /* Handle "sensing radius" */
173 if (m_ptr->cdis <= (is_pet(m_ptr) ? ((r_ptr->aaf > MAX_SIGHT) ? MAX_SIGHT : r_ptr->aaf) : r_ptr->aaf)) {
177 /* Handle "sight" and "aggravation" */
178 else if ((m_ptr->cdis <= MAX_SIGHT) && (player_has_los_bold(player_ptr, m_ptr->fy, m_ptr->fx))) {
187 auto notice = (uint32_t)randint0(1024);
189 /* Nightmare monsters are more alert */
190 if (ironman_nightmare) {
194 /* Hack -- See if monster "notices" player */
195 if ((notice * notice * notice) > csleep_noise) {
199 /* Hack -- amount of "waking" */
200 /* Wake up faster near the player */
201 auto d = (m_ptr->cdis < AAF_LIMIT / 2) ? (AAF_LIMIT / m_ptr->cdis) : 1;
203 /* Hack -- amount of "waking" is affected by speed of player */
204 d = (d * speed_to_energy(player_ptr->pspeed)) / 10;
209 /* Monster wakes up "a little bit" */
212 if (!set_monster_csleep(player_ptr, m_idx, monster_csleep_remaining(m_ptr) - d)) {
213 /* Notice the "not waking up" */
214 if (is_original_ap_and_seen(player_ptr, m_ptr)) {
215 /* Hack -- Count the ignores */
216 if (r_ptr->r_ignore < MAX_UCHAR) {
224 /* Notice the "waking up" */
226 GAME_TEXT m_name[MAX_NLEN];
227 monster_desc(player_ptr, m_name, m_ptr, 0);
228 msg_format(_("%^sが目を覚ました。", "%^s wakes up."), m_name);
231 if (is_original_ap_and_seen(player_ptr, m_ptr)) {
232 /* Hack -- Count the wakings */
233 if (r_ptr->r_wake < MAX_UCHAR) {
242 /* Reduce by one, note if expires */
243 if (set_monster_fast(player_ptr, m_idx, monster_fast_remaining(m_ptr) - 1)) {
244 if (is_seen(player_ptr, m_ptr)) {
245 GAME_TEXT m_name[MAX_NLEN];
246 monster_desc(player_ptr, m_name, m_ptr, 0);
247 msg_format(_("%^sはもう加速されていない。", "%^s is no longer fast."), m_name);
254 /* Reduce by one, note if expires */
255 if (set_monster_slow(player_ptr, m_idx, monster_slow_remaining(m_ptr) - 1)) {
256 if (is_seen(player_ptr, m_ptr)) {
257 GAME_TEXT m_name[MAX_NLEN];
258 monster_desc(player_ptr, m_name, m_ptr, 0);
259 msg_format(_("%^sはもう減速されていない。", "%^s is no longer slow."), m_name);
265 case MTIMED_STUNNED: {
266 int rlev = r_info[m_ptr->r_idx].level;
268 /* Recover from stun */
269 if (set_monster_stunned(player_ptr, m_idx, (randint0(10000) <= rlev * rlev) ? 0 : (monster_stunned_remaining(m_ptr) - 1))) {
270 /* Message if visible */
271 if (is_seen(player_ptr, m_ptr)) {
272 GAME_TEXT m_name[MAX_NLEN];
273 monster_desc(player_ptr, m_name, m_ptr, 0);
274 msg_format(_("%^sは朦朧状態から立ち直った。", "%^s is no longer stunned."), m_name);
281 case MTIMED_CONFUSED: {
282 /* Reduce the confusion */
283 if (!set_monster_confused(player_ptr, m_idx, monster_confused_remaining(m_ptr) - randint1(r_info[m_ptr->r_idx].level / 20 + 1))) {
287 /* Message if visible */
288 if (is_seen(player_ptr, m_ptr)) {
289 GAME_TEXT m_name[MAX_NLEN];
290 monster_desc(player_ptr, m_name, m_ptr, 0);
291 msg_format(_("%^sは混乱から立ち直った。", "%^s is no longer confused."), m_name);
297 case MTIMED_MONFEAR: {
298 /* Reduce the fear */
299 if (!set_monster_monfear(player_ptr, m_idx, monster_fear_remaining(m_ptr) - randint1(r_info[m_ptr->r_idx].level / 20 + 1))) {
304 if (is_seen(player_ptr, m_ptr)) {
305 GAME_TEXT m_name[MAX_NLEN];
310 /* Acquire the monster possessive */
311 monster_desc(player_ptr, m_poss, m_ptr, MD_PRON_VISIBLE | MD_POSSESSIVE);
313 monster_desc(player_ptr, m_name, m_ptr, 0);
315 msg_format("%^sは勇気を取り戻した。", m_name);
317 msg_format("%^s recovers %s courage.", m_name, m_poss);
324 case MTIMED_INVULNER: {
325 /* Reduce by one, note if expires */
326 if (!set_monster_invulner(player_ptr, m_idx, monster_invulner_remaining(m_ptr) - 1, true)) {
330 if (is_seen(player_ptr, m_ptr)) {
331 GAME_TEXT m_name[MAX_NLEN];
332 monster_desc(player_ptr, m_name, m_ptr, 0);
333 msg_format(_("%^sはもう無敵でない。", "%^s is no longer invulnerable."), m_name);
342 * @brief 全モンスターの各種状態値を時間経過により更新するメインルーチン
343 * @param mtimed_idx 更新するモンスターの時限ステータスID
344 * @param player_ptr プレイヤーへの参照ポインタ
346 * Process the counters of monsters (once per 10 game turns)\n
347 * These functions are to process monsters' counters same as player's.
349 void process_monsters_mtimed(PlayerType *player_ptr, int mtimed_idx)
351 auto *floor_ptr = player_ptr->current_floor_ptr;
352 const auto &cur_mproc_list = floor_ptr->mproc_list[mtimed_idx];
354 /* Hack -- calculate the "player noise" */
355 if (mtimed_idx == MTIMED_CSLEEP) {
356 csleep_noise = (1U << (30 - player_ptr->skill_stl));
359 /* Process the monsters (backwards) */
360 for (auto i = floor_ptr->mproc_max[mtimed_idx] - 1; i >= 0; i--) {
361 process_monsters_mtimed_aux(player_ptr, cur_mproc_list[i], mtimed_idx);
366 * @brief モンスターへの魔力消去処理
367 * @param player_ptr プレイヤーへの参照ポインタ
368 * @param m_idx 魔力消去を受けるモンスターの参照ID
370 void dispel_monster_status(PlayerType *player_ptr, MONSTER_IDX m_idx)
372 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
373 GAME_TEXT m_name[MAX_NLEN];
375 monster_desc(player_ptr, m_name, m_ptr, 0);
376 if (set_monster_invulner(player_ptr, m_idx, 0, true)) {
378 msg_format(_("%sはもう無敵ではない。", "%^s is no longer invulnerable."), m_name);
382 if (set_monster_fast(player_ptr, m_idx, 0)) {
384 msg_format(_("%sはもう加速されていない。", "%^s is no longer fast."), m_name);
388 if (set_monster_slow(player_ptr, m_idx, 0)) {
390 msg_format(_("%sはもう減速されていない。", "%^s is no longer slow."), m_name);
396 * @brief モンスターの経験値取得処理
397 * @param player_ptr プレイヤーへの参照ポインタ
398 * @param m_idx 経験値を得るモンスターの参照ID
399 * @param s_idx 撃破されたモンスター種族の参照ID
401 void monster_gain_exp(PlayerType *player_ptr, MONSTER_IDX m_idx, MonsterRaceId s_idx)
403 if (m_idx <= 0 || !MonsterRace(s_idx).is_valid()) {
407 auto *floor_ptr = player_ptr->current_floor_ptr;
408 auto *m_ptr = &floor_ptr->m_list[m_idx];
410 if (!monster_is_valid(m_ptr)) {
414 auto *r_ptr = &r_info[m_ptr->r_idx];
415 auto *s_ptr = &r_info[s_idx];
417 if (player_ptr->phase_out || (r_ptr->next_exp == 0)) {
421 auto new_exp = s_ptr->mexp * s_ptr->level / (r_ptr->level + 2);
422 if (m_idx == player_ptr->riding) {
423 new_exp = (new_exp + 1) / 2;
426 if (!floor_ptr->dun_level) {
430 m_ptr->exp += new_exp;
431 if (m_ptr->mflag2.has(MonsterConstantFlagType::CHAMELEON)) {
435 if (m_ptr->exp < r_ptr->next_exp) {
436 if (m_idx == player_ptr->riding) {
437 player_ptr->update |= PU_BONUS;
443 GAME_TEXT m_name[MAX_NLEN];
444 auto old_hp = m_ptr->hp;
445 auto old_maxhp = m_ptr->max_maxhp;
446 auto old_r_idx = m_ptr->r_idx;
447 auto old_sub_align = m_ptr->sub_align;
449 /* Hack -- Reduce the racial counter of previous monster */
450 real_r_ptr(m_ptr)->cur_num--;
452 monster_desc(player_ptr, m_name, m_ptr, 0);
453 m_ptr->r_idx = r_ptr->next_r_idx;
455 /* Count the monsters on the level */
456 real_r_ptr(m_ptr)->cur_num++;
458 m_ptr->ap_r_idx = m_ptr->r_idx;
459 r_ptr = &r_info[m_ptr->r_idx];
461 m_ptr->max_maxhp = any_bits(r_ptr->flags1, RF1_FORCE_MAXHP) ? maxroll(r_ptr->hdice, r_ptr->hside) : damroll(r_ptr->hdice, r_ptr->hside);
462 if (ironman_nightmare) {
463 auto hp = m_ptr->max_maxhp * 2;
464 m_ptr->max_maxhp = std::min(MONSTER_MAXHP, hp);
467 m_ptr->maxhp = m_ptr->max_maxhp;
468 m_ptr->hp = old_hp * m_ptr->maxhp / old_maxhp;
470 /* dealt damage is 0 at initial*/
471 m_ptr->dealt_damage = 0;
473 /* Extract the monster base speed */
474 m_ptr->mspeed = get_mspeed(floor_ptr, r_ptr);
476 /* Sub-alignment of a monster */
477 if (!is_pet(m_ptr) && r_ptr->kind_flags.has_none_of(alignment_mask)) {
478 m_ptr->sub_align = old_sub_align;
480 m_ptr->sub_align = SUB_ALIGN_NEUTRAL;
481 if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
482 m_ptr->sub_align |= SUB_ALIGN_EVIL;
485 if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
486 m_ptr->sub_align |= SUB_ALIGN_GOOD;
491 if (is_pet(m_ptr) || m_ptr->ml) {
492 auto is_hallucinated = player_ptr->effects()->hallucination()->is_hallucinated();
493 if (!ignore_unview || player_can_see_bold(player_ptr, m_ptr->fy, m_ptr->fx)) {
494 if (is_hallucinated) {
495 monster_race *hallucinated_race = nullptr;
497 auto r_idx = MonsterRace::pick_one_at_random();
498 hallucinated_race = &r_info[r_idx];
499 } while (hallucinated_race->name.empty() || hallucinated_race->kind_flags.has(MonsterKindType::UNIQUE));
500 auto mes_evolution = _("%sは%sに進化した。", "%^s evolved into %s.");
501 auto mes_degeneration = _("%sは%sに退化した。", "%^s degenerated into %s.");
502 auto mes = randint0(2) == 0 ? mes_evolution : mes_degeneration;
503 msg_format(mes, m_name, hallucinated_race->name.c_str());
505 msg_format(_("%sは%sに進化した。", "%^s evolved into %s."), m_name, r_ptr->name.c_str());
509 if (!is_hallucinated) {
510 r_info[old_r_idx].r_can_evolve = true;
513 /* Now you feel very close to this pet. */
514 m_ptr->parent_m_idx = 0;
517 update_monster(player_ptr, m_idx, false);
518 lite_spot(player_ptr, m_ptr->fy, m_ptr->fx);
520 if (m_idx == player_ptr->riding) {
521 player_ptr->update |= PU_BONUS;
525 bool monster_is_valid(monster_type *m_ptr)
527 return MonsterRace(m_ptr->r_idx).is_valid();
530 TIME_EFFECT monster_csleep_remaining(monster_type *m_ptr)
532 return m_ptr->mtimed[MTIMED_CSLEEP];
535 TIME_EFFECT monster_fast_remaining(monster_type *m_ptr)
537 return m_ptr->mtimed[MTIMED_FAST];
540 TIME_EFFECT monster_slow_remaining(monster_type *m_ptr)
542 return m_ptr->mtimed[MTIMED_SLOW];
545 TIME_EFFECT monster_stunned_remaining(monster_type *m_ptr)
547 return m_ptr->mtimed[MTIMED_STUNNED];
550 TIME_EFFECT monster_confused_remaining(monster_type *m_ptr)
552 return m_ptr->mtimed[MTIMED_CONFUSED];
555 TIME_EFFECT monster_fear_remaining(monster_type *m_ptr)
557 return m_ptr->mtimed[MTIMED_MONFEAR];
560 TIME_EFFECT monster_invulner_remaining(monster_type *m_ptr)
562 return m_ptr->mtimed[MTIMED_INVULNER];