2 * @brief モンスターが詠唱する魔法を選択する処理
5 * @details ba-no-ru/rupa-toの特殊処理はここで実施
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"
25 * @brief 指定したID値が指定した範囲内のIDかどうかを返す
27 * enum値に対して範囲で判定するのはあまり好ましくないが、歴史的経緯により仕方がない
29 * @param spell 判定対象のID
30 * @param start 範囲の開始ID
31 * @param end 範囲の終了ID(このIDも含む)
32 * @return IDが start <= spell <= end なら true、そうでなければ false
34 static bool spell_in_between(MonsterAbilityType spell, MonsterAbilityType start, MonsterAbilityType end)
36 auto spell_int = enum2i(spell);
37 return enum2i(start) <= spell_int && spell_int <= enum2i(end);
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を返す。
46 static bool spell_attack(MonsterAbilityType spell)
48 /* All RF4 spells hurt (except for shriek and dispel) */
49 if (spell_in_between(spell, MonsterAbilityType::ROCKET, MonsterAbilityType::BR_DISI))
52 /* Various "ball" spells */
53 if (spell_in_between(spell, MonsterAbilityType::BA_ACID, MonsterAbilityType::BA_DARK))
56 /* "Cause wounds" and "bolt" spells */
57 if (spell_in_between(spell, MonsterAbilityType::CAUSE_1, MonsterAbilityType::MISSILE))
61 if (spell == MonsterAbilityType::HAND_DOOM)
65 if (spell == MonsterAbilityType::PSY_SPEAR)
73 * @brief ID値が退避目的に適したモンスター魔法IDかどうかを返す /
74 * Return TRUE if a spell is good for escaping.
75 * @param spell 判定対象のID
76 * @return 適した魔法のIDならばTRUEを返す。
78 static bool spell_escape(MonsterAbilityType spell)
80 /* Blink or Teleport */
81 if (spell == MonsterAbilityType::BLINK || spell == MonsterAbilityType::TPORT)
84 /* Teleport the player away */
85 if (spell == MonsterAbilityType::TELE_AWAY || spell == MonsterAbilityType::TELE_LEVEL)
88 /* Isn't good for escaping */
93 * @brief ID値が妨害目的に適したモンスター魔法IDかどうかを返す /
94 * Return TRUE if a spell is good for annoying the player.
95 * @param spell 判定対象のID
96 * @return 適した魔法のIDならばTRUEを返す。
98 static bool spell_annoy(MonsterAbilityType spell)
101 if (spell == MonsterAbilityType::SHRIEK)
104 /* Brain smash, et al (added curses) */
105 if (spell_in_between(spell, MonsterAbilityType::DRAIN_MANA, MonsterAbilityType::CAUSE_4))
108 /* Scare, confuse, blind, slow, paralyze */
109 if (spell_in_between(spell, MonsterAbilityType::SCARE, MonsterAbilityType::HOLD))
113 if (spell == MonsterAbilityType::TELE_TO)
117 if (spell == MonsterAbilityType::TELE_LEVEL)
120 /* Darkness, make traps, cause amnesia */
121 if (spell_in_between(spell, MonsterAbilityType::TRAPS, MonsterAbilityType::RAISE_DEAD))
129 * @brief ID値が召喚型のモンスター魔法IDかどうかを返す /
130 * Return TRUE if a spell is good for annoying the player.
131 * @param spell 判定対象のID
132 * @return 召喚型魔法のIDならばTRUEを返す。
134 static bool spell_summon(MonsterAbilityType spell)
136 return spell_in_between(spell, MonsterAbilityType::S_KIN, MonsterAbilityType::S_UNIQUE);
140 * @brief ID値が死者復活処理かどうかを返す /
141 * Return TRUE if a spell is good for annoying the player.
142 * @param spell 判定対象のID
143 * @return 死者復活の処理ならばTRUEを返す。
145 static bool spell_raise(MonsterAbilityType spell)
147 return spell == MonsterAbilityType::RAISE_DEAD;
151 * @brief ID値が戦術的なモンスター魔法IDかどうかを返す /
152 * Return TRUE if a spell is good in a tactical situation.
153 * @param spell 判定対象のID
154 * @return 戦術的な魔法のIDならばTRUEを返す。
156 static bool spell_tactic(MonsterAbilityType spell)
158 return spell == MonsterAbilityType::BLINK;
162 * @brief ID値が無敵化するモンスター魔法IDかどうかを返す /
163 * Return TRUE if a spell makes invulnerable.
164 * @param spell 判定対象のID
165 * @return 召喚型魔法のIDならばTRUEを返す。
167 static bool spell_invulner(MonsterAbilityType spell)
169 return spell == MonsterAbilityType::INVULNER;
173 * @brief ID値が加速するモンスター魔法IDかどうかを返す /
174 * Return TRUE if a spell hastes.
175 * @param spell 判定対象のID
176 * @return 召喚型魔法のIDならばTRUEを返す。
178 static bool spell_haste(MonsterAbilityType spell)
180 return spell == MonsterAbilityType::HASTE;
184 * @brief ID値が時間停止を行うモンスター魔法IDかどうかを返す /
185 * Return TRUE if a spell world.
186 * @param spell 判定対象のID
187 * @return 時間停止魔法のIDならばTRUEを返す。
189 static bool spell_world(MonsterAbilityType spell)
191 return spell == MonsterAbilityType::WORLD;
195 * @brief ID値が特別効果のモンスター魔法IDかどうかを返す /
196 * Return TRUE if a spell special.
197 * @param player_ptr プレイヤーへの参照ポインタ
198 * @param spell 判定対象のID
199 * @return 特別効果魔法のIDならばTRUEを返す。
201 static bool spell_special(PlayerType *player_ptr, MonsterAbilityType spell)
203 if (player_ptr->phase_out)
206 return spell == MonsterAbilityType::SPECIAL;
210 * @brief ID値が光の剣のモンスター魔法IDかどうかを返す /
211 * Return TRUE if a spell psycho-spear.
212 * @param spell 判定対象のID
213 * @return 光の剣のIDならばTRUEを返す。
215 static bool spell_psy_spe(MonsterAbilityType spell)
217 return spell == MonsterAbilityType::PSY_SPEAR;
221 * @brief ID値が治癒魔法かどうかを返す /
222 * Return TRUE if a spell is good for healing.
223 * @param spell 判定対象のID
224 * @return 治癒魔法のIDならばTRUEを返す。
226 static bool spell_heal(MonsterAbilityType spell)
228 return spell == MonsterAbilityType::HEAL;
232 * @brief ID値が魔力消去かどうかを返す /
233 * Return TRUE if a spell is good for dispel.
234 * @param spell 判定対象のID
235 * @return 魔力消去のIDならばTRUEを返す。
237 static bool spell_dispel(MonsterAbilityType spell)
239 return spell == MonsterAbilityType::DISPEL;
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
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
255 * Stupid monsters will just pick a spell randomly. Smart monsters\n
256 * will choose more "intelligently".\n
258 * Use the helper functions above to put spells into categories.\n
260 * This function may well be an efficiency bottleneck.\n
263 MonsterAbilityType choose_attack_spell(PlayerType *player_ptr, msa_type *msa_ptr)
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;
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())]);
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]);
288 if (spell_attack(msa_ptr->mspells[i]))
289 attack.push_back(msa_ptr->mspells[i]);
291 if (spell_summon(msa_ptr->mspells[i]))
292 summon.push_back(msa_ptr->mspells[i]);
294 if (spell_tactic(msa_ptr->mspells[i]))
295 tactic.push_back(msa_ptr->mspells[i]);
297 if (spell_annoy(msa_ptr->mspells[i]))
298 annoy.push_back(msa_ptr->mspells[i]);
300 if (spell_invulner(msa_ptr->mspells[i]))
301 invul.push_back(msa_ptr->mspells[i]);
303 if (spell_haste(msa_ptr->mspells[i]))
304 haste.push_back(msa_ptr->mspells[i]);
306 if (spell_world(msa_ptr->mspells[i]))
307 world.push_back(msa_ptr->mspells[i]);
309 if (spell_special(player_ptr, msa_ptr->mspells[i]))
310 special.push_back(msa_ptr->mspells[i]);
312 if (spell_psy_spe(msa_ptr->mspells[i]))
313 psy_spe.push_back(msa_ptr->mspells[i]);
315 if (spell_raise(msa_ptr->mspells[i]))
316 raise.push_back(msa_ptr->mspells[i]);
318 if (spell_heal(msa_ptr->mspells[i]))
319 heal.push_back(msa_ptr->mspells[i]);
321 if (spell_dispel(msa_ptr->mspells[i]))
322 dispel.push_back(msa_ptr->mspells[i]);
325 if (!world.empty() && (randint0(100) < 15) && !w_ptr->timewalk_m_idx)
326 return (world[randint0(world.size())]);
328 if (!special.empty()) {
329 bool success = false;
330 switch (m_ptr->r_idx) {
333 if ((m_ptr->hp < m_ptr->maxhp / 2) && r_info[MON_BANOR].max_num && r_info[MON_LUPART].max_num)
341 return (special[randint0(special.size())]);
344 if (m_ptr->hp < m_ptr->maxhp / 3 && one_in_(2)) {
346 return (heal[randint0(heal.size())]);
349 if (((m_ptr->hp < m_ptr->maxhp / 3) || monster_fear_remaining(m_ptr)) && one_in_(2)) {
351 return (escape[randint0(escape.size())]);
354 if (!special.empty()) {
355 bool success = false;
356 switch (m_ptr->r_idx) {
361 case MON_BANORLUPART:
362 if (randint0(100) < 70)
366 if (randint0(100) < 40)
370 if (randint0(100) < 50)
375 return (special[randint0(special.size())]);
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) {
381 return (tactic[randint0(tactic.size())]);
384 if (!summon.empty() && (randint0(100) < 40))
385 return (summon[randint0(summon.size())]);
387 if (!dispel.empty() && one_in_(2)) {
388 if (dispel_check(player_ptr, msa_ptr->m_idx)) {
389 return (dispel[randint0(dispel.size())]);
393 if (!raise.empty() && (randint0(100) < 40))
394 return (raise[randint0(raise.size())]);
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())]);
402 } else if (!attack.empty() && (randint0(100) < 85)) {
403 return (attack[randint0(attack.size())]);
406 if (!tactic.empty() && (randint0(100) < 50) && !w_ptr->timewalk_m_idx)
407 return (tactic[randint0(tactic.size())]);
409 if (!invul.empty() && !m_ptr->mtimed[MTIMED_INVULNER] && (randint0(100) < 50))
410 return (invul[randint0(invul.size())]);
412 if ((m_ptr->hp < m_ptr->maxhp * 3 / 4) && (randint0(100) < 25)) {
414 return (heal[randint0(heal.size())]);
417 if (!haste.empty() && (randint0(100) < 20) && !monster_fast_remaining(m_ptr))
418 return (haste[randint0(haste.size())]);
420 if (!annoy.empty() && (randint0(100) < 80))
421 return (annoy[randint0(annoy.size())]);
423 return MonsterAbilityType::MAX;