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 "world/world.h"
24 * @brief 指定したID値が指定した範囲内のIDかどうかを返す
26 * enum値に対して範囲で判定するのはあまり好ましくないが、歴史的経緯により仕方がない
28 * @param spell 判定対象のID
29 * @param start 範囲の開始ID
30 * @param end 範囲の終了ID(このIDも含む)
31 * @return IDが start <= spell <= end なら true、そうでなければ false
33 static bool spell_in_between(RF_ABILITY spell, RF_ABILITY start, RF_ABILITY end)
35 auto spell_int = static_cast<int>(spell);
36 return static_cast<int>(start) <= spell_int && spell_int <= static_cast<int>(end);
40 * @brief ID値が攻撃魔法のIDかどうかを返す /
41 * Return TRUE if a spell is good for hurting the player (directly).
42 * @param spell 判定対象のID
43 * @return 正しいIDならばTRUEを返す。
45 static bool spell_attack(RF_ABILITY spell)
47 /* All RF4 spells hurt (except for shriek and dispel) */
48 if (spell_in_between(spell, RF_ABILITY::ROCKET, RF_ABILITY::BR_DISI))
51 /* Various "ball" spells */
52 if (spell_in_between(spell, RF_ABILITY::BA_ACID, RF_ABILITY::BA_DARK))
55 /* "Cause wounds" and "bolt" spells */
56 if (spell_in_between(spell, RF_ABILITY::CAUSE_1, RF_ABILITY::MISSILE))
60 if (spell == RF_ABILITY::HAND_DOOM)
64 if (spell == RF_ABILITY::PSY_SPEAR)
72 * @brief ID値が退避目的に適したモンスター魔法IDかどうかを返す /
73 * Return TRUE if a spell is good for escaping.
74 * @param spell 判定対象のID
75 * @return 適した魔法のIDならばTRUEを返す。
77 static bool spell_escape(RF_ABILITY spell)
79 /* Blink or Teleport */
80 if (spell == RF_ABILITY::BLINK || spell == RF_ABILITY::TPORT)
83 /* Teleport the player away */
84 if (spell == RF_ABILITY::TELE_AWAY || spell == RF_ABILITY::TELE_LEVEL)
87 /* Isn't good for escaping */
92 * @brief ID値が妨害目的に適したモンスター魔法IDかどうかを返す /
93 * Return TRUE if a spell is good for annoying the player.
94 * @param spell 判定対象のID
95 * @return 適した魔法のIDならばTRUEを返す。
97 static bool spell_annoy(RF_ABILITY spell)
100 if (spell == RF_ABILITY::SHRIEK)
103 /* Brain smash, et al (added curses) */
104 if (spell_in_between(spell, RF_ABILITY::DRAIN_MANA, RF_ABILITY::CAUSE_4))
107 /* Scare, confuse, blind, slow, paralyze */
108 if (spell_in_between(spell, RF_ABILITY::SCARE, RF_ABILITY::HOLD))
112 if (spell == RF_ABILITY::TELE_TO)
116 if (spell == RF_ABILITY::TELE_LEVEL)
119 /* Darkness, make traps, cause amnesia */
120 if (spell_in_between(spell, RF_ABILITY::TRAPS, RF_ABILITY::RAISE_DEAD))
128 * @brief ID値が召喚型のモンスター魔法IDかどうかを返す /
129 * Return TRUE if a spell is good for annoying the player.
130 * @param spell 判定対象のID
131 * @return 召喚型魔法のIDならばTRUEを返す。
133 static bool spell_summon(RF_ABILITY spell)
135 return spell_in_between(spell, RF_ABILITY::S_KIN, RF_ABILITY::S_UNIQUE);
139 * @brief ID値が死者復活処理かどうかを返す /
140 * Return TRUE if a spell is good for annoying the player.
141 * @param spell 判定対象のID
142 * @return 死者復活の処理ならばTRUEを返す。
144 static bool spell_raise(RF_ABILITY spell)
146 return spell == RF_ABILITY::RAISE_DEAD;
150 * @brief ID値が戦術的なモンスター魔法IDかどうかを返す /
151 * Return TRUE if a spell is good in a tactical situation.
152 * @param spell 判定対象のID
153 * @return 戦術的な魔法のIDならばTRUEを返す。
155 static bool spell_tactic(RF_ABILITY spell)
157 return spell == RF_ABILITY::BLINK;
161 * @brief ID値が無敵化するモンスター魔法IDかどうかを返す /
162 * Return TRUE if a spell makes invulnerable.
163 * @param spell 判定対象のID
164 * @return 召喚型魔法のIDならばTRUEを返す。
166 static bool spell_invulner(RF_ABILITY spell)
168 return spell == RF_ABILITY::INVULNER;
172 * @brief ID値が加速するモンスター魔法IDかどうかを返す /
173 * Return TRUE if a spell hastes.
174 * @param spell 判定対象のID
175 * @return 召喚型魔法のIDならばTRUEを返す。
177 static bool spell_haste(RF_ABILITY spell)
179 return spell == RF_ABILITY::HASTE;
183 * @brief ID値が時間停止を行うモンスター魔法IDかどうかを返す /
184 * Return TRUE if a spell world.
185 * @param spell 判定対象のID
186 * @return 時間停止魔法のIDならばTRUEを返す。
188 static bool spell_world(RF_ABILITY spell)
190 return spell == RF_ABILITY::WORLD;
194 * @brief ID値が特別効果のモンスター魔法IDかどうかを返す /
195 * Return TRUE if a spell special.
196 * @param target_ptr プレーヤーへの参照ポインタ
197 * @param spell 判定対象のID
198 * @return 特別効果魔法のIDならばTRUEを返す。
200 static bool spell_special(player_type *target_ptr, RF_ABILITY spell)
202 if (target_ptr->phase_out)
205 return spell == RF_ABILITY::SPECIAL;
209 * @brief ID値が光の剣のモンスター魔法IDかどうかを返す /
210 * Return TRUE if a spell psycho-spear.
211 * @param spell 判定対象のID
212 * @return 光の剣のIDならばTRUEを返す。
214 static bool spell_psy_spe(RF_ABILITY spell)
216 return spell == RF_ABILITY::PSY_SPEAR;
220 * @brief ID値が治癒魔法かどうかを返す /
221 * Return TRUE if a spell is good for healing.
222 * @param spell 判定対象のID
223 * @return 治癒魔法のIDならばTRUEを返す。
225 static bool spell_heal(RF_ABILITY spell)
227 return spell == RF_ABILITY::HEAL;
231 * @brief ID値が魔力消去かどうかを返す /
232 * Return TRUE if a spell is good for dispel.
233 * @param spell 判定対象のID
234 * @return 魔力消去のIDならばTRUEを返す。
236 static bool spell_dispel(RF_ABILITY spell)
238 return spell == RF_ABILITY::DISPEL;
242 * @brief モンスターの魔法選択ルーチン
243 * Have a monster choose a spell from a list of "useful" spells.
244 * @param target_ptr プレーヤーへの参照ポインタ
245 * @param m_idx モンスターの構造体配列ID
246 * @param spells 候補魔法IDをまとめた配列
247 * @param num spellsの長さ
248 * @return 選択したモンスター魔法のID
250 * Note that this list does NOT include spells that will just hit\n
251 * other monsters, and the list is restricted when the monster is\n
252 * "desperate". Should that be the job of this function instead?\n
254 * Stupid monsters will just pick a spell randomly. Smart monsters\n
255 * will choose more "intelligently".\n
257 * Use the helper functions above to put spells into categories.\n
259 * This function may well be an efficiency bottleneck.\n
262 RF_ABILITY choose_attack_spell(player_type *target_ptr, msa_type *msa_ptr)
264 std::vector<RF_ABILITY> escape;
265 std::vector<RF_ABILITY> attack;
266 std::vector<RF_ABILITY> summon;
267 std::vector<RF_ABILITY> tactic;
268 std::vector<RF_ABILITY> annoy;
269 std::vector<RF_ABILITY> invul;
270 std::vector<RF_ABILITY> haste;
271 std::vector<RF_ABILITY> world;
272 std::vector<RF_ABILITY> special;
273 std::vector<RF_ABILITY> psy_spe;
274 std::vector<RF_ABILITY> raise;
275 std::vector<RF_ABILITY> heal;
276 std::vector<RF_ABILITY> dispel;
278 monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[msa_ptr->m_idx];
279 monster_race *r_ptr = &r_info[m_ptr->r_idx];
280 if (r_ptr->flags2 & RF2_STUPID)
281 return (msa_ptr->mspells[randint0(msa_ptr->mspells.size())]);
283 for (size_t i = 0; i < msa_ptr->mspells.size(); i++) {
284 if (spell_escape(msa_ptr->mspells[i]))
285 escape.push_back(msa_ptr->mspells[i]);
287 if (spell_attack(msa_ptr->mspells[i]))
288 attack.push_back(msa_ptr->mspells[i]);
290 if (spell_summon(msa_ptr->mspells[i]))
291 summon.push_back(msa_ptr->mspells[i]);
293 if (spell_tactic(msa_ptr->mspells[i]))
294 tactic.push_back(msa_ptr->mspells[i]);
296 if (spell_annoy(msa_ptr->mspells[i]))
297 annoy.push_back(msa_ptr->mspells[i]);
299 if (spell_invulner(msa_ptr->mspells[i]))
300 invul.push_back(msa_ptr->mspells[i]);
302 if (spell_haste(msa_ptr->mspells[i]))
303 haste.push_back(msa_ptr->mspells[i]);
305 if (spell_world(msa_ptr->mspells[i]))
306 world.push_back(msa_ptr->mspells[i]);
308 if (spell_special(target_ptr, msa_ptr->mspells[i]))
309 special.push_back(msa_ptr->mspells[i]);
311 if (spell_psy_spe(msa_ptr->mspells[i]))
312 psy_spe.push_back(msa_ptr->mspells[i]);
314 if (spell_raise(msa_ptr->mspells[i]))
315 raise.push_back(msa_ptr->mspells[i]);
317 if (spell_heal(msa_ptr->mspells[i]))
318 heal.push_back(msa_ptr->mspells[i]);
320 if (spell_dispel(msa_ptr->mspells[i]))
321 dispel.push_back(msa_ptr->mspells[i]);
324 if (!world.empty() && (randint0(100) < 15) && !current_world_ptr->timewalk_m_idx)
325 return (world[randint0(world.size())]);
327 if (!special.empty()) {
328 bool success = false;
329 switch (m_ptr->r_idx) {
332 if ((m_ptr->hp < m_ptr->maxhp / 2) && r_info[MON_BANOR].max_num && r_info[MON_LUPART].max_num)
340 return (special[randint0(special.size())]);
343 if (m_ptr->hp < m_ptr->maxhp / 3 && one_in_(2)) {
345 return (heal[randint0(heal.size())]);
348 if (((m_ptr->hp < m_ptr->maxhp / 3) || monster_fear_remaining(m_ptr)) && one_in_(2)) {
350 return (escape[randint0(escape.size())]);
353 if (!special.empty()) {
354 bool success = false;
355 switch (m_ptr->r_idx) {
360 case MON_BANORLUPART:
361 if (randint0(100) < 70)
365 if (randint0(100) < 40)
369 if (randint0(100) < 50)
374 return (special[randint0(special.size())]);
377 if ((distance(target_ptr->y, target_ptr->x, m_ptr->fy, m_ptr->fx) < 4) && (!attack.empty() || r_ptr->ability_flags.has(RF_ABILITY::TRAPS)) && (randint0(100) < 75)
378 && !current_world_ptr->timewalk_m_idx) {
380 return (tactic[randint0(tactic.size())]);
383 if (!summon.empty() && (randint0(100) < 40))
384 return (summon[randint0(summon.size())]);
386 if (!dispel.empty() && one_in_(2)) {
387 if (dispel_check(target_ptr, msa_ptr->m_idx)) {
388 return (dispel[randint0(dispel.size())]);
392 if (!raise.empty() && (randint0(100) < 40))
393 return (raise[randint0(raise.size())]);
395 if (is_invuln(target_ptr)) {
396 if (!psy_spe.empty() && (randint0(100) < 50)) {
397 return (psy_spe[randint0(psy_spe.size())]);
398 } else if (!attack.empty() && (randint0(100) < 40)) {
399 return (attack[randint0(attack.size())]);
401 } else if (!attack.empty() && (randint0(100) < 85)) {
402 return (attack[randint0(attack.size())]);
405 if (!tactic.empty() && (randint0(100) < 50) && !current_world_ptr->timewalk_m_idx)
406 return (tactic[randint0(tactic.size())]);
408 if (!invul.empty() && !m_ptr->mtimed[MTIMED_INVULNER] && (randint0(100) < 50))
409 return (invul[randint0(invul.size())]);
411 if ((m_ptr->hp < m_ptr->maxhp * 3 / 4) && (randint0(100) < 25)) {
413 return (heal[randint0(heal.size())]);
416 if (!haste.empty() && (randint0(100) < 20) && !monster_fast_remaining(m_ptr))
417 return (haste[randint0(haste.size())]);
419 if (!annoy.empty() && (randint0(100) < 80))
420 return (annoy[randint0(annoy.size())]);
422 return RF_ABILITY::MAX;