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/floor-type-definition.h"
18 #include "system/monster-race-info.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"
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 if (spell_in_between(spell, MonsterAbilityType::BR_VOID, MonsterAbilityType::BR_ABYSS)) {
56 /* Various "ball" spells */
57 if (spell_in_between(spell, MonsterAbilityType::BA_ACID, MonsterAbilityType::BA_DARK)) {
60 if (spell_in_between(spell, MonsterAbilityType::BA_VOID, MonsterAbilityType::BA_ABYSS)) {
64 /* "Cause wounds" and "bolt" spells */
65 if (spell_in_between(spell, MonsterAbilityType::CAUSE_1, MonsterAbilityType::MISSILE)) {
68 if (spell_in_between(spell, MonsterAbilityType::BO_VOID, MonsterAbilityType::BO_ABYSS)) {
73 if (spell == MonsterAbilityType::HAND_DOOM) {
78 if (spell == MonsterAbilityType::PSY_SPEAR) {
87 * @brief ID値が退避目的に適したモンスター魔法IDかどうかを返す /
88 * Return TRUE if a spell is good for escaping.
89 * @param spell 判定対象のID
90 * @return 適した魔法のIDならばTRUEを返す。
92 static bool spell_escape(MonsterAbilityType spell)
94 /* Blink or Teleport */
95 if (spell == MonsterAbilityType::BLINK || spell == MonsterAbilityType::TPORT) {
99 /* Teleport the player away */
100 if (spell == MonsterAbilityType::TELE_AWAY || spell == MonsterAbilityType::TELE_LEVEL) {
104 /* Isn't good for escaping */
109 * @brief ID値が妨害目的に適したモンスター魔法IDかどうかを返す /
110 * Return TRUE if a spell is good for annoying the player.
111 * @param spell 判定対象のID
112 * @return 適した魔法のIDならばTRUEを返す。
114 static bool spell_annoy(MonsterAbilityType spell)
117 if (spell == MonsterAbilityType::SHRIEK) {
121 /* Brain smash, et al (added curses) */
122 if (spell_in_between(spell, MonsterAbilityType::DRAIN_MANA, MonsterAbilityType::CAUSE_4)) {
126 /* Scare, confuse, blind, slow, paralyze */
127 if (spell_in_between(spell, MonsterAbilityType::SCARE, MonsterAbilityType::HOLD)) {
132 if (spell == MonsterAbilityType::TELE_TO) {
137 if (spell == MonsterAbilityType::TELE_LEVEL) {
141 /* Darkness, make traps, cause amnesia */
142 if (spell_in_between(spell, MonsterAbilityType::TRAPS, MonsterAbilityType::RAISE_DEAD)) {
151 * @brief ID値が召喚型のモンスター魔法IDかどうかを返す /
152 * Return TRUE if a spell is good for annoying the player.
153 * @param spell 判定対象のID
154 * @return 召喚型魔法のIDならばTRUEを返す。
156 static bool spell_summon(MonsterAbilityType spell)
158 return spell_in_between(spell, MonsterAbilityType::S_KIN, MonsterAbilityType::S_UNIQUE);
162 * @brief ID値が死者復活処理かどうかを返す /
163 * Return TRUE if a spell is good for annoying the player.
164 * @param spell 判定対象のID
165 * @return 死者復活の処理ならばTRUEを返す。
167 static bool spell_raise(MonsterAbilityType spell)
169 return spell == MonsterAbilityType::RAISE_DEAD;
173 * @brief ID値が戦術的なモンスター魔法IDかどうかを返す /
174 * Return TRUE if a spell is good in a tactical situation.
175 * @param spell 判定対象のID
176 * @return 戦術的な魔法のIDならばTRUEを返す。
178 static bool spell_tactic(MonsterAbilityType spell)
180 return spell == MonsterAbilityType::BLINK;
184 * @brief ID値が無敵化するモンスター魔法IDかどうかを返す /
185 * Return TRUE if a spell makes invulnerable.
186 * @param spell 判定対象のID
187 * @return 召喚型魔法のIDならばTRUEを返す。
189 static bool spell_invulner(MonsterAbilityType spell)
191 return spell == MonsterAbilityType::INVULNER;
195 * @brief ID値が加速するモンスター魔法IDかどうかを返す /
196 * Return TRUE if a spell hastes.
197 * @param spell 判定対象のID
198 * @return 召喚型魔法のIDならばTRUEを返す。
200 static bool spell_haste(MonsterAbilityType spell)
202 return spell == MonsterAbilityType::HASTE;
206 * @brief ID値が時間停止を行うモンスター魔法IDかどうかを返す /
207 * Return TRUE if a spell world.
208 * @param spell 判定対象のID
209 * @return 時間停止魔法のIDならばTRUEを返す。
211 static bool spell_world(MonsterAbilityType spell)
213 return spell == MonsterAbilityType::WORLD;
217 * @brief ID値が特別効果のモンスター魔法IDかどうかを返す /
218 * Return TRUE if a spell special.
219 * @param player_ptr プレイヤーへの参照ポインタ
220 * @param spell 判定対象のID
221 * @return 特別効果魔法のIDならばTRUEを返す。
223 static bool spell_special(PlayerType *player_ptr, MonsterAbilityType spell)
225 if (player_ptr->phase_out) {
229 return spell == MonsterAbilityType::SPECIAL;
233 * @brief ID値が光の剣のモンスター魔法IDかどうかを返す /
234 * Return TRUE if a spell psycho-spear.
235 * @param spell 判定対象のID
236 * @return 光の剣のIDならばTRUEを返す。
238 static bool spell_psy_spe(MonsterAbilityType spell)
240 return spell == MonsterAbilityType::PSY_SPEAR;
244 * @brief ID値が治癒魔法かどうかを返す /
245 * Return TRUE if a spell is good for healing.
246 * @param spell 判定対象のID
247 * @return 治癒魔法のIDならばTRUEを返す。
249 static bool spell_heal(MonsterAbilityType spell)
251 return spell == MonsterAbilityType::HEAL;
255 * @brief ID値が魔力消去かどうかを返す /
256 * Return TRUE if a spell is good for dispel.
257 * @param spell 判定対象のID
258 * @return 魔力消去のIDならばTRUEを返す。
260 static bool spell_dispel(MonsterAbilityType spell)
262 return spell == MonsterAbilityType::DISPEL;
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
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
278 * Stupid monsters will just pick a spell randomly. Smart monsters\n
279 * will choose more "intelligently".\n
281 * Use the helper functions above to put spells into categories.\n
283 * This function may well be an efficiency bottleneck.\n
286 MonsterAbilityType choose_attack_spell(PlayerType *player_ptr, msa_type *msa_ptr)
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;
302 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[msa_ptr->m_idx];
303 auto *r_ptr = &monraces_info[m_ptr->r_idx];
304 if (r_ptr->flags2 & RF2_STUPID) {
305 return msa_ptr->mspells[randint0(msa_ptr->mspells.size())];
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]);
313 if (spell_attack(msa_ptr->mspells[i])) {
314 attack.push_back(msa_ptr->mspells[i]);
317 if (spell_summon(msa_ptr->mspells[i])) {
318 summon.push_back(msa_ptr->mspells[i]);
321 if (spell_tactic(msa_ptr->mspells[i])) {
322 tactic.push_back(msa_ptr->mspells[i]);
325 if (spell_annoy(msa_ptr->mspells[i])) {
326 annoy.push_back(msa_ptr->mspells[i]);
329 if (spell_invulner(msa_ptr->mspells[i])) {
330 invul.push_back(msa_ptr->mspells[i]);
333 if (spell_haste(msa_ptr->mspells[i])) {
334 haste.push_back(msa_ptr->mspells[i]);
337 if (spell_world(msa_ptr->mspells[i])) {
338 world.push_back(msa_ptr->mspells[i]);
341 if (spell_special(player_ptr, msa_ptr->mspells[i])) {
342 special.push_back(msa_ptr->mspells[i]);
345 if (spell_psy_spe(msa_ptr->mspells[i])) {
346 psy_spe.push_back(msa_ptr->mspells[i]);
349 if (spell_raise(msa_ptr->mspells[i])) {
350 raise.push_back(msa_ptr->mspells[i]);
353 if (spell_heal(msa_ptr->mspells[i])) {
354 heal.push_back(msa_ptr->mspells[i]);
357 if (spell_dispel(msa_ptr->mspells[i])) {
358 dispel.push_back(msa_ptr->mspells[i]);
362 if (!world.empty() && (randint0(100) < 15) && !w_ptr->timewalk_m_idx) {
363 return world[randint0(world.size())];
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) && monraces_info[MonsterRaceId::BANOR].max_num && monraces_info[MonsterRaceId::LUPART].max_num) {
380 return special[randint0(special.size())];
384 if (m_ptr->hp < m_ptr->maxhp / 3 && one_in_(2)) {
386 return heal[randint0(heal.size())];
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())];
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:
403 case MonsterRaceId::BANORLUPART:
404 if (randint0(100) < 70) {
408 case MonsterRaceId::ROLENTO:
409 if (randint0(100) < 40) {
414 if (randint0(100) < 50) {
420 return special[randint0(special.size())];
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())];
430 if (!summon.empty() && (randint0(100) < 40)) {
431 return summon[randint0(summon.size())];
434 if (!dispel.empty() && one_in_(2)) {
435 if (dispel_check(player_ptr, msa_ptr->m_idx)) {
436 return dispel[randint0(dispel.size())];
440 if (!raise.empty() && (randint0(100) < 40)) {
441 return raise[randint0(raise.size())];
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())];
450 } else if (!attack.empty() && (randint0(100) < 85)) {
451 return attack[randint0(attack.size())];
454 if (!tactic.empty() && (randint0(100) < 50) && !w_ptr->timewalk_m_idx) {
455 return tactic[randint0(tactic.size())];
458 if (!invul.empty() && !m_ptr->mtimed[MTIMED_INVULNER] && (randint0(100) < 50)) {
459 return invul[randint0(invul.size())];
462 if ((m_ptr->hp < m_ptr->maxhp * 3 / 4) && (randint0(100) < 25)) {
464 return heal[randint0(heal.size())];
468 if (!haste.empty() && (randint0(100) < 20) && !m_ptr->is_accelerated()) {
469 return haste[randint0(haste.size())];
472 if (!annoy.empty() && (randint0(100) < 80)) {
473 return annoy[randint0(annoy.size())];
476 return MonsterAbilityType::MAX;