OSDN Git Service

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