OSDN Git Service

3a710d7dca40b46806bc218da2b2512273dbe103
[hengbandforosx/hengbandosx.git] / src / monster / monster-status.cpp
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"
9 #include "grid/grid.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"
30
31 #if JP
32 #else
33 #include "monster/monster-description-types.h"
34 #endif
35
36 static uint32_t csleep_noise;
37
38 /*!
39  * @brief モンスターIDからPOWERFULフラグの有無を取得する /
40  * @param floor_ptr 現在フロアへの参照ポインタ
41  * @param m_idx モンスターID
42  * @return POWERFULフラグがあればTRUE、なければFALSEを返す。
43  */
44 bool monster_is_powerful(floor_type *floor_ptr, MONSTER_IDX m_idx)
45 {
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);
49 }
50
51 /*!
52  * @brief モンスターIDからモンスターのレベルを取得する(ただし最低1を保証する) /
53  * @param m_idx モンスターID
54  * @return モンスターのレベル
55  */
56 DEPTH monster_level_idx(floor_type *floor_ptr, MONSTER_IDX m_idx)
57 {
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;
61 }
62
63 /*!
64  * @brief モンスターに与えたダメージの修正処理 /
65  * Modify the physical damage done to the monster.
66  * @param player_ptr プレイヤーへの参照ポインタ
67  * @param m_ptr ダメージを受けるモンスターの構造体参照ポインタ
68  * @param dam ダメージ基本値
69  * @param is_psy_spear 攻撃手段が光の剣ならばTRUE
70  * @return 修正を行った結果のダメージ量
71  * @details RES_ALL持ちはAC軽減後のダメージを1/100に補正する. 光の剣は無敵を無効化する. 一定確率で無敵は貫通できる.
72  */
73 int mon_damage_mod(PlayerType *player_ptr, monster_type *m_ptr, int dam, bool is_psy_spear)
74 {
75     auto *r_ptr = &r_info[m_ptr->r_idx];
76     if (r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_ALL) && dam > 0) {
77         dam /= 100;
78         if ((dam == 0) && one_in_(3)) {
79             dam = 1;
80         }
81     }
82
83     if (!monster_invulner_remaining(m_ptr)) {
84         return dam;
85     }
86
87     if (is_psy_spear) {
88         if (!player_ptr->blind && is_seen(player_ptr, m_ptr)) {
89             msg_print(_("バリアを切り裂いた!", "The barrier is penetrated!"));
90         }
91
92         return dam;
93     }
94
95     return one_in_(PENETRATE_INVULNERABILITY) ? dam : 0;
96 }
97
98 /*!
99  * @brief モンスターの時限ステータスを取得する
100  * @param floor_ptr 現在フロアへの参照ポインタ
101  * @return m_idx モンスターの参照ID
102  * @return mproc_type モンスターの時限ステータスID
103  * @return 残りターン値
104  */
105 int get_mproc_idx(floor_type *floor_ptr, MONSTER_IDX m_idx, int mproc_type)
106 {
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) {
110             return i;
111         }
112     }
113
114     return -1;
115 }
116
117 /*!
118  * @brief モンスターの時限ステータスリストを追加する
119  * @param floor_ptr 現在フロアへの参照ポインタ
120  * @return m_idx モンスターの参照ID
121  * @return mproc_type 追加したいモンスターの時限ステータスID
122  */
123 void mproc_add(floor_type *floor_ptr, MONSTER_IDX m_idx, int mproc_type)
124 {
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;
127     }
128 }
129
130 /*!
131  * @brief モンスターの時限ステータスリストを初期化する / Initialize monster process
132  * @param floor_ptr 現在フロアへの参照ポインタ
133  */
134 void mproc_init(floor_type *floor_ptr)
135 {
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;
139     }
140
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];
144
145         /* Ignore "dead" monsters */
146         if (!monster_is_valid(m_ptr)) {
147             continue;
148         }
149
150         for (int cmi = 0; cmi < MAX_MTIMED; cmi++) {
151             if (m_ptr->mtimed[cmi]) {
152                 mproc_add(floor_ptr, i, cmi);
153             }
154         }
155     }
156 }
157
158 /*!
159  * @brief モンスターの各種状態値を時間経過により更新するサブルーチン
160  * @param floor_ptr 現在フロアへの参照ポインタ
161  * @param m_idx モンスター参照ID
162  * @param mtimed_idx 更新するモンスターの時限ステータスID
163  */
164 static void process_monsters_mtimed_aux(PlayerType *player_ptr, MONSTER_IDX m_idx, int mtimed_idx)
165 {
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)) {
174                 is_wakeup = true;
175             }
176
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))) {
179                 is_wakeup = true;
180             }
181         }
182
183         if (!is_wakeup) {
184             break;
185         }
186
187         auto notice = (uint32_t)randint0(1024);
188
189         /* Nightmare monsters are more alert */
190         if (ironman_nightmare) {
191             notice /= 2;
192         }
193
194         /* Hack -- See if monster "notices" player */
195         if ((notice * notice * notice) > csleep_noise) {
196             break;
197         }
198
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;
202
203         /* Hack -- amount of "waking" is affected by speed of player */
204         d = (d * speed_to_energy(player_ptr->pspeed)) / 10;
205         if (d < 0) {
206             d = 1;
207         }
208
209         /* Monster wakes up "a little bit" */
210
211         /* Still asleep */
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) {
217                     r_ptr->r_ignore++;
218                 }
219             }
220
221             break;
222         }
223
224         /* Notice the "waking up" */
225         if (m_ptr->ml) {
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);
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, 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);
248             }
249         }
250
251         break;
252
253     case MTIMED_SLOW:
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);
260             }
261         }
262
263         break;
264
265     case MTIMED_STUNNED: {
266         int rlev = r_info[m_ptr->r_idx].level;
267
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);
275             }
276         }
277
278         break;
279     }
280
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))) {
284             break;
285         }
286
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);
292         }
293
294         break;
295     }
296
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))) {
300             break;
301         }
302
303         /* Visual note */
304         if (is_seen(player_ptr, m_ptr)) {
305             GAME_TEXT m_name[MAX_NLEN];
306 #ifdef JP
307 #else
308             char m_poss[80];
309
310             /* Acquire the monster possessive */
311             monster_desc(player_ptr, m_poss, m_ptr, MD_PRON_VISIBLE | MD_POSSESSIVE);
312 #endif
313             monster_desc(player_ptr, m_name, m_ptr, 0);
314 #ifdef JP
315             msg_format("%^sは勇気を取り戻した。", m_name);
316 #else
317             msg_format("%^s recovers %s courage.", m_name, m_poss);
318 #endif
319         }
320
321         break;
322     }
323
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)) {
327             break;
328         }
329
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);
334         }
335
336         break;
337     }
338     }
339 }
340
341 /*!
342  * @brief 全モンスターの各種状態値を時間経過により更新するメインルーチン
343  * @param mtimed_idx 更新するモンスターの時限ステータスID
344  * @param player_ptr プレイヤーへの参照ポインタ
345  * @details
346  * Process the counters of monsters (once per 10 game turns)\n
347  * These functions are to process monsters' counters same as player's.
348  */
349 void process_monsters_mtimed(PlayerType *player_ptr, int mtimed_idx)
350 {
351     auto *floor_ptr = player_ptr->current_floor_ptr;
352     const auto &cur_mproc_list = floor_ptr->mproc_list[mtimed_idx];
353
354     /* Hack -- calculate the "player noise" */
355     if (mtimed_idx == MTIMED_CSLEEP) {
356         csleep_noise = (1U << (30 - player_ptr->skill_stl));
357     }
358
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);
362     }
363 }
364
365 /*!
366  * @brief モンスターへの魔力消去処理
367  * @param player_ptr プレイヤーへの参照ポインタ
368  * @param m_idx 魔力消去を受けるモンスターの参照ID
369  */
370 void dispel_monster_status(PlayerType *player_ptr, MONSTER_IDX m_idx)
371 {
372     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
373     GAME_TEXT m_name[MAX_NLEN];
374
375     monster_desc(player_ptr, m_name, m_ptr, 0);
376     if (set_monster_invulner(player_ptr, m_idx, 0, true)) {
377         if (m_ptr->ml) {
378             msg_format(_("%sはもう無敵ではない。", "%^s is no longer invulnerable."), m_name);
379         }
380     }
381
382     if (set_monster_fast(player_ptr, m_idx, 0)) {
383         if (m_ptr->ml) {
384             msg_format(_("%sはもう加速されていない。", "%^s is no longer fast."), m_name);
385         }
386     }
387
388     if (set_monster_slow(player_ptr, m_idx, 0)) {
389         if (m_ptr->ml) {
390             msg_format(_("%sはもう減速されていない。", "%^s is no longer slow."), m_name);
391         }
392     }
393 }
394
395 /*!
396  * @brief モンスターの経験値取得処理
397  * @param player_ptr プレイヤーへの参照ポインタ
398  * @param m_idx 経験値を得るモンスターの参照ID
399  * @param s_idx 撃破されたモンスター種族の参照ID
400  */
401 void monster_gain_exp(PlayerType *player_ptr, MONSTER_IDX m_idx, MonsterRaceId s_idx)
402 {
403     if (m_idx <= 0 || !MonsterRace(s_idx).is_valid()) {
404         return;
405     }
406
407     auto *floor_ptr = player_ptr->current_floor_ptr;
408     auto *m_ptr = &floor_ptr->m_list[m_idx];
409
410     if (!monster_is_valid(m_ptr)) {
411         return;
412     }
413
414     auto *r_ptr = &r_info[m_ptr->r_idx];
415     auto *s_ptr = &r_info[s_idx];
416
417     if (player_ptr->phase_out || (r_ptr->next_exp == 0)) {
418         return;
419     }
420
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;
424     }
425
426     if (!floor_ptr->dun_level) {
427         new_exp /= 5;
428     }
429
430     m_ptr->exp += new_exp;
431     if (m_ptr->mflag2.has(MonsterConstantFlagType::CHAMELEON)) {
432         return;
433     }
434
435     if (m_ptr->exp < r_ptr->next_exp) {
436         if (m_idx == player_ptr->riding) {
437             player_ptr->update |= PU_BONUS;
438         }
439
440         return;
441     }
442
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;
448
449     /* Hack -- Reduce the racial counter of previous monster */
450     real_r_ptr(m_ptr)->cur_num--;
451
452     monster_desc(player_ptr, m_name, m_ptr, 0);
453     m_ptr->r_idx = r_ptr->next_r_idx;
454
455     /* Count the monsters on the level */
456     real_r_ptr(m_ptr)->cur_num++;
457
458     m_ptr->ap_r_idx = m_ptr->r_idx;
459     r_ptr = &r_info[m_ptr->r_idx];
460
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);
465     }
466
467     m_ptr->maxhp = m_ptr->max_maxhp;
468     m_ptr->hp = old_hp * m_ptr->maxhp / old_maxhp;
469
470     /* dealt damage is 0 at initial*/
471     m_ptr->dealt_damage = 0;
472
473     /* Extract the monster base speed */
474     m_ptr->mspeed = get_mspeed(floor_ptr, r_ptr);
475
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;
479     } else {
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;
483         }
484
485         if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
486             m_ptr->sub_align |= SUB_ALIGN_GOOD;
487         }
488     }
489
490     m_ptr->exp = 0;
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;
496                 do {
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());
504             } else {
505                 msg_format(_("%sは%sに進化した。", "%^s evolved into %s."), m_name, r_ptr->name.c_str());
506             }
507         }
508
509         if (!is_hallucinated) {
510             r_info[old_r_idx].r_can_evolve = true;
511         }
512
513         /* Now you feel very close to this pet. */
514         m_ptr->parent_m_idx = 0;
515     }
516
517     update_monster(player_ptr, m_idx, false);
518     lite_spot(player_ptr, m_ptr->fy, m_ptr->fx);
519
520     if (m_idx == player_ptr->riding) {
521         player_ptr->update |= PU_BONUS;
522     }
523 }
524
525 bool monster_is_valid(monster_type *m_ptr)
526 {
527     return MonsterRace(m_ptr->r_idx).is_valid();
528 }
529
530 TIME_EFFECT monster_csleep_remaining(monster_type *m_ptr)
531 {
532     return m_ptr->mtimed[MTIMED_CSLEEP];
533 }
534
535 TIME_EFFECT monster_fast_remaining(monster_type *m_ptr)
536 {
537     return m_ptr->mtimed[MTIMED_FAST];
538 }
539
540 TIME_EFFECT monster_slow_remaining(monster_type *m_ptr)
541 {
542     return m_ptr->mtimed[MTIMED_SLOW];
543 }
544
545 TIME_EFFECT monster_stunned_remaining(monster_type *m_ptr)
546 {
547     return m_ptr->mtimed[MTIMED_STUNNED];
548 }
549
550 TIME_EFFECT monster_confused_remaining(monster_type *m_ptr)
551 {
552     return m_ptr->mtimed[MTIMED_CONFUSED];
553 }
554
555 TIME_EFFECT monster_fear_remaining(monster_type *m_ptr)
556 {
557     return m_ptr->mtimed[MTIMED_MONFEAR];
558 }
559
560 TIME_EFFECT monster_invulner_remaining(monster_type *m_ptr)
561 {
562     return m_ptr->mtimed[MTIMED_INVULNER];
563 }