OSDN Git Service

Merge branch 'master' of https://github.com/hengband/hengband
[hengbandforosx/hengbandosx.git] / src / monster / monster-status.cpp
1 #include "monster/monster-status.h"
2 #include "autopick/autopick-pref-processor.h"
3 #include "core/speed-table.h"
4 #include "floor/cave.h"
5 #include "floor/geometry.h"
6 #include "game-option/birth-options.h"
7 #include "game-option/text-display-options.h"
8 #include "grid/grid.h"
9 #include "monster-race/monster-kind-mask.h"
10 #include "monster-race/monster-race.h"
11 #include "monster-race/race-flags-resistance.h"
12 #include "monster-race/race-flags1.h"
13 #include "monster-race/race-flags2.h"
14 #include "monster-race/race-flags3.h"
15 #include "monster/monster-describer.h"
16 #include "monster/monster-info.h"
17 #include "monster/monster-list.h"
18 #include "monster/monster-status-setter.h" //!< @todo 相互依存. 後で何とかする.
19 #include "monster/monster-update.h"
20 #include "system/floor-type-definition.h"
21 #include "system/monster-entity.h"
22 #include "system/monster-race-info.h"
23 #include "system/player-type-definition.h"
24 #include "system/redrawing-flags-updater.h"
25 #include "timed-effect/player-blindness.h"
26 #include "timed-effect/player-hallucination.h"
27 #include "timed-effect/timed-effects.h"
28 #include "util/bit-flags-calculator.h"
29 #include "view/display-messages.h"
30 #include "world/world.h"
31
32 #if JP
33 #else
34 #include "monster/monster-description-types.h"
35 #endif
36
37 static uint32_t csleep_noise;
38
39 /*!
40  * @brief モンスターIDからPOWERFULフラグの有無を取得する /
41  * @param floor_ptr 現在フロアへの参照ポインタ
42  * @param m_idx モンスターID
43  * @return POWERFULフラグがあればTRUE、なければFALSEを返す。
44  */
45 bool monster_is_powerful(FloorType *floor_ptr, MONSTER_IDX m_idx)
46 {
47     auto *m_ptr = &floor_ptr->m_list[m_idx];
48     auto *r_ptr = &monraces_info[m_ptr->r_idx];
49     return any_bits(r_ptr->flags2, RF2_POWERFUL);
50 }
51
52 /*!
53  * @brief モンスターIDからモンスターのレベルを取得する(ただし最低1を保証する) /
54  * @param m_idx モンスターID
55  * @return モンスターのレベル
56  */
57 DEPTH monster_level_idx(FloorType *floor_ptr, MONSTER_IDX m_idx)
58 {
59     auto *m_ptr = &floor_ptr->m_list[m_idx];
60     auto *r_ptr = &monraces_info[m_ptr->r_idx];
61     return (r_ptr->level >= 1) ? r_ptr->level : 1;
62 }
63
64 /*!
65  * @brief モンスターに与えたダメージの修正処理 /
66  * Modify the physical damage done to the monster.
67  * @param player_ptr プレイヤーへの参照ポインタ
68  * @param m_ptr ダメージを受けるモンスターの構造体参照ポインタ
69  * @param dam ダメージ基本値
70  * @param is_psy_spear 攻撃手段が光の剣ならばTRUE
71  * @return 修正を行った結果のダメージ量
72  * @details RES_ALL持ちはAC軽減後のダメージを1/100に補正する. 光の剣は無敵を無効化する. 一定確率で無敵は貫通できる.
73  */
74 int mon_damage_mod(PlayerType *player_ptr, MonsterEntity *m_ptr, int dam, bool is_psy_spear)
75 {
76     auto *r_ptr = &monraces_info[m_ptr->r_idx];
77     if (r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_ALL) && dam > 0) {
78         dam /= 100;
79         if ((dam == 0) && one_in_(3)) {
80             dam = 1;
81         }
82     }
83
84     if (!m_ptr->is_invulnerable()) {
85         return dam;
86     }
87
88     if (is_psy_spear) {
89         if (!player_ptr->effects()->blindness()->is_blind() && is_seen(player_ptr, m_ptr)) {
90             msg_print(_("バリアを切り裂いた!", "The barrier is penetrated!"));
91         }
92
93         return dam;
94     }
95
96     return one_in_(PENETRATE_INVULNERABILITY) ? dam : 0;
97 }
98
99 /*!
100  * @brief モンスターの時限ステータスを取得する
101  * @param floor_ptr 現在フロアへの参照ポインタ
102  * @return m_idx モンスターの参照ID
103  * @return mproc_type モンスターの時限ステータスID
104  * @return 残りターン値
105  */
106 int get_mproc_idx(FloorType *floor_ptr, MONSTER_IDX m_idx, int mproc_type)
107 {
108     const auto &cur_mproc_list = floor_ptr->mproc_list[mproc_type];
109     for (int i = floor_ptr->mproc_max[mproc_type] - 1; i >= 0; i--) {
110         if (cur_mproc_list[i] == m_idx) {
111             return i;
112         }
113     }
114
115     return -1;
116 }
117
118 /*!
119  * @brief モンスターの時限ステータスリストを追加する
120  * @param floor_ptr 現在フロアへの参照ポインタ
121  * @return m_idx モンスターの参照ID
122  * @return mproc_type 追加したいモンスターの時限ステータスID
123  */
124 void mproc_add(FloorType *floor_ptr, MONSTER_IDX m_idx, int mproc_type)
125 {
126     if (floor_ptr->mproc_max[mproc_type] < w_ptr->max_m_idx) {
127         floor_ptr->mproc_list[mproc_type][floor_ptr->mproc_max[mproc_type]++] = (int16_t)m_idx;
128     }
129 }
130
131 /*!
132  * @brief モンスターの時限ステータスリストを初期化する / Initialize monster process
133  * @param floor_ptr 現在フロアへの参照ポインタ
134  */
135 void mproc_init(FloorType *floor_ptr)
136 {
137     /* Reset "player_ptr->current_floor_ptr->mproc_max[]" */
138     for (int i = 0; i < MAX_MTIMED; i++) {
139         floor_ptr->mproc_max[i] = 0;
140     }
141
142     /* Process the monsters (backwards) */
143     for (MONSTER_IDX i = floor_ptr->m_max - 1; i >= 1; i--) {
144         auto *m_ptr = &floor_ptr->m_list[i];
145
146         /* Ignore "dead" monsters */
147         if (!m_ptr->is_valid()) {
148             continue;
149         }
150
151         for (int cmi = 0; cmi < MAX_MTIMED; cmi++) {
152             if (m_ptr->mtimed[cmi]) {
153                 mproc_add(floor_ptr, i, cmi);
154             }
155         }
156     }
157 }
158
159 /*!
160  * @brief モンスターの各種状態値を時間経過により更新するサブルーチン
161  * @param floor_ptr 現在フロアへの参照ポインタ
162  * @param m_idx モンスター参照ID
163  * @param mtimed_idx 更新するモンスターの時限ステータスID
164  */
165 static void process_monsters_mtimed_aux(PlayerType *player_ptr, MONSTER_IDX m_idx, int mtimed_idx)
166 {
167     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
168     switch (mtimed_idx) {
169     case MTIMED_CSLEEP: {
170         auto *r_ptr = &monraces_info[m_ptr->r_idx];
171         auto is_wakeup = false;
172         if (m_ptr->cdis < MAX_MONSTER_SENSING) {
173             /* Handle "sensing radius" */
174             if (m_ptr->cdis <= (m_ptr->is_pet() ? ((r_ptr->aaf > MAX_PLAYER_SIGHT) ? MAX_PLAYER_SIGHT : r_ptr->aaf) : r_ptr->aaf)) {
175                 is_wakeup = true;
176             }
177
178             /* Handle "sight" and "aggravation" */
179             else if ((m_ptr->cdis <= MAX_PLAYER_SIGHT) && (player_has_los_bold(player_ptr, m_ptr->fy, m_ptr->fx))) {
180                 is_wakeup = true;
181             }
182         }
183
184         if (!is_wakeup) {
185             break;
186         }
187
188         auto notice = (uint32_t)randint0(1024);
189
190         /* Nightmare monsters are more alert */
191         if (ironman_nightmare) {
192             notice /= 2;
193         }
194
195         /* Hack -- See if monster "notices" player */
196         if ((notice * notice * notice) > csleep_noise) {
197             break;
198         }
199
200         /* Hack -- amount of "waking" */
201         /* Wake up faster near the player */
202         auto d = (m_ptr->cdis < MAX_MONSTER_SENSING / 2) ? (MAX_MONSTER_SENSING / m_ptr->cdis) : 1;
203
204         /* Hack -- amount of "waking" is affected by speed of player */
205         d = (d * speed_to_energy(player_ptr->pspeed)) / 10;
206         if (d < 0) {
207             d = 1;
208         }
209
210         /* Monster wakes up "a little bit" */
211
212         /* Still asleep */
213         if (!set_monster_csleep(player_ptr, m_idx, m_ptr->get_remaining_sleep() - d)) {
214             /* Notice the "not waking up" */
215             if (is_original_ap_and_seen(player_ptr, m_ptr)) {
216                 /* Hack -- Count the ignores */
217                 if (r_ptr->r_ignore < MAX_UCHAR) {
218                     r_ptr->r_ignore++;
219                 }
220             }
221
222             break;
223         }
224
225         /* Notice the "waking up" */
226         if (m_ptr->ml) {
227             const auto m_name = monster_desc(player_ptr, m_ptr, 0);
228             msg_format(_("%s^が目を覚ました。", "%s^ wakes up."), m_name.data());
229         }
230
231         if (is_original_ap_and_seen(player_ptr, m_ptr)) {
232             /* Hack -- Count the wakings */
233             if (r_ptr->r_wake < MAX_UCHAR) {
234                 r_ptr->r_wake++;
235             }
236         }
237
238         break;
239     }
240
241     case MTIMED_FAST:
242         /* Reduce by one, note if expires */
243         if (set_monster_fast(player_ptr, m_idx, m_ptr->get_remaining_acceleration() - 1)) {
244             if (is_seen(player_ptr, m_ptr)) {
245                 const auto m_name = monster_desc(player_ptr, m_ptr, 0);
246                 msg_format(_("%s^はもう加速されていない。", "%s^ is no longer fast."), m_name.data());
247             }
248         }
249
250         break;
251
252     case MTIMED_SLOW:
253         /* Reduce by one, note if expires */
254         if (set_monster_slow(player_ptr, m_idx, m_ptr->get_remaining_deceleration() - 1)) {
255             if (is_seen(player_ptr, m_ptr)) {
256                 const auto m_name = monster_desc(player_ptr, m_ptr, 0);
257                 msg_format(_("%s^はもう減速されていない。", "%s^ is no longer slow."), m_name.data());
258             }
259         }
260
261         break;
262
263     case MTIMED_STUNNED: {
264         int rlev = monraces_info[m_ptr->r_idx].level;
265
266         /* Recover from stun */
267         if (set_monster_stunned(player_ptr, m_idx, (randint0(10000) <= rlev * rlev) ? 0 : (m_ptr->get_remaining_stun() - 1))) {
268             /* Message if visible */
269             if (is_seen(player_ptr, m_ptr)) {
270                 const auto m_name = monster_desc(player_ptr, m_ptr, 0);
271                 msg_format(_("%s^は朦朧状態から立ち直った。", "%s^ is no longer stunned."), m_name.data());
272             }
273         }
274
275         break;
276     }
277
278     case MTIMED_CONFUSED: {
279         /* Reduce the confusion */
280         if (!set_monster_confused(player_ptr, m_idx, m_ptr->get_remaining_confusion() - randint1(monraces_info[m_ptr->r_idx].level / 20 + 1))) {
281             break;
282         }
283
284         /* Message if visible */
285         if (is_seen(player_ptr, m_ptr)) {
286             const auto m_name = monster_desc(player_ptr, m_ptr, 0);
287             msg_format(_("%s^は混乱から立ち直った。", "%s^ is no longer confused."), m_name.data());
288         }
289
290         break;
291     }
292
293     case MTIMED_MONFEAR: {
294         /* Reduce the fear */
295         if (!set_monster_monfear(player_ptr, m_idx, m_ptr->get_remaining_fear() - randint1(monraces_info[m_ptr->r_idx].level / 20 + 1))) {
296             break;
297         }
298
299         /* Visual note */
300         if (is_seen(player_ptr, m_ptr)) {
301             const auto m_name = monster_desc(player_ptr, m_ptr, 0);
302 #ifdef JP
303 #else
304             /* Acquire the monster possessive */
305             const auto m_poss = monster_desc(player_ptr, m_ptr, MD_PRON_VISIBLE | MD_POSSESSIVE);
306 #endif
307 #ifdef JP
308             msg_format("%s^は勇気を取り戻した。", m_name.data());
309 #else
310             msg_format("%s^ recovers %s courage.", m_name.data(), m_poss.data());
311 #endif
312         }
313
314         break;
315     }
316
317     case MTIMED_INVULNER: {
318         /* Reduce by one, note if expires */
319         if (!set_monster_invulner(player_ptr, m_idx, m_ptr->get_remaining_invulnerability() - 1, true)) {
320             break;
321         }
322
323         if (is_seen(player_ptr, m_ptr)) {
324             const auto m_name = monster_desc(player_ptr, m_ptr, 0);
325             msg_format(_("%s^はもう無敵でない。", "%s^ is no longer invulnerable."), m_name.data());
326         }
327
328         break;
329     }
330     }
331 }
332
333 /*!
334  * @brief 全モンスターの各種状態値を時間経過により更新するメインルーチン
335  * @param mtimed_idx 更新するモンスターの時限ステータスID
336  * @param player_ptr プレイヤーへの参照ポインタ
337  * @details
338  * Process the counters of monsters (once per 10 game turns)\n
339  * These functions are to process monsters' counters same as player's.
340  */
341 void process_monsters_mtimed(PlayerType *player_ptr, int mtimed_idx)
342 {
343     auto *floor_ptr = player_ptr->current_floor_ptr;
344     const auto &cur_mproc_list = floor_ptr->mproc_list[mtimed_idx];
345
346     /* Hack -- calculate the "player noise" */
347     if (mtimed_idx == MTIMED_CSLEEP) {
348         csleep_noise = (1U << (30 - player_ptr->skill_stl));
349     }
350
351     /* Process the monsters (backwards) */
352     for (auto i = floor_ptr->mproc_max[mtimed_idx] - 1; i >= 0; i--) {
353         process_monsters_mtimed_aux(player_ptr, cur_mproc_list[i], mtimed_idx);
354     }
355 }
356
357 /*!
358  * @brief モンスターへの魔力消去処理
359  * @param player_ptr プレイヤーへの参照ポインタ
360  * @param m_idx 魔力消去を受けるモンスターの参照ID
361  */
362 void dispel_monster_status(PlayerType *player_ptr, MONSTER_IDX m_idx)
363 {
364     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
365     const auto m_name = monster_desc(player_ptr, m_ptr, 0);
366     if (set_monster_invulner(player_ptr, m_idx, 0, true)) {
367         if (m_ptr->ml) {
368             msg_format(_("%sはもう無敵ではない。", "%s^ is no longer invulnerable."), m_name.data());
369         }
370     }
371
372     if (set_monster_fast(player_ptr, m_idx, 0)) {
373         if (m_ptr->ml) {
374             msg_format(_("%sはもう加速されていない。", "%s^ is no longer fast."), m_name.data());
375         }
376     }
377
378     if (set_monster_slow(player_ptr, m_idx, 0)) {
379         if (m_ptr->ml) {
380             msg_format(_("%sはもう減速されていない。", "%s^ is no longer slow."), m_name.data());
381         }
382     }
383 }
384
385 /*!
386  * @brief モンスターの経験値取得処理
387  * @param player_ptr プレイヤーへの参照ポインタ
388  * @param m_idx 経験値を得るモンスターの参照ID
389  * @param s_idx 撃破されたモンスター種族の参照ID
390  */
391 void monster_gain_exp(PlayerType *player_ptr, MONSTER_IDX m_idx, MonsterRaceId s_idx)
392 {
393     if (m_idx <= 0 || !MonsterRace(s_idx).is_valid()) {
394         return;
395     }
396
397     auto *floor_ptr = player_ptr->current_floor_ptr;
398     auto *m_ptr = &floor_ptr->m_list[m_idx];
399
400     if (!m_ptr->is_valid()) {
401         return;
402     }
403
404     auto *r_ptr = &monraces_info[m_ptr->r_idx];
405     auto *s_ptr = &monraces_info[s_idx];
406
407     if (player_ptr->phase_out || (r_ptr->next_exp == 0)) {
408         return;
409     }
410
411     auto new_exp = s_ptr->mexp * s_ptr->level / (r_ptr->level + 2);
412     if (m_idx == player_ptr->riding) {
413         new_exp = (new_exp + 1) / 2;
414     }
415
416     if (!floor_ptr->dun_level) {
417         new_exp /= 5;
418     }
419
420     m_ptr->exp += new_exp;
421     if (m_ptr->mflag2.has(MonsterConstantFlagType::CHAMELEON)) {
422         return;
423     }
424
425     auto &rfu = RedrawingFlagsUpdater::get_instance();
426     if (m_ptr->exp < r_ptr->next_exp) {
427         if (m_idx == player_ptr->riding) {
428             rfu.set_flag(StatusRedrawingFlag::BONUS);
429         }
430
431         return;
432     }
433
434     auto old_hp = m_ptr->hp;
435     auto old_maxhp = m_ptr->max_maxhp;
436     auto old_r_idx = m_ptr->r_idx;
437     auto old_sub_align = m_ptr->sub_align;
438
439     /* Hack -- Reduce the racial counter of previous monster */
440     m_ptr->get_real_r_ref().cur_num--;
441
442     const auto m_name = monster_desc(player_ptr, m_ptr, 0);
443     m_ptr->r_idx = r_ptr->next_r_idx;
444
445     /* Count the monsters on the level */
446     m_ptr->get_real_r_ref().cur_num++;
447
448     m_ptr->ap_r_idx = m_ptr->r_idx;
449     r_ptr = &monraces_info[m_ptr->r_idx];
450
451     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);
452     if (ironman_nightmare) {
453         auto hp = m_ptr->max_maxhp * 2;
454         m_ptr->max_maxhp = std::min(MONSTER_MAXHP, hp);
455     }
456
457     m_ptr->maxhp = m_ptr->max_maxhp;
458     m_ptr->hp = old_hp * m_ptr->maxhp / old_maxhp;
459
460     /* dealt damage is 0 at initial*/
461     m_ptr->dealt_damage = 0;
462
463     /* Extract the monster base speed */
464     m_ptr->mspeed = get_mspeed(floor_ptr, r_ptr);
465
466     /* Sub-alignment of a monster */
467     if (!m_ptr->is_pet() && r_ptr->kind_flags.has_none_of(alignment_mask)) {
468         m_ptr->sub_align = old_sub_align;
469     } else {
470         m_ptr->sub_align = SUB_ALIGN_NEUTRAL;
471         if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
472             m_ptr->sub_align |= SUB_ALIGN_EVIL;
473         }
474
475         if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
476             m_ptr->sub_align |= SUB_ALIGN_GOOD;
477         }
478     }
479
480     m_ptr->exp = 0;
481     if (m_ptr->is_pet() || m_ptr->ml) {
482         auto is_hallucinated = player_ptr->effects()->hallucination()->is_hallucinated();
483         if (!ignore_unview || player_can_see_bold(player_ptr, m_ptr->fy, m_ptr->fx)) {
484             if (is_hallucinated) {
485                 MonsterRaceInfo *hallucinated_race = nullptr;
486                 do {
487                     auto r_idx = MonsterRace::pick_one_at_random();
488                     hallucinated_race = &monraces_info[r_idx];
489                 } while (hallucinated_race->name.empty() || hallucinated_race->kind_flags.has(MonsterKindType::UNIQUE));
490                 auto mes_evolution = _("%sは%sに進化した。", "%s^ evolved into %s.");
491                 auto mes_degeneration = _("%sは%sに退化した。", "%s^ degenerated into %s.");
492                 auto mes = randint0(2) == 0 ? mes_evolution : mes_degeneration;
493                 msg_format(mes, m_name.data(), hallucinated_race->name.data());
494             } else {
495                 msg_format(_("%sは%sに進化した。", "%s^ evolved into %s."), m_name.data(), r_ptr->name.data());
496             }
497         }
498
499         if (!is_hallucinated) {
500             monraces_info[old_r_idx].r_can_evolve = true;
501         }
502
503         /* Now you feel very close to this pet. */
504         m_ptr->parent_m_idx = 0;
505     }
506
507     update_monster(player_ptr, m_idx, false);
508     lite_spot(player_ptr, m_ptr->fy, m_ptr->fx);
509
510     if (m_idx == player_ptr->riding) {
511         rfu.set_flag(StatusRedrawingFlag::BONUS);
512     }
513 }