OSDN Git Service

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