OSDN Git Service

[Refactor] struct player_type を class PlayerType に置換。
[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/monster-type-definition.h"
18 #include "system/floor-type-definition.h"
19 #include "system/monster-race-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     /* Various "ball" spells */
53     if (spell_in_between(spell, MonsterAbilityType::BA_ACID, MonsterAbilityType::BA_DARK))
54         return true;
55
56     /* "Cause wounds" and "bolt" spells */
57     if (spell_in_between(spell, MonsterAbilityType::CAUSE_1, MonsterAbilityType::MISSILE))
58         return true;
59
60     /* Hand of Doom */
61     if (spell == MonsterAbilityType::HAND_DOOM)
62         return true;
63
64     /* Psycho-Spear */
65     if (spell == MonsterAbilityType::PSY_SPEAR)
66         return true;
67
68     /* Doesn't hurt */
69     return false;
70 }
71
72 /*!
73  * @brief ID値が退避目的に適したモンスター魔法IDかどうかを返す /
74  * Return TRUE if a spell is good for escaping.
75  * @param spell 判定対象のID
76  * @return 適した魔法のIDならばTRUEを返す。
77  */
78 static bool spell_escape(MonsterAbilityType spell)
79 {
80     /* Blink or Teleport */
81     if (spell == MonsterAbilityType::BLINK || spell == MonsterAbilityType::TPORT)
82         return true;
83
84     /* Teleport the player away */
85     if (spell == MonsterAbilityType::TELE_AWAY || spell == MonsterAbilityType::TELE_LEVEL)
86         return true;
87
88     /* Isn't good for escaping */
89     return false;
90 }
91
92 /*!
93  * @brief ID値が妨害目的に適したモンスター魔法IDかどうかを返す /
94  * Return TRUE if a spell is good for annoying the player.
95  * @param spell 判定対象のID
96  * @return 適した魔法のIDならばTRUEを返す。
97  */
98 static bool spell_annoy(MonsterAbilityType spell)
99 {
100     /* Shriek */
101     if (spell == MonsterAbilityType::SHRIEK)
102         return true;
103
104     /* Brain smash, et al (added curses) */
105     if (spell_in_between(spell, MonsterAbilityType::DRAIN_MANA, MonsterAbilityType::CAUSE_4))
106         return true;
107
108     /* Scare, confuse, blind, slow, paralyze */
109     if (spell_in_between(spell, MonsterAbilityType::SCARE, MonsterAbilityType::HOLD))
110         return true;
111
112     /* Teleport to */
113     if (spell == MonsterAbilityType::TELE_TO)
114         return true;
115
116     /* Teleport level */
117     if (spell == MonsterAbilityType::TELE_LEVEL)
118         return true;
119
120     /* Darkness, make traps, cause amnesia */
121     if (spell_in_between(spell, MonsterAbilityType::TRAPS, MonsterAbilityType::RAISE_DEAD))
122         return true;
123
124     /* Doesn't annoy */
125     return false;
126 }
127
128 /*!
129  * @brief ID値が召喚型のモンスター魔法IDかどうかを返す /
130  * Return TRUE if a spell is good for annoying the player.
131  * @param spell 判定対象のID
132  * @return 召喚型魔法のIDならばTRUEを返す。
133  */
134 static bool spell_summon(MonsterAbilityType spell)
135 {
136     return spell_in_between(spell, MonsterAbilityType::S_KIN, MonsterAbilityType::S_UNIQUE);
137 }
138
139 /*!
140  * @brief ID値が死者復活処理かどうかを返す /
141  * Return TRUE if a spell is good for annoying the player.
142  * @param spell 判定対象のID
143  * @return 死者復活の処理ならばTRUEを返す。
144  */
145 static bool spell_raise(MonsterAbilityType spell)
146 {
147     return spell == MonsterAbilityType::RAISE_DEAD;
148 }
149
150 /*!
151  * @brief ID値が戦術的なモンスター魔法IDかどうかを返す /
152  * Return TRUE if a spell is good in a tactical situation.
153  * @param spell 判定対象のID
154  * @return 戦術的な魔法のIDならばTRUEを返す。
155  */
156 static bool spell_tactic(MonsterAbilityType spell)
157 {
158     return spell == MonsterAbilityType::BLINK;
159 }
160
161 /*!
162  * @brief ID値が無敵化するモンスター魔法IDかどうかを返す /
163  * Return TRUE if a spell makes invulnerable.
164  * @param spell 判定対象のID
165  * @return 召喚型魔法のIDならばTRUEを返す。
166  */
167 static bool spell_invulner(MonsterAbilityType spell)
168 {
169     return spell == MonsterAbilityType::INVULNER;
170 }
171
172 /*!
173  * @brief ID値が加速するモンスター魔法IDかどうかを返す /
174  * Return TRUE if a spell hastes.
175  * @param spell 判定対象のID
176  * @return 召喚型魔法のIDならばTRUEを返す。
177  */
178 static bool spell_haste(MonsterAbilityType spell)
179 {
180     return spell == MonsterAbilityType::HASTE;
181 }
182
183 /*!
184  * @brief ID値が時間停止を行うモンスター魔法IDかどうかを返す /
185  * Return TRUE if a spell world.
186  * @param spell 判定対象のID
187  * @return 時間停止魔法のIDならばTRUEを返す。
188  */
189 static bool spell_world(MonsterAbilityType spell)
190 {
191     return spell == MonsterAbilityType::WORLD;
192 }
193
194 /*!
195  * @brief ID値が特別効果のモンスター魔法IDかどうかを返す /
196  * Return TRUE if a spell special.
197  * @param player_ptr プレイヤーへの参照ポインタ
198  * @param spell 判定対象のID
199  * @return 特別効果魔法のIDならばTRUEを返す。
200  */
201 static bool spell_special(PlayerType *player_ptr, MonsterAbilityType spell)
202 {
203     if (player_ptr->phase_out)
204         return false;
205
206     return spell == MonsterAbilityType::SPECIAL;
207 }
208
209 /*!
210  * @brief ID値が光の剣のモンスター魔法IDかどうかを返す /
211  * Return TRUE if a spell psycho-spear.
212  * @param spell 判定対象のID
213  * @return 光の剣のIDならばTRUEを返す。
214  */
215 static bool spell_psy_spe(MonsterAbilityType spell)
216 {
217     return spell == MonsterAbilityType::PSY_SPEAR;
218 }
219
220 /*!
221  * @brief ID値が治癒魔法かどうかを返す /
222  * Return TRUE if a spell is good for healing.
223  * @param spell 判定対象のID
224  * @return 治癒魔法のIDならばTRUEを返す。
225  */
226 static bool spell_heal(MonsterAbilityType spell)
227 {
228     return spell == MonsterAbilityType::HEAL;
229 }
230
231 /*!
232  * @brief ID値が魔力消去かどうかを返す /
233  * Return TRUE if a spell is good for dispel.
234  * @param spell 判定対象のID
235  * @return 魔力消去のIDならばTRUEを返す。
236  */
237 static bool spell_dispel(MonsterAbilityType spell)
238 {
239     return spell == MonsterAbilityType::DISPEL;
240 }
241
242 /*!
243  * @brief モンスターの魔法選択ルーチン
244  * Have a monster choose a spell from a list of "useful" spells.
245  * @param player_ptr プレイヤーへの参照ポインタ
246  * @param m_idx モンスターの構造体配列ID
247  * @param spells 候補魔法IDをまとめた配列
248  * @param num spellsの長さ
249  * @return 選択したモンスター魔法のID
250  * @details
251  * Note that this list does NOT include spells that will just hit\n
252  * other monsters, and the list is restricted when the monster is\n
253  * "desperate".  Should that be the job of this function instead?\n
254  *\n
255  * Stupid monsters will just pick a spell randomly.  Smart monsters\n
256  * will choose more "intelligently".\n
257  *\n
258  * Use the helper functions above to put spells into categories.\n
259  *\n
260  * This function may well be an efficiency bottleneck.\n
261  * @todo 長過ぎる。切り分けが必要
262  */
263 MonsterAbilityType choose_attack_spell(PlayerType *player_ptr, msa_type *msa_ptr)
264 {
265     std::vector<MonsterAbilityType> escape;
266     std::vector<MonsterAbilityType> attack;
267     std::vector<MonsterAbilityType> summon;
268     std::vector<MonsterAbilityType> tactic;
269     std::vector<MonsterAbilityType> annoy;
270     std::vector<MonsterAbilityType> invul;
271     std::vector<MonsterAbilityType> haste;
272     std::vector<MonsterAbilityType> world;
273     std::vector<MonsterAbilityType> special;
274     std::vector<MonsterAbilityType> psy_spe;
275     std::vector<MonsterAbilityType> raise;
276     std::vector<MonsterAbilityType> heal;
277     std::vector<MonsterAbilityType> dispel;
278
279     monster_type *m_ptr = &player_ptr->current_floor_ptr->m_list[msa_ptr->m_idx];
280     monster_race *r_ptr = &r_info[m_ptr->r_idx];
281     if (r_ptr->flags2 & RF2_STUPID)
282         return (msa_ptr->mspells[randint0(msa_ptr->mspells.size())]);
283
284     for (size_t i = 0; i < msa_ptr->mspells.size(); i++) {
285         if (spell_escape(msa_ptr->mspells[i]))
286             escape.push_back(msa_ptr->mspells[i]);
287
288         if (spell_attack(msa_ptr->mspells[i]))
289             attack.push_back(msa_ptr->mspells[i]);
290
291         if (spell_summon(msa_ptr->mspells[i]))
292             summon.push_back(msa_ptr->mspells[i]);
293
294         if (spell_tactic(msa_ptr->mspells[i]))
295             tactic.push_back(msa_ptr->mspells[i]);
296
297         if (spell_annoy(msa_ptr->mspells[i]))
298             annoy.push_back(msa_ptr->mspells[i]);
299
300         if (spell_invulner(msa_ptr->mspells[i]))
301             invul.push_back(msa_ptr->mspells[i]);
302
303         if (spell_haste(msa_ptr->mspells[i]))
304             haste.push_back(msa_ptr->mspells[i]);
305
306         if (spell_world(msa_ptr->mspells[i]))
307             world.push_back(msa_ptr->mspells[i]);
308
309         if (spell_special(player_ptr, msa_ptr->mspells[i]))
310             special.push_back(msa_ptr->mspells[i]);
311
312         if (spell_psy_spe(msa_ptr->mspells[i]))
313             psy_spe.push_back(msa_ptr->mspells[i]);
314
315         if (spell_raise(msa_ptr->mspells[i]))
316             raise.push_back(msa_ptr->mspells[i]);
317
318         if (spell_heal(msa_ptr->mspells[i]))
319             heal.push_back(msa_ptr->mspells[i]);
320
321         if (spell_dispel(msa_ptr->mspells[i]))
322             dispel.push_back(msa_ptr->mspells[i]);
323     }
324
325     if (!world.empty() && (randint0(100) < 15) && !w_ptr->timewalk_m_idx)
326         return (world[randint0(world.size())]);
327
328     if (!special.empty()) {
329         bool success = false;
330         switch (m_ptr->r_idx) {
331         case MON_BANOR:
332         case MON_LUPART:
333             if ((m_ptr->hp < m_ptr->maxhp / 2) && r_info[MON_BANOR].max_num && r_info[MON_LUPART].max_num)
334                 success = true;
335             break;
336         default:
337             break;
338         }
339
340         if (success)
341             return (special[randint0(special.size())]);
342     }
343
344     if (m_ptr->hp < m_ptr->maxhp / 3 && one_in_(2)) {
345         if (!heal.empty())
346             return (heal[randint0(heal.size())]);
347     }
348
349     if (((m_ptr->hp < m_ptr->maxhp / 3) || monster_fear_remaining(m_ptr)) && one_in_(2)) {
350         if (!escape.empty())
351             return (escape[randint0(escape.size())]);
352     }
353
354     if (!special.empty()) {
355         bool success = false;
356         switch (m_ptr->r_idx) {
357         case MON_OHMU:
358         case MON_BANOR:
359         case MON_LUPART:
360             break;
361         case MON_BANORLUPART:
362             if (randint0(100) < 70)
363                 success = true;
364             break;
365         case MON_ROLENTO:
366             if (randint0(100) < 40)
367                 success = true;
368             break;
369         default:
370             if (randint0(100) < 50)
371                 success = true;
372             break;
373         }
374         if (success)
375             return (special[randint0(special.size())]);
376     }
377
378     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)
379         && !w_ptr->timewalk_m_idx) {
380         if (!tactic.empty())
381             return (tactic[randint0(tactic.size())]);
382     }
383
384     if (!summon.empty() && (randint0(100) < 40))
385         return (summon[randint0(summon.size())]);
386
387     if (!dispel.empty() && one_in_(2)) {
388         if (dispel_check(player_ptr, msa_ptr->m_idx)) {
389             return (dispel[randint0(dispel.size())]);
390         }
391     }
392
393     if (!raise.empty() && (randint0(100) < 40))
394         return (raise[randint0(raise.size())]);
395
396     if (is_invuln(player_ptr)) {
397         if (!psy_spe.empty() && (randint0(100) < 50)) {
398             return (psy_spe[randint0(psy_spe.size())]);
399         } else if (!attack.empty() && (randint0(100) < 40)) {
400             return (attack[randint0(attack.size())]);
401         }
402     } else if (!attack.empty() && (randint0(100) < 85)) {
403         return (attack[randint0(attack.size())]);
404     }
405
406     if (!tactic.empty() && (randint0(100) < 50) && !w_ptr->timewalk_m_idx)
407         return (tactic[randint0(tactic.size())]);
408
409     if (!invul.empty() && !m_ptr->mtimed[MTIMED_INVULNER] && (randint0(100) < 50))
410         return (invul[randint0(invul.size())]);
411
412     if ((m_ptr->hp < m_ptr->maxhp * 3 / 4) && (randint0(100) < 25)) {
413         if (!heal.empty())
414             return (heal[randint0(heal.size())]);
415     }
416
417     if (!haste.empty() && (randint0(100) < 20) && !monster_fast_remaining(m_ptr))
418         return (haste[randint0(haste.size())]);
419
420     if (!annoy.empty() && (randint0(100) < 80))
421         return (annoy[randint0(annoy.size())]);
422
423     return MonsterAbilityType::MAX;
424 }