OSDN Git Service

[Refactor] #2647 monster_fear_remaining() を monster_type::is_fearful() とmonster_type...
[hengbandforosx/hengbandosx.git] / src / mspell / mspell-selector.cpp
1 /*!
2  * @brief モンスターが詠唱する魔法を選択する処理
3  * @date 2020/07/23
4  * @author Hourier
5  * @details ba-no-ru/rupa-toの特殊処理はここで実施
6  */
7
8 #include "mspell/mspell-selector.h"
9 #include "floor/geometry.h"
10 #include "monster-race/monster-race.h"
11 #include "monster-race/race-flags2.h"
12 #include "monster-race/race-indice-types.h"
13 #include "monster/monster-status.h"
14 #include "mspell/mspell-attack-util.h"
15 #include "mspell/mspell-judgement.h"
16 #include "player/player-status.h"
17 #include "system/floor-type-definition.h"
18 #include "system/monster-race-definition.h"
19 #include "system/monster-type-definition.h"
20 #include "system/player-type-definition.h"
21 #include "util/enum-converter.h"
22 #include "world/world.h"
23
24 /*!
25  * @brief 指定したID値が指定した範囲内のIDかどうかを返す
26  *
27  * enum値に対して範囲で判定するのはあまり好ましくないが、歴史的経緯により仕方がない
28  *
29  * @param spell 判定対象のID
30  * @param start 範囲の開始ID
31  * @param end 範囲の終了ID(このIDも含む)
32  * @return IDが start <= spell <= end なら true、そうでなければ false
33  */
34 static bool spell_in_between(MonsterAbilityType spell, MonsterAbilityType start, MonsterAbilityType end)
35 {
36     auto spell_int = enum2i(spell);
37     return enum2i(start) <= spell_int && spell_int <= enum2i(end);
38 }
39
40 /*!
41  * @brief ID値が攻撃魔法のIDかどうかを返す /
42  * Return TRUE if a spell is good for hurting the player (directly).
43  * @param spell 判定対象のID
44  * @return 正しいIDならばTRUEを返す。
45  */
46 static bool spell_attack(MonsterAbilityType spell)
47 {
48     /* All RF4 spells hurt (except for shriek and dispel) */
49     if (spell_in_between(spell, MonsterAbilityType::ROCKET, MonsterAbilityType::BR_DISI)) {
50         return true;
51     }
52     if (spell_in_between(spell, MonsterAbilityType::BR_VOID, MonsterAbilityType::BR_ABYSS)) {
53         return true;
54     }
55
56     /* Various "ball" spells */
57     if (spell_in_between(spell, MonsterAbilityType::BA_ACID, MonsterAbilityType::BA_DARK)) {
58         return true;
59     }
60     if (spell_in_between(spell, MonsterAbilityType::BA_VOID, MonsterAbilityType::BA_ABYSS)) {
61         return true;
62     }
63
64     /* "Cause wounds" and "bolt" spells */
65     if (spell_in_between(spell, MonsterAbilityType::CAUSE_1, MonsterAbilityType::MISSILE)) {
66         return true;
67     }
68     if (spell_in_between(spell, MonsterAbilityType::BO_VOID, MonsterAbilityType::BO_ABYSS)) {
69         return true;
70     }
71
72     /* Hand of Doom */
73     if (spell == MonsterAbilityType::HAND_DOOM) {
74         return true;
75     }
76
77     /* Psycho-Spear */
78     if (spell == MonsterAbilityType::PSY_SPEAR) {
79         return true;
80     }
81
82     /* Doesn't hurt */
83     return false;
84 }
85
86 /*!
87  * @brief ID値が退避目的に適したモンスター魔法IDかどうかを返す /
88  * Return TRUE if a spell is good for escaping.
89  * @param spell 判定対象のID
90  * @return 適した魔法のIDならばTRUEを返す。
91  */
92 static bool spell_escape(MonsterAbilityType spell)
93 {
94     /* Blink or Teleport */
95     if (spell == MonsterAbilityType::BLINK || spell == MonsterAbilityType::TPORT) {
96         return true;
97     }
98
99     /* Teleport the player away */
100     if (spell == MonsterAbilityType::TELE_AWAY || spell == MonsterAbilityType::TELE_LEVEL) {
101         return true;
102     }
103
104     /* Isn't good for escaping */
105     return false;
106 }
107
108 /*!
109  * @brief ID値が妨害目的に適したモンスター魔法IDかどうかを返す /
110  * Return TRUE if a spell is good for annoying the player.
111  * @param spell 判定対象のID
112  * @return 適した魔法のIDならばTRUEを返す。
113  */
114 static bool spell_annoy(MonsterAbilityType spell)
115 {
116     /* Shriek */
117     if (spell == MonsterAbilityType::SHRIEK) {
118         return true;
119     }
120
121     /* Brain smash, et al (added curses) */
122     if (spell_in_between(spell, MonsterAbilityType::DRAIN_MANA, MonsterAbilityType::CAUSE_4)) {
123         return true;
124     }
125
126     /* Scare, confuse, blind, slow, paralyze */
127     if (spell_in_between(spell, MonsterAbilityType::SCARE, MonsterAbilityType::HOLD)) {
128         return true;
129     }
130
131     /* Teleport to */
132     if (spell == MonsterAbilityType::TELE_TO) {
133         return true;
134     }
135
136     /* Teleport level */
137     if (spell == MonsterAbilityType::TELE_LEVEL) {
138         return true;
139     }
140
141     /* Darkness, make traps, cause amnesia */
142     if (spell_in_between(spell, MonsterAbilityType::TRAPS, MonsterAbilityType::RAISE_DEAD)) {
143         return true;
144     }
145
146     /* Doesn't annoy */
147     return false;
148 }
149
150 /*!
151  * @brief ID値が召喚型のモンスター魔法IDかどうかを返す /
152  * Return TRUE if a spell is good for annoying the player.
153  * @param spell 判定対象のID
154  * @return 召喚型魔法のIDならばTRUEを返す。
155  */
156 static bool spell_summon(MonsterAbilityType spell)
157 {
158     return spell_in_between(spell, MonsterAbilityType::S_KIN, MonsterAbilityType::S_UNIQUE);
159 }
160
161 /*!
162  * @brief ID値が死者復活処理かどうかを返す /
163  * Return TRUE if a spell is good for annoying the player.
164  * @param spell 判定対象のID
165  * @return 死者復活の処理ならばTRUEを返す。
166  */
167 static bool spell_raise(MonsterAbilityType spell)
168 {
169     return spell == MonsterAbilityType::RAISE_DEAD;
170 }
171
172 /*!
173  * @brief ID値が戦術的なモンスター魔法IDかどうかを返す /
174  * Return TRUE if a spell is good in a tactical situation.
175  * @param spell 判定対象のID
176  * @return 戦術的な魔法のIDならばTRUEを返す。
177  */
178 static bool spell_tactic(MonsterAbilityType spell)
179 {
180     return spell == MonsterAbilityType::BLINK;
181 }
182
183 /*!
184  * @brief ID値が無敵化するモンスター魔法IDかどうかを返す /
185  * Return TRUE if a spell makes invulnerable.
186  * @param spell 判定対象のID
187  * @return 召喚型魔法のIDならばTRUEを返す。
188  */
189 static bool spell_invulner(MonsterAbilityType spell)
190 {
191     return spell == MonsterAbilityType::INVULNER;
192 }
193
194 /*!
195  * @brief ID値が加速するモンスター魔法IDかどうかを返す /
196  * Return TRUE if a spell hastes.
197  * @param spell 判定対象のID
198  * @return 召喚型魔法のIDならばTRUEを返す。
199  */
200 static bool spell_haste(MonsterAbilityType spell)
201 {
202     return spell == MonsterAbilityType::HASTE;
203 }
204
205 /*!
206  * @brief ID値が時間停止を行うモンスター魔法IDかどうかを返す /
207  * Return TRUE if a spell world.
208  * @param spell 判定対象のID
209  * @return 時間停止魔法のIDならばTRUEを返す。
210  */
211 static bool spell_world(MonsterAbilityType spell)
212 {
213     return spell == MonsterAbilityType::WORLD;
214 }
215
216 /*!
217  * @brief ID値が特別効果のモンスター魔法IDかどうかを返す /
218  * Return TRUE if a spell special.
219  * @param player_ptr プレイヤーへの参照ポインタ
220  * @param spell 判定対象のID
221  * @return 特別効果魔法のIDならばTRUEを返す。
222  */
223 static bool spell_special(PlayerType *player_ptr, MonsterAbilityType spell)
224 {
225     if (player_ptr->phase_out) {
226         return false;
227     }
228
229     return spell == MonsterAbilityType::SPECIAL;
230 }
231
232 /*!
233  * @brief ID値が光の剣のモンスター魔法IDかどうかを返す /
234  * Return TRUE if a spell psycho-spear.
235  * @param spell 判定対象のID
236  * @return 光の剣のIDならばTRUEを返す。
237  */
238 static bool spell_psy_spe(MonsterAbilityType spell)
239 {
240     return spell == MonsterAbilityType::PSY_SPEAR;
241 }
242
243 /*!
244  * @brief ID値が治癒魔法かどうかを返す /
245  * Return TRUE if a spell is good for healing.
246  * @param spell 判定対象のID
247  * @return 治癒魔法のIDならばTRUEを返す。
248  */
249 static bool spell_heal(MonsterAbilityType spell)
250 {
251     return spell == MonsterAbilityType::HEAL;
252 }
253
254 /*!
255  * @brief ID値が魔力消去かどうかを返す /
256  * Return TRUE if a spell is good for dispel.
257  * @param spell 判定対象のID
258  * @return 魔力消去のIDならばTRUEを返す。
259  */
260 static bool spell_dispel(MonsterAbilityType spell)
261 {
262     return spell == MonsterAbilityType::DISPEL;
263 }
264
265 /*!
266  * @brief モンスターの魔法選択ルーチン
267  * Have a monster choose a spell from a list of "useful" spells.
268  * @param player_ptr プレイヤーへの参照ポインタ
269  * @param m_idx モンスターの構造体配列ID
270  * @param spells 候補魔法IDをまとめた配列
271  * @param num spellsの長さ
272  * @return 選択したモンスター魔法のID
273  * @details
274  * Note that this list does NOT include spells that will just hit\n
275  * other monsters, and the list is restricted when the monster is\n
276  * "desperate".  Should that be the job of this function instead?\n
277  *\n
278  * Stupid monsters will just pick a spell randomly.  Smart monsters\n
279  * will choose more "intelligently".\n
280  *\n
281  * Use the helper functions above to put spells into categories.\n
282  *\n
283  * This function may well be an efficiency bottleneck.\n
284  * @todo 長過ぎる。切り分けが必要
285  */
286 MonsterAbilityType choose_attack_spell(PlayerType *player_ptr, msa_type *msa_ptr)
287 {
288     std::vector<MonsterAbilityType> escape;
289     std::vector<MonsterAbilityType> attack;
290     std::vector<MonsterAbilityType> summon;
291     std::vector<MonsterAbilityType> tactic;
292     std::vector<MonsterAbilityType> annoy;
293     std::vector<MonsterAbilityType> invul;
294     std::vector<MonsterAbilityType> haste;
295     std::vector<MonsterAbilityType> world;
296     std::vector<MonsterAbilityType> special;
297     std::vector<MonsterAbilityType> psy_spe;
298     std::vector<MonsterAbilityType> raise;
299     std::vector<MonsterAbilityType> heal;
300     std::vector<MonsterAbilityType> dispel;
301
302     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[msa_ptr->m_idx];
303     auto *r_ptr = &r_info[m_ptr->r_idx];
304     if (r_ptr->flags2 & RF2_STUPID) {
305         return msa_ptr->mspells[randint0(msa_ptr->mspells.size())];
306     }
307
308     for (size_t i = 0; i < msa_ptr->mspells.size(); i++) {
309         if (spell_escape(msa_ptr->mspells[i])) {
310             escape.push_back(msa_ptr->mspells[i]);
311         }
312
313         if (spell_attack(msa_ptr->mspells[i])) {
314             attack.push_back(msa_ptr->mspells[i]);
315         }
316
317         if (spell_summon(msa_ptr->mspells[i])) {
318             summon.push_back(msa_ptr->mspells[i]);
319         }
320
321         if (spell_tactic(msa_ptr->mspells[i])) {
322             tactic.push_back(msa_ptr->mspells[i]);
323         }
324
325         if (spell_annoy(msa_ptr->mspells[i])) {
326             annoy.push_back(msa_ptr->mspells[i]);
327         }
328
329         if (spell_invulner(msa_ptr->mspells[i])) {
330             invul.push_back(msa_ptr->mspells[i]);
331         }
332
333         if (spell_haste(msa_ptr->mspells[i])) {
334             haste.push_back(msa_ptr->mspells[i]);
335         }
336
337         if (spell_world(msa_ptr->mspells[i])) {
338             world.push_back(msa_ptr->mspells[i]);
339         }
340
341         if (spell_special(player_ptr, msa_ptr->mspells[i])) {
342             special.push_back(msa_ptr->mspells[i]);
343         }
344
345         if (spell_psy_spe(msa_ptr->mspells[i])) {
346             psy_spe.push_back(msa_ptr->mspells[i]);
347         }
348
349         if (spell_raise(msa_ptr->mspells[i])) {
350             raise.push_back(msa_ptr->mspells[i]);
351         }
352
353         if (spell_heal(msa_ptr->mspells[i])) {
354             heal.push_back(msa_ptr->mspells[i]);
355         }
356
357         if (spell_dispel(msa_ptr->mspells[i])) {
358             dispel.push_back(msa_ptr->mspells[i]);
359         }
360     }
361
362     if (!world.empty() && (randint0(100) < 15) && !w_ptr->timewalk_m_idx) {
363         return world[randint0(world.size())];
364     }
365
366     if (!special.empty()) {
367         bool success = false;
368         switch (m_ptr->r_idx) {
369         case MonsterRaceId::BANOR:
370         case MonsterRaceId::LUPART:
371             if ((m_ptr->hp < m_ptr->maxhp / 2) && r_info[MonsterRaceId::BANOR].max_num && r_info[MonsterRaceId::LUPART].max_num) {
372                 success = true;
373             }
374             break;
375         default:
376             break;
377         }
378
379         if (success) {
380             return special[randint0(special.size())];
381         }
382     }
383
384     if (m_ptr->hp < m_ptr->maxhp / 3 && one_in_(2)) {
385         if (!heal.empty()) {
386             return heal[randint0(heal.size())];
387         }
388     }
389
390     if (((m_ptr->hp < m_ptr->maxhp / 3) || m_ptr->is_fearful()) && one_in_(2)) {
391         if (!escape.empty()) {
392             return escape[randint0(escape.size())];
393         }
394     }
395
396     if (!special.empty()) {
397         bool success = false;
398         switch (m_ptr->r_idx) {
399         case MonsterRaceId::OHMU:
400         case MonsterRaceId::BANOR:
401         case MonsterRaceId::LUPART:
402             break;
403         case MonsterRaceId::BANORLUPART:
404             if (randint0(100) < 70) {
405                 success = true;
406             }
407             break;
408         case MonsterRaceId::ROLENTO:
409             if (randint0(100) < 40) {
410                 success = true;
411             }
412             break;
413         default:
414             if (randint0(100) < 50) {
415                 success = true;
416             }
417             break;
418         }
419         if (success) {
420             return special[randint0(special.size())];
421         }
422     }
423
424     if ((distance(player_ptr->y, player_ptr->x, m_ptr->fy, m_ptr->fx) < 4) && (!attack.empty() || r_ptr->ability_flags.has(MonsterAbilityType::TRAPS)) && (randint0(100) < 75) && !w_ptr->timewalk_m_idx) {
425         if (!tactic.empty()) {
426             return tactic[randint0(tactic.size())];
427         }
428     }
429
430     if (!summon.empty() && (randint0(100) < 40)) {
431         return summon[randint0(summon.size())];
432     }
433
434     if (!dispel.empty() && one_in_(2)) {
435         if (dispel_check(player_ptr, msa_ptr->m_idx)) {
436             return dispel[randint0(dispel.size())];
437         }
438     }
439
440     if (!raise.empty() && (randint0(100) < 40)) {
441         return raise[randint0(raise.size())];
442     }
443
444     if (is_invuln(player_ptr)) {
445         if (!psy_spe.empty() && (randint0(100) < 50)) {
446             return psy_spe[randint0(psy_spe.size())];
447         } else if (!attack.empty() && (randint0(100) < 40)) {
448             return attack[randint0(attack.size())];
449         }
450     } else if (!attack.empty() && (randint0(100) < 85)) {
451         return attack[randint0(attack.size())];
452     }
453
454     if (!tactic.empty() && (randint0(100) < 50) && !w_ptr->timewalk_m_idx) {
455         return tactic[randint0(tactic.size())];
456     }
457
458     if (!invul.empty() && !m_ptr->mtimed[MTIMED_INVULNER] && (randint0(100) < 50)) {
459         return invul[randint0(invul.size())];
460     }
461
462     if ((m_ptr->hp < m_ptr->maxhp * 3 / 4) && (randint0(100) < 25)) {
463         if (!heal.empty()) {
464             return heal[randint0(heal.size())];
465         }
466     }
467
468     if (!haste.empty() && (randint0(100) < 20) && !m_ptr->is_accelerated()) {
469         return haste[randint0(haste.size())];
470     }
471
472     if (!annoy.empty() && (randint0(100) < 80)) {
473         return annoy[randint0(annoy.size())];
474     }
475
476     return MonsterAbilityType::MAX;
477 }