OSDN Git Service

Merge pull request #3322 from habu1010/feature/refactor-switch-randint1-by-rand_choice
[hengbandforosx/hengbandosx.git] / src / mind / mind-elementalist.cpp
1 /*!
2  * @brief 元素使いの魔法系統
3  */
4
5 #include "mind/mind-elementalist.h"
6 #include "action/action-limited.h"
7 #include "avatar/avatar.h"
8 #include "cmd-action/cmd-mind.h"
9 #include "cmd-action/cmd-spell.h"
10 #include "cmd-io/cmd-gameoption.h"
11 #include "core/asking-player.h"
12 #include "core/player-redraw-types.h"
13 #include "core/stuff-handler.h"
14 #include "core/window-redrawer.h"
15 #include "effect/effect-characteristics.h"
16 #include "effect/effect-monster-util.h"
17 #include "effect/effect-processor.h"
18 #include "effect/spells-effect-util.h"
19 #include "floor/cave.h"
20 #include "floor/floor-util.h"
21 #include "floor/geometry.h"
22 #include "game-option/disturbance-options.h"
23 #include "game-option/input-options.h"
24 #include "game-option/text-display-options.h"
25 #include "grid/feature-flag-types.h"
26 #include "grid/grid.h"
27 #include "hpmp/hp-mp-processor.h"
28 #include "io/command-repeater.h"
29 #include "io/input-key-acceptor.h"
30 #include "io/input-key-requester.h"
31 #include "main/sound-definitions-table.h"
32 #include "main/sound-of-music.h"
33 #include "mind/mind-explanations-table.h"
34 #include "mind/mind-mindcrafter.h"
35 #include "monster-race/monster-race.h"
36 #include "monster-race/race-brightness-flags.h"
37 #include "monster-race/race-flags-resistance.h"
38 #include "monster-race/race-flags3.h"
39 #include "monster-race/race-flags7.h"
40 #include "monster/monster-describer.h"
41 #include "player-base/player-class.h"
42 #include "player-info/equipment-info.h"
43 #include "player-status/player-energy.h"
44 #include "player-status/player-status-base.h"
45 #include "player/player-status-table.h"
46 #include "racial/racial-util.h"
47 #include "spell-kind/earthquake.h"
48 #include "spell-kind/magic-item-recharger.h"
49 #include "spell-kind/spells-beam.h"
50 #include "spell-kind/spells-charm.h"
51 #include "spell-kind/spells-detection.h"
52 #include "spell-kind/spells-genocide.h"
53 #include "spell-kind/spells-launcher.h"
54 #include "spell-kind/spells-lite.h"
55 #include "spell-kind/spells-sight.h"
56 #include "spell-kind/spells-teleport.h"
57 #include "spell-kind/spells-world.h"
58 #include "status/bad-status-setter.h"
59 #include "status/base-status.h"
60 #include "system/floor-type-definition.h"
61 #include "system/game-option-types.h"
62 #include "system/grid-type-definition.h"
63 #include "system/monster-entity.h"
64 #include "system/monster-race-info.h"
65 #include "system/player-type-definition.h"
66 #include "target/grid-selector.h"
67 #include "target/target-getter.h"
68 #include "term/screen-processor.h"
69 #include "term/term-color-types.h"
70 #include "term/z-form.h"
71 #include "timed-effect/player-stun.h"
72 #include "timed-effect/timed-effects.h"
73 #include "util/bit-flags-calculator.h"
74 #include "util/enum-converter.h"
75 #include "util/int-char-converter.h"
76 #include "view/display-messages.h"
77 #include "view/display-util.h"
78 #include <array>
79 #include <string>
80 #include <unordered_map>
81
82 /*!
83  * @brief 元素魔法呪文のID定義
84  */
85 enum class ElementSpells {
86     BOLT_1ST = 0,
87     MON_DETECT = 1,
88     PERCEPT = 2,
89     CURE = 3,
90     BOLT_2ND = 4,
91     MAG_DETECT = 5,
92     BALL_3RD = 6,
93     BALL_1ST = 7,
94     BREATH_2ND = 8,
95     ANNIHILATE = 9,
96     BOLT_3RD = 10,
97     WAVE_1ST = 11,
98     BALL_2ND = 12,
99     BURST_1ST = 13,
100     STORM_2ND = 14,
101     BREATH_1ST = 15,
102     STORM_3ND = 16,
103     MAX
104 };
105
106 /*!
107  * @brief 元素魔法タイプ構造体
108  */
109 struct element_type {
110     std::string_view title; //!< 領域名
111     std::array<AttributeType, 3> type; //!< 属性タイプリスト
112     std::array<std::string_view, 3> name; //!< 属性名リスト
113     std::unordered_map<AttributeType, AttributeType> extra; //!< 追加属性タイプ
114 };
115
116 /*!
117  * @brief 元素魔法難易度構造体
118  */
119 struct element_power {
120     int elem; //!< 使用属性番号
121     mind_type info; //!< 難易度構造体
122 };
123
124 using element_type_list = const std::unordered_map<ElementRealmType, element_type>;
125 using element_power_list = const std::unordered_map<ElementSpells, element_power>;
126 using element_tip_list = const std::unordered_map<ElementSpells, std::string_view>;
127 using element_text_list = const std::unordered_map<ElementRealmType, std::string_view>;
128
129 // clang-format off
130 /*!
131  * @brief 元素魔法タイプ定義
132  */
133 static element_type_list element_types = {
134     {
135         ElementRealmType::FIRE, {
136             _("炎", "Fire"),
137             { { AttributeType::FIRE, AttributeType::HELL_FIRE, AttributeType::PLASMA } },
138             { { _("火炎", "Fire"), _("業火", "Hell Fire"), _("プラズマ", "Plasma") } },
139             { },
140         }
141     },
142     {
143         ElementRealmType::ICE, {
144             _("氷", "Ice"),
145             { { AttributeType::COLD, AttributeType::INERTIAL, AttributeType::TIME } },
146             { { _("冷気", "Ice"), _("遅鈍", "Inertia"), _("時間逆転", "Time Stream") } },
147             { { AttributeType::COLD, AttributeType::ICE} },
148         }
149     },
150     {
151         ElementRealmType::SKY, {
152             _("空", "Sky"),
153             { { AttributeType::ELEC, AttributeType::LITE, AttributeType::MANA } },
154             { { _("電撃", "Lightning"), _("光", "Light"), _("魔力", "Mana") } },
155             { },
156         }
157     },
158     {
159         ElementRealmType::SEA, {
160             _("海", "Sea"),
161             { { AttributeType::ACID, AttributeType::WATER, AttributeType::DISINTEGRATE } },
162             { { _("酸", "Acid"), _("水", "Water"), _("分解", "Disintegration") } },
163             { },
164         }
165     },
166     {
167         ElementRealmType::DARKNESS, {
168             _("闇", "Darkness"),
169             { { AttributeType::DARK, AttributeType::NETHER, AttributeType::VOID_MAGIC } },
170             { { _("暗黒", "Darkness"), _("地獄", "Nether"), _("虚無", "void") } },
171             { { AttributeType::DARK, AttributeType::ABYSS } },
172         }
173     },
174     {
175         ElementRealmType::CHAOS, {
176             _("混沌", "Chaos"),
177             { { AttributeType::CONFUSION, AttributeType::CHAOS, AttributeType::NEXUS } },
178             { { _("混乱", "Confusion"), _("カオス", "Chaos"), _("因果混乱", "Nexus") } },
179             { },
180         }
181     },
182     {
183         ElementRealmType::EARTH, {
184             _("地", "Earth"),
185             { { AttributeType::SHARDS, AttributeType::FORCE, AttributeType::METEOR } },
186             { { _("破片", "Shards"), _("フォース", "Force"), _("隕石", "Meteor") } },
187             { },
188         }
189     },
190     {
191         ElementRealmType::DEATH, {
192             _("瘴気", "Death"),
193             { { AttributeType::POIS, AttributeType::HYPODYNAMIA, AttributeType::DISENCHANT } },
194             { { _("毒", "Poison"), _("吸血", "Drain Life"), _("劣化", "Disenchantment") } },
195             { },
196         }
197     },
198 };
199
200 /*!
201  * @brief 元素魔法呪文定義
202  */
203 static element_power_list element_powers = {
204     { ElementSpells::BOLT_1ST,   { 0, {  1,  1,  15, _("%sの矢", "%s Bolt") }}},
205     { ElementSpells::MON_DETECT, { 0, {  2,  2,  20, _("モンスター感知", "Detect Monsters") }}},
206     { ElementSpells::PERCEPT,    { 0, {  5,  5,  50, _("擬似鑑定", "Psychometry") }}},
207     { ElementSpells::CURE,       { 0, {  6,  5,  35, _("傷の治癒", "Cure Wounds") }}},
208     { ElementSpells::BOLT_2ND,   { 1, {  8,  6,  35, _("%sの矢", "%s Bolt") }}},
209     { ElementSpells::MAG_DETECT, { 0, { 10,  8,  60, _("魔法感知", "Detect Magical Objs") }}},
210     { ElementSpells::BALL_3RD,   { 2, { 15, 10,  55, _("%s放射", "%s Spout") }}},
211     { ElementSpells::BALL_1ST,   { 0, { 18, 13,  65, _("%sの球", "%s Ball") }}},
212     { ElementSpells::BREATH_2ND, { 1, { 21, 20,  70, _("%sのブレス", "Breath of %s") }}},
213     { ElementSpells::ANNIHILATE, { 0, { 24, 20,  75, _("モンスター消滅", "Annihilation") }}},
214     { ElementSpells::BOLT_3RD,   { 2, { 25, 15,  60, _("%sの矢", "%s Bolt") }}},
215     { ElementSpells::WAVE_1ST,   { 0, { 28, 30,  75, _("元素の波動", "Elemental Wave") }}},
216     { ElementSpells::BALL_2ND,   { 1, { 28, 22,  75, _("%sの球", "%s Ball") }}},
217     { ElementSpells::BURST_1ST,  { 0, { 33, 35,  75, _("精気乱射", "%s Blast") }}},
218     { ElementSpells::STORM_2ND,  { 1, { 35, 30,  75, _("%sの嵐", "%s Storm") }}},
219     { ElementSpells::BREATH_1ST, { 0, { 42, 48,  75, _("%sのブレス", "Breath of %s") }}},
220     { ElementSpells::STORM_3ND,  { 2, { 45, 60,  80, _("%sの嵐", "%s Storm") }}},
221 };
222
223 /*!
224  * @brief 元素魔法呪文説明文定義
225  */
226 static element_tip_list element_tips = {
227     { ElementSpells::BOLT_1ST,
228     _("弱い%sの矢を放つ。", "Fire a weak bolt of %s.") },
229     { ElementSpells::MON_DETECT,
230     _("近くの全てのモンスターを感知する。", "Detects monsters.") },
231     { ElementSpells::PERCEPT,
232     _("アイテムの雰囲気を知る。", "Gives feeling of an item.") },
233     { ElementSpells::CURE,
234     _("怪我と体力を少し回復させる。", "Heals HP and wounds a bit.") },
235     { ElementSpells::BOLT_2ND,
236     _("%sの矢を放つ。", "Fire a bolt of %s.") },
237     { ElementSpells::MAG_DETECT,
238     _("近くの魔法のアイテムを感知する。", "Detects magic devices.") },
239     { ElementSpells::BALL_3RD,
240     _("高威力で射程が短い%sの球を放つ。", "Fire a strong, short-range, ball of %s.") },
241     { ElementSpells::BALL_1ST,
242     _("%sの球を放つ。",  "Fire a ball of %s.") },
243     { ElementSpells::BREATH_2ND,
244     _("%sのブレスを吐く。", "Fire a breath of %s.") },
245     { ElementSpells::ANNIHILATE,
246     _("%s耐性のないモンスターを1体抹殺する。", "Erase a monster unless it resists %s.") },
247     { ElementSpells::BOLT_3RD,
248     _("%sの矢を放つ。", "Fire a bolt of %s.") },
249     { ElementSpells::WAVE_1ST,
250     _("視界内の全ての敵に%sによるダメージを与える。", "Inflict %s damage on all monsters in sight.") },
251     { ElementSpells::BALL_2ND,
252     _("%sの球を放つ。",  "Fire a ball of %s.") },
253     { ElementSpells::BURST_1ST,
254     _("ランダムな方向に%sの矢を放つ。", "Fire some bolts of %s in random direction.") },
255     { ElementSpells::STORM_2ND,
256     _("%sの巨大な球を放つ。", "Fire a large ball of %s.") },
257     { ElementSpells::BREATH_1ST,
258     _("%sのブレスを吐く。", "Fire a breath of %s.") },
259     { ElementSpells::STORM_3ND,
260     _("%sの巨大な球を放つ。", "Fire a large ball of %s.") },
261 };
262
263 /*!
264  * @brief 元素魔法選択時説明文定義
265  */
266 static element_text_list element_texts = {
267     { ElementRealmType::FIRE,
268     _("炎系統は巨大なエネルギーで灼熱を生み出し、全ての敵を燃やし尽くそうとします。",
269         "Great energy of Fire system will be able to burn out all of your enemies.")},
270     { ElementRealmType::ICE,
271     _("氷系統の魔法はその冷たさで敵の動きを奪い尽くし、魂すらも止めてしまうでしょう。",
272         "Ice system will freeze your enemies, even their souls.")},
273     { ElementRealmType::SKY,
274     _("空系統は大いなる天空のエネルギーを駆使して敵の全てを撃滅できます。",
275         "Sky system can terminate all of your enemies powerfully with the energy of the great sky.")},
276     { ElementRealmType::SEA,
277     _("海系統はその敵の全てを溶かし、大いなる海へと返してしまいます。",
278         "Sea system melts all of your enemies and returns them to the great ocean.")},
279     { ElementRealmType::DARKNESS,
280     _("闇系統は恐るべき力を常闇から引き出し、敵を地獄へと叩き落とすでしょう。",
281         "Dark system draws terrifying power from the darkness and knocks your enemies into hell.")},
282     { ElementRealmType::CHAOS,
283     _("混沌系統は敵の意識も条理も捻じ曲げ、その存在をあの世に送ってしまいます。",
284         "Chaos system twists and wraps your enemies, even their souls, and scatters them as dust in the wind.")},
285     { ElementRealmType::EARTH,
286     _("地系統は偉大なる大地の力を呼び出して、数多の敵のことごとくを粉砕しようとします。",
287         "Earth system smashes all of your enemies massively using its huge powers.")},
288     { ElementRealmType::DEATH,
289     _("瘴気系統は全ての生ける者にとって途轍もない毒です。",
290         "Death system is a tremendous poison for all living enemies.")},
291 };
292
293 // clang-format on
294
295 /*!
296  * @brief 元素魔法の領域名を返す
297  * @param realm_idx 領域番号
298  * @return 領域名
299  */
300 concptr get_element_title(int realm_idx)
301 {
302     auto realm = i2enum<ElementRealmType>(realm_idx);
303     return element_types.at(realm).title.data();
304 }
305
306 /*!
307  * @brief 元素魔法領域の属性リストを返す
308  * @param realm_idx 領域番号
309  * @return 領域で使用できる属性リスト
310  */
311 static std::array<AttributeType, 3> get_element_types(int realm_idx)
312 {
313     auto realm = i2enum<ElementRealmType>(realm_idx);
314     return element_types.at(realm).type;
315 }
316
317 /*!
318  * @brief 元素魔法領域のn番目の属性を返す
319  * @param realm_idx 領域番号
320  * @param n 属性の何番目か
321  * @return 属性タイプ
322  */
323 AttributeType get_element_type(int realm_idx, int n)
324 {
325     return get_element_types(realm_idx)[n];
326 }
327
328 /*!
329  * @brief 元素魔法領域のn番目の呪文用の属性を返す
330  * @param realm_idx 領域番号
331  * @param n 属性の何番目か
332  * @return 属性タイプ
333  */
334 static AttributeType get_element_spells_type(PlayerType *player_ptr, int n)
335 {
336     auto realm = element_types.at(i2enum<ElementRealmType>(player_ptr->element));
337     auto t = realm.type.at(n);
338     if (realm.extra.find(t) != realm.extra.end()) {
339         if (randint0(100) < player_ptr->lev * 2) {
340             return realm.extra.at(t);
341         }
342     }
343     return t;
344 }
345
346 /*!
347  * @brief 元素魔法領域の属性名リストを返す
348  * @param realm_idx 領域番号
349  * @return 領域で使用できる属性の名称リスト
350  */
351 static std::array<std::string_view, 3> get_element_names(int realm_idx)
352 {
353     auto realm = i2enum<ElementRealmType>(realm_idx);
354     return element_types.at(realm).name;
355 }
356
357 /*!
358  * @brief 元素魔法領域のn番目の属性名を返す
359  * @param realm_idx 領域番号
360  * @param n 属性の何番目か
361  * @return 属性名
362  */
363 concptr get_element_name(int realm_idx, int n)
364 {
365     return get_element_names(realm_idx)[n].data();
366 }
367
368 /*!
369  * @brief 元素魔法の説明文を取得
370  * @param player_ptr プレイヤー情報への参照ポインタ
371  * @param spell_idx 呪文番号
372  * @return 説明文
373  */
374 static std::string get_element_tip(PlayerType *player_ptr, int spell_idx)
375 {
376     auto realm = i2enum<ElementRealmType>(player_ptr->element);
377     auto spell = i2enum<ElementSpells>(spell_idx);
378     auto elem = element_powers.at(spell).elem;
379     return format(element_tips.at(spell).data(), element_types.at(realm).name[elem].data());
380 }
381
382 /*!
383  * @brief 元素魔法の説明文を取得
384  * @param player_ptr プレイヤー情報への参照ポインタ
385  * @param spell_idx 呪文番号
386  * @return 説明文
387  */
388 static int get_elemental_elem(PlayerType *player_ptr, int spell_idx)
389 {
390     (void)player_ptr;
391     auto spell = i2enum<ElementSpells>(spell_idx);
392     return element_powers.at(spell).elem;
393 }
394
395 /*!
396  * @brief 元素魔法呪文の難易度データを取得
397  * @param player_ptr プレイヤー情報への参照ポインタ
398  * @param spell_idx 呪文番号
399  * @return 説明文
400  */
401 static mind_type get_elemental_info(PlayerType *player_ptr, int spell_idx)
402 {
403     (void)player_ptr;
404     auto spell = i2enum<ElementSpells>(spell_idx);
405     return element_powers.at(spell).info;
406 }
407
408 /*!
409  * @brief 元素魔法呪文の効果表示文字列を取得
410  * @param player_ptr プレイヤー情報への参照ポインタ
411  * @param spell_idx 呪文番号
412  * @return std::string 魔法の効果を表す文字列
413  */
414 static std::string get_element_effect_info(PlayerType *player_ptr, int spell_idx)
415 {
416     PLAYER_LEVEL plev = player_ptr->lev;
417     auto spell = i2enum<ElementSpells>(spell_idx);
418     int dam = 0;
419
420     switch (spell) {
421     case ElementSpells::BOLT_1ST:
422         return format(" %s%dd%d", KWD_DAM, 3 + ((plev - 1) / 5), 4);
423     case ElementSpells::CURE:
424         return format(" %s%dd%d", KWD_HEAL, 2, 8);
425     case ElementSpells::BOLT_2ND:
426         return format(" %s%dd%d", KWD_DAM, 8 + ((plev - 5) / 4), 8);
427     case ElementSpells::BALL_3RD:
428         return format(" %s%d", KWD_DAM, (50 + plev * 2));
429     case ElementSpells::BALL_1ST:
430         return format(" %s%d", KWD_DAM, 55 + plev);
431     case ElementSpells::BREATH_2ND:
432         dam = p_ptr->chp / 2;
433         return format(" %s%d", KWD_DAM, (dam > 150) ? 150 : dam);
434     case ElementSpells::ANNIHILATE:
435         return format(" %s%d", _("効力:", "pow "), 50 + plev);
436     case ElementSpells::BOLT_3RD:
437         return format(" %s%dd%d", KWD_DAM, 12 + ((plev - 5) / 4), 8);
438     case ElementSpells::WAVE_1ST:
439         return format(" %s50+d%d", KWD_DAM, plev * 3);
440     case ElementSpells::BALL_2ND:
441         return format(" %s%d", KWD_DAM, 75 + plev * 3 / 2);
442     case ElementSpells::BURST_1ST:
443         return format(" %s%dd%d", KWD_DAM, 6 + plev / 8, 7);
444     case ElementSpells::STORM_2ND:
445         return format(" %s%d", KWD_DAM, 115 + plev * 5 / 2);
446     case ElementSpells::BREATH_1ST:
447         return format(" %s%d", KWD_DAM, p_ptr->chp * 2 / 3);
448     case ElementSpells::STORM_3ND:
449         return format(" %s%d", KWD_DAM, 300 + plev * 5);
450     default:
451         return std::string();
452     }
453 }
454
455 /*!
456  * @brief 元素魔法呪文を実行する
457  * @param player_ptr プレイヤー情報への参照ポインタ
458  * @param spell_idx 呪文番号
459  * @return 実行したらTRUE、キャンセルならFALSE
460  */
461 static bool cast_element_spell(PlayerType *player_ptr, SPELL_IDX spell_idx)
462 {
463     auto spell = i2enum<ElementSpells>(spell_idx);
464     auto power = element_powers.at(spell);
465     AttributeType typ;
466     DIRECTION dir;
467     PLAYER_LEVEL plev = player_ptr->lev;
468     int dam;
469     POSITION y, x;
470     int num;
471
472     switch (spell) {
473     case ElementSpells::BOLT_1ST:
474         if (!get_aim_dir(player_ptr, &dir)) {
475             return false;
476         }
477         dam = damroll(3 + ((plev - 1) / 5), 4);
478         typ = get_element_spells_type(player_ptr, power.elem);
479         (void)fire_bolt(player_ptr, typ, dir, dam);
480         break;
481     case ElementSpells::MON_DETECT:
482         (void)detect_monsters_normal(player_ptr, DETECT_RAD_DEFAULT);
483         (void)detect_monsters_invis(player_ptr, DETECT_RAD_DEFAULT);
484         break;
485     case ElementSpells::PERCEPT:
486         return psychometry(player_ptr);
487     case ElementSpells::CURE:
488         (void)hp_player(player_ptr, damroll(2, 8));
489         (void)BadStatusSetter(player_ptr).mod_cut(-10);
490         break;
491     case ElementSpells::BOLT_2ND:
492         if (!get_aim_dir(player_ptr, &dir)) {
493             return false;
494         }
495         dam = damroll(8 + ((plev - 5) / 4), 8);
496         typ = get_element_spells_type(player_ptr, power.elem);
497         if (fire_bolt_or_beam(player_ptr, plev, typ, dir, dam)) {
498             if (typ == AttributeType::HYPODYNAMIA) {
499                 (void)hp_player(player_ptr, dam / 2);
500             }
501         }
502         break;
503     case ElementSpells::MAG_DETECT:
504         (void)detect_objects_magic(player_ptr, DETECT_RAD_DEFAULT);
505         break;
506     case ElementSpells::BALL_3RD:
507         project_length = 4;
508         if (!get_aim_dir(player_ptr, &dir)) {
509             return false;
510         }
511         typ = get_element_spells_type(player_ptr, power.elem);
512         dam = 50 + plev * 2;
513         (void)fire_ball(player_ptr, typ, dir, dam, 1);
514         project_length = 0;
515         break;
516     case ElementSpells::BALL_1ST:
517         if (!get_aim_dir(player_ptr, &dir)) {
518             return false;
519         }
520         dam = 55 + plev;
521         typ = get_element_spells_type(player_ptr, power.elem);
522         (void)fire_ball(player_ptr, typ, dir, dam, 2);
523         break;
524     case ElementSpells::BREATH_2ND:
525         if (!get_aim_dir(player_ptr, &dir)) {
526             return false;
527         }
528         dam = std::min(150, player_ptr->chp / 2);
529         typ = get_element_spells_type(player_ptr, power.elem);
530         if (fire_breath(player_ptr, typ, dir, dam, 3)) {
531             if (typ == AttributeType::HYPODYNAMIA) {
532                 (void)hp_player(player_ptr, dam / 2);
533             }
534         }
535         break;
536     case ElementSpells::ANNIHILATE:
537         if (!get_aim_dir(player_ptr, &dir)) {
538             return false;
539         }
540         fire_ball_hide(player_ptr, AttributeType::E_GENOCIDE, dir, plev + 50, 0);
541         break;
542     case ElementSpells::BOLT_3RD:
543         if (!get_aim_dir(player_ptr, &dir)) {
544             return false;
545         }
546         dam = damroll(12 + ((plev - 5) / 4), 8);
547         typ = get_element_spells_type(player_ptr, power.elem);
548         fire_bolt_or_beam(player_ptr, plev, typ, dir, dam);
549         break;
550     case ElementSpells::WAVE_1ST:
551         dam = 50 + randint1(plev * 3);
552         typ = get_element_spells_type(player_ptr, power.elem);
553         project_all_los(player_ptr, typ, dam);
554         break;
555     case ElementSpells::BALL_2ND:
556         if (!get_aim_dir(player_ptr, &dir)) {
557             return false;
558         }
559         dam = 75 + plev * 3 / 2;
560         typ = get_element_spells_type(player_ptr, power.elem);
561         if (fire_ball(player_ptr, typ, dir, dam, 3)) {
562             if (typ == AttributeType::HYPODYNAMIA) {
563                 (void)hp_player(player_ptr, dam / 2);
564             }
565         }
566         break;
567     case ElementSpells::BURST_1ST:
568         y = player_ptr->y;
569         x = player_ptr->x;
570         num = damroll(4, 3);
571         typ = get_element_spells_type(player_ptr, power.elem);
572         for (int k = 0; k < num; k++) {
573             int attempts = 1000;
574             while (attempts--) {
575                 scatter(player_ptr, &y, &x, player_ptr->y, player_ptr->x, 4, PROJECT_NONE);
576                 if (!cave_has_flag_bold(player_ptr->current_floor_ptr, y, x, TerrainCharacteristics::PROJECT)) {
577                     continue;
578                 }
579                 if (!player_bold(player_ptr, y, x)) {
580                     break;
581                 }
582             }
583             project(player_ptr, 0, 0, y, x, damroll(6 + plev / 8, 7), typ, (PROJECT_BEAM | PROJECT_THRU | PROJECT_GRID | PROJECT_KILL));
584         }
585         break;
586     case ElementSpells::STORM_2ND:
587         if (!get_aim_dir(player_ptr, &dir)) {
588             return false;
589         }
590         dam = 115 + plev * 5 / 2;
591         typ = get_element_spells_type(player_ptr, power.elem);
592         if (fire_ball(player_ptr, typ, dir, dam, 4)) {
593             if (typ == AttributeType::HYPODYNAMIA) {
594                 (void)hp_player(player_ptr, dam / 2);
595             }
596         }
597         break;
598     case ElementSpells::BREATH_1ST:
599         if (!get_aim_dir(player_ptr, &dir)) {
600             return false;
601         }
602         dam = player_ptr->chp * 2 / 3;
603         typ = get_element_spells_type(player_ptr, power.elem);
604         (void)fire_breath(player_ptr, typ, dir, dam, 3);
605         break;
606     case ElementSpells::STORM_3ND:
607         if (!get_aim_dir(player_ptr, &dir)) {
608             return false;
609         }
610         dam = 300 + plev * 5;
611         typ = get_element_spells_type(player_ptr, power.elem);
612         (void)fire_ball(player_ptr, typ, dir, dam, 5);
613         break;
614     default:
615         return false;
616     }
617
618     return true;
619 }
620
621 /*!
622  * @brief 元素魔法呪文の失敗率を計算
623  * @param player_ptr プレイヤー情報への参照ポインタ
624  * @param spell_idx 呪文番号
625  * @return 失敗率
626  */
627 static PERCENTAGE decide_element_chance(PlayerType *player_ptr, mind_type spell)
628 {
629     PERCENTAGE chance = spell.fail;
630
631     chance -= 3 * (player_ptr->lev - spell.min_lev);
632     chance += player_ptr->to_m_chance;
633     chance -= 3 * (adj_mag_stat[player_ptr->stat_index[A_WIS]] - 1);
634
635     PERCENTAGE minfail = adj_mag_fail[player_ptr->stat_index[A_WIS]];
636     if (chance < minfail) {
637         chance = minfail;
638     }
639
640     auto player_stun = player_ptr->effects()->stun();
641     chance += player_stun->get_magic_chance_penalty();
642     if (heavy_armor(player_ptr)) {
643         chance += 5;
644     }
645
646     if (player_ptr->is_icky_wield[0]) {
647         chance += 5;
648     }
649
650     if (player_ptr->is_icky_wield[1]) {
651         chance += 5;
652     }
653
654     if (chance > 95) {
655         chance = 95;
656     }
657
658     return chance;
659 }
660
661 /*!
662  * @brief 元素魔法呪文の消費MPを計算
663  * @param player_ptr プレイヤー情報への参照ポインタ
664  * @param spell_idx 呪文番号
665  * @return 消費MP
666  */
667 static MANA_POINT decide_element_mana_cost(PlayerType *player_ptr, mind_type spell)
668 {
669     (void)player_ptr;
670     return spell.mana_cost;
671 }
672
673 /*!
674  * @brief 元素魔法呪文を選択して取得
675  * @param player_ptr プレイヤー情報への参照ポインタ
676  * @param sn 呪文番号
677  * @param only_browse 閲覧モードかどうか
678  * @return 選んだらTRUE、選ばなかったらFALSE
679  */
680 static bool get_element_power(PlayerType *player_ptr, SPELL_IDX *sn, bool only_browse)
681 {
682     SPELL_IDX i;
683     int num = 0;
684     TERM_LEN y = 1;
685     TERM_LEN x = 10;
686     PLAYER_LEVEL plev = player_ptr->lev;
687     char choice;
688     char out_val[160];
689     COMMAND_CODE code;
690     bool flag, redraw;
691     int menu_line = (use_menu ? 1 : 0);
692
693     *sn = -1;
694     if (repeat_pull(&code)) {
695         *sn = (SPELL_IDX)code;
696         if (get_elemental_info(player_ptr, *sn).min_lev <= plev) {
697             return true;
698         }
699     }
700
701     concptr p = _("元素魔法", "power");
702     flag = false;
703     redraw = false;
704
705     for (i = 0; i < static_cast<SPELL_IDX>(ElementSpells::MAX); i++) {
706         if (get_elemental_info(player_ptr, i).min_lev <= plev) {
707             num++;
708         }
709     }
710
711     if (only_browse) {
712         constexpr auto mes = _("(%s^ %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%s^s %c-%c, *=List, ESC=exit) Use which %s? ");
713         (void)strnfmt(out_val, 78, mes, p, I2A(0), I2A(num - 1), p);
714     } else {
715         constexpr auto mes = _("(%s^ %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%s^s %c-%c, *=List, ESC=exit) Use which %s? ");
716         (void)strnfmt(out_val, 78, mes, p, I2A(0), I2A(num - 1), p);
717     }
718
719     if (use_menu && !only_browse) {
720         screen_save();
721     }
722
723     int elem;
724     mind_type spell;
725     choice = (always_show_list || use_menu) ? ESCAPE : 1;
726     while (!flag) {
727         if (choice == ESCAPE) {
728             choice = ' ';
729         } else if (!get_com(out_val, &choice, true)) {
730             break;
731         }
732
733         auto should_redraw_cursor = true;
734         if (use_menu && choice != ' ') {
735             switch (choice) {
736             case '0':
737                 if (!only_browse) {
738                     screen_load();
739                 }
740                 return false;
741             case '8':
742             case 'k':
743             case 'K':
744                 menu_line += (num - 1);
745                 break;
746             case '2':
747             case 'j':
748             case 'J':
749                 menu_line++;
750                 break;
751             case 'x':
752             case 'X':
753             case '\r':
754             case '\n':
755                 i = menu_line - 1;
756                 should_redraw_cursor = false;
757                 break;
758             }
759
760             if (menu_line > num) {
761                 menu_line -= num;
762             }
763         }
764
765         int spell_max = enum2i(ElementSpells::MAX);
766         if ((choice == ' ') || (choice == '*') || (choice == '?') || (use_menu && should_redraw_cursor)) {
767             if (!redraw || use_menu) {
768                 redraw = true;
769                 if (!only_browse && !use_menu) {
770                     screen_save();
771                 }
772
773                 prt("", y, x);
774                 put_str(_("名前", "Name"), y, x + 5);
775                 put_str(_("Lv   MP   失率 効果", "Lv   MP Fail Info"), y, x + 35);
776                 for (i = 0; i < spell_max; i++) {
777                     elem = get_elemental_elem(player_ptr, i);
778                     spell = get_elemental_info(player_ptr, i);
779
780                     if (spell.min_lev > plev) {
781                         break;
782                     }
783
784                     PERCENTAGE chance = decide_element_chance(player_ptr, spell);
785                     int mana_cost = decide_element_mana_cost(player_ptr, spell);
786                     const auto comment = get_element_effect_info(player_ptr, i);
787
788                     std::string desc;
789                     if (use_menu) {
790                         if (i == (menu_line - 1)) {
791                             desc = _("  》 ", "  >  ");
792                         } else {
793                             desc = "     ";
794                         }
795                     } else {
796                         desc = format("  %c) ", I2A(i));
797                     }
798
799                     concptr s = get_element_name(player_ptr->element, elem);
800                     std::string name = format(spell.name, s);
801                     desc.append(format("%-30s%2d %4d %3d%%%s", name.data(), spell.min_lev, mana_cost, chance, comment.data()));
802                     prt(desc, y + i + 1, x);
803                 }
804
805                 prt("", y + i + 1, x);
806             } else if (!only_browse) {
807                 redraw = false;
808                 screen_load();
809             }
810
811             continue;
812         }
813
814         if (!use_menu) {
815             i = A2I(choice);
816         }
817
818         if ((i < 0) || (i >= num)) {
819             bell();
820             continue;
821         }
822
823         flag = true;
824     }
825
826     if (redraw && !only_browse) {
827         screen_load();
828     }
829
830     set_bits(player_ptr->window_flags, PW_SPELL);
831     handle_stuff(player_ptr);
832     if (!flag) {
833         return false;
834     }
835
836     *sn = i;
837     repeat_push((COMMAND_CODE)i);
838     return true;
839 }
840
841 /*!
842  * @brief 元素魔法呪文をMPがなくても挑戦するか確認する
843  * @param player_ptr プレイヤー情報への参照ポインタ
844  * @param mana_cost 消費MP
845  * @return 詠唱するならTRUE、しないならFALSE
846  */
847 static bool check_element_mp_sufficiency(PlayerType *player_ptr, int mana_cost)
848 {
849     if (mana_cost <= player_ptr->csp) {
850         return true;
851     }
852
853     msg_print(_("MPが足りません。", "You do not have enough mana to use this power."));
854     if (!over_exert) {
855         return false;
856     }
857
858     return get_check(_("それでも挑戦しますか? ", "Attempt it anyway? "));
859 }
860
861 /*!
862  * @brief 元素魔法呪文の詠唱を試み、成功なら詠唱し、失敗ならファンブルする
863  * @param player_ptr プレイヤー情報への参照ポインタ
864  * @param spell_idx 呪文番号
865  * @param chance 失敗率
866  * @return 詠唱して実行したらTRUE、されなかったらFALSE
867  */
868 static bool try_cast_element_spell(PlayerType *player_ptr, SPELL_IDX spell_idx, PERCENTAGE chance)
869 {
870     if (randint0(100) >= chance) {
871         sound(SOUND_ZAP);
872         return cast_element_spell(player_ptr, spell_idx);
873     }
874
875     if (flush_failure) {
876         flush();
877     }
878
879     msg_format(_("魔力の集中に失敗した!", "You failed to focus the elemental power!"));
880     sound(SOUND_FAIL);
881
882     if (randint1(100) < chance / 2) {
883         int plev = player_ptr->lev;
884         msg_print(_("元素の力が制御できない氾流となって解放された!", "The elemental power surges from you in an uncontrollable torrent!"));
885         const auto element = get_element_types(player_ptr->element)[0];
886         constexpr auto flags = PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM;
887         project(player_ptr, PROJECT_WHO_UNCTRL_POWER, 2 + plev / 10, player_ptr->y, player_ptr->x, plev * 2, element, flags);
888         player_ptr->csp = std::max(0, player_ptr->csp - player_ptr->msp * 10 / (20 + randint1(10)));
889
890         PlayerEnergy(player_ptr).set_player_turn_energy(100);
891         set_bits(player_ptr->redraw, PR_MP);
892         set_bits(player_ptr->window_flags, PW_PLAYER | PW_SPELL);
893
894         return false;
895     }
896
897     return true;
898 }
899
900 /*!
901  * @brief 元素魔法コマンドのメインルーチン
902  * @param player_ptr プレイヤー情報への参照ポインタ
903  */
904 void do_cmd_element(PlayerType *player_ptr)
905 {
906     SPELL_IDX i;
907     if (cmd_limit_confused(player_ptr) || !get_element_power(player_ptr, &i, false)) {
908         return;
909     }
910
911     mind_type spell = get_elemental_info(player_ptr, i);
912     PERCENTAGE chance = decide_element_chance(player_ptr, spell);
913     int mana_cost = decide_element_mana_cost(player_ptr, spell);
914
915     if (!check_element_mp_sufficiency(player_ptr, mana_cost)) {
916         return;
917     }
918
919     if (!try_cast_element_spell(player_ptr, i, chance)) {
920         return;
921     }
922
923     if (mana_cost <= player_ptr->csp) {
924         player_ptr->csp -= mana_cost;
925     } else {
926         int oops = mana_cost;
927         player_ptr->csp = 0;
928         player_ptr->csp_frac = 0;
929         msg_print(_("精神を集中しすぎて気を失ってしまった!", "You faint from the effort!"));
930         (void)BadStatusSetter(player_ptr).mod_paralysis(randint1(5 * oops + 1));
931         chg_virtue(player_ptr, Virtue::KNOWLEDGE, -10);
932         if (randint0(100) < 50) {
933             bool perm = (randint0(100) < 25);
934             msg_print(_("体を悪くしてしまった!", "You have damaged your health!"));
935             (void)dec_stat(player_ptr, A_CON, 15 + randint1(10), perm);
936         }
937     }
938
939     PlayerEnergy(player_ptr).set_player_turn_energy(100);
940     set_bits(player_ptr->redraw, PR_MP);
941     set_bits(player_ptr->window_flags, PW_PLAYER | PW_SPELL);
942 }
943
944 /*!
945  * @brief 現在プレイヤーが使用可能な元素魔法の一覧表示
946  * @param player_ptr プレイヤー情報への参照ポインタ
947  */
948 void do_cmd_element_browse(PlayerType *player_ptr)
949 {
950     SPELL_IDX n = 0;
951
952     screen_save();
953     while (true) {
954         if (!get_element_power(player_ptr, &n, true)) {
955             screen_load();
956             return;
957         }
958
959         term_erase(12, 21, 255);
960         term_erase(12, 20, 255);
961         term_erase(12, 19, 255);
962         term_erase(12, 18, 255);
963         term_erase(12, 17, 255);
964         term_erase(12, 16, 255);
965         display_wrap_around(get_element_tip(player_ptr, n), 62, 17, 15);
966
967         prt(_("何かキーを押して下さい。", "Hit any key."), 0, 0);
968         (void)inkey();
969     }
970 }
971
972 /*!
973  * @brief 元素魔法の単体抹殺が有効か確認する
974  * @param r_ptr モンスター種族への参照ポインタ
975  * @param type 魔法攻撃属性
976  * @return 効果があるならTRUE、なければFALSE
977  */
978 bool is_elemental_genocide_effective(MonsterRaceInfo *r_ptr, AttributeType type)
979 {
980     switch (type) {
981     case AttributeType::FIRE:
982         if (r_ptr->resistance_flags.has(MonsterResistanceType::IMMUNE_FIRE)) {
983             return false;
984         }
985         break;
986     case AttributeType::COLD:
987         if (r_ptr->resistance_flags.has(MonsterResistanceType::IMMUNE_COLD)) {
988             return false;
989         }
990         break;
991     case AttributeType::ELEC:
992         if (r_ptr->resistance_flags.has(MonsterResistanceType::IMMUNE_ELEC)) {
993             return false;
994         }
995         break;
996     case AttributeType::ACID:
997         if (r_ptr->resistance_flags.has(MonsterResistanceType::IMMUNE_ACID)) {
998             return false;
999         }
1000         break;
1001     case AttributeType::DARK:
1002         if (r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_DARK) || r_ptr->r_resistance_flags.has(MonsterResistanceType::HURT_LITE)) {
1003             return false;
1004         }
1005         break;
1006     case AttributeType::CONFUSION:
1007         if (any_bits(r_ptr->flags3, RF3_NO_CONF)) {
1008             return false;
1009         }
1010         break;
1011     case AttributeType::SHARDS:
1012         if (r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_SHARDS)) {
1013             return false;
1014         }
1015         break;
1016     case AttributeType::POIS:
1017         if (r_ptr->resistance_flags.has(MonsterResistanceType::IMMUNE_POISON)) {
1018             return false;
1019         }
1020         break;
1021     default:
1022         return false;
1023     }
1024
1025     return true;
1026 }
1027
1028 /*!
1029  * @brief 元素魔法の単体抹殺の効果を発動する
1030  * @param player_ptr プレイヤー情報への参照ポインタ
1031  * @param em_ptr 魔法効果情報への参照ポインタ
1032  * @return 効果処理を続けるかどうか
1033  */
1034 ProcessResult effect_monster_elemental_genocide(PlayerType *player_ptr, effect_monster_type *em_ptr)
1035 {
1036     auto type = get_element_type(player_ptr->element, 0);
1037     auto name = get_element_name(player_ptr->element, 0);
1038     bool b = is_elemental_genocide_effective(em_ptr->r_ptr, type);
1039
1040     if (em_ptr->seen_msg) {
1041         msg_format(_("%sが%sを包み込んだ。", "The %s surrounds %s."), name, em_ptr->m_name);
1042     }
1043
1044     if (em_ptr->seen) {
1045         em_ptr->obvious = true;
1046     }
1047
1048     if (!b) {
1049         if (em_ptr->seen_msg) {
1050             msg_format(_("%sには効果がなかった。", "%s^ is unaffected."), em_ptr->m_name);
1051         }
1052         em_ptr->dam = 0;
1053         return ProcessResult::PROCESS_TRUE;
1054     }
1055
1056     if (genocide_aux(player_ptr, em_ptr->g_ptr->m_idx, em_ptr->dam, !em_ptr->who, (em_ptr->r_ptr->level + 1) / 2, _("モンスター消滅", "Genocide One"))) {
1057         if (em_ptr->seen_msg) {
1058             msg_format(_("%sは消滅した!", "%s^ disappeared!"), em_ptr->m_name);
1059         }
1060         em_ptr->dam = 0;
1061         chg_virtue(player_ptr, Virtue::VITALITY, -1);
1062         return ProcessResult::PROCESS_TRUE;
1063     }
1064
1065     em_ptr->skipped = true;
1066     return ProcessResult::PROCESS_CONTINUE;
1067 }
1068
1069 /*!
1070  * @brief 元素領域とレベルの条件に見合うかチェックする
1071  * @param player_ptr プレイヤー情報への参照ポインタ
1072  * @param realm 領域
1073  * @param lev プレイヤーレベル
1074  * @return 見合うならTRUE、そうでなければFALSE
1075  * @details
1076  * レベルに応じて取得する耐性などの判定に使用する
1077  */
1078 bool has_element_resist(PlayerType *player_ptr, ElementRealmType realm, PLAYER_LEVEL lev)
1079 {
1080     if (!PlayerClass(player_ptr).equals(PlayerClassType::ELEMENTALIST)) {
1081         return false;
1082     }
1083
1084     auto prealm = i2enum<ElementRealmType>(player_ptr->element);
1085     return (prealm == realm) && (player_ptr->lev >= lev);
1086 }
1087
1088 /*!
1089  * @brief 領域選択時のカーソル表示(シンボル+領域名)
1090  * @param i 位置
1091  * @param n 最後尾の位置
1092  * @param color 表示色
1093  */
1094 static void display_realm_cursor(int i, int n, term_color_type color)
1095 {
1096     char sym;
1097     concptr name;
1098     if (i == n) {
1099         sym = '*';
1100         name = _("ランダム", "Random");
1101     } else {
1102         sym = I2A(i);
1103         name = element_types.at(i2enum<ElementRealmType>(i + 1)).title.data();
1104     }
1105
1106     c_put_str(color, format("%c) %s", sym, name), 12 + (i / 5), 2 + 15 * (i % 5));
1107 }
1108
1109 /*!
1110  * @brief 領域選択時の移動キー処理
1111  * @param cs 現在位置
1112  * @param n 最後尾の位置
1113  * @param c 入力キー
1114  * @return 新しい位置
1115  */
1116 static int interpret_realm_select_key(int cs, int n, char c)
1117 {
1118     if (c == 'Q') {
1119         quit(nullptr);
1120     }
1121
1122     if (c == '8') {
1123         if (cs >= 5) {
1124             return cs - 5;
1125         }
1126     }
1127
1128     if (c == '4') {
1129         if (cs > 0) {
1130             return cs - 1;
1131         }
1132     }
1133
1134     if (c == '6') {
1135         if (cs < n) {
1136             return cs + 1;
1137         }
1138     }
1139
1140     if (c == '2') {
1141         if (cs + 5 <= n) {
1142             return cs + 5;
1143         }
1144     }
1145
1146     return cs;
1147 }
1148
1149 /*!
1150  * @brief 領域選択ループ処理
1151  * @param player_ptr プレイヤー情報への参照ポインタ
1152  * @param n 最後尾の位置
1153  * @return 領域番号
1154  */
1155 static int get_element_realm(PlayerType *player_ptr, int is, int n)
1156 {
1157     int cs = std::max(0, is);
1158     int os = cs;
1159     int k;
1160
1161     std::string buf = format(_("領域を選んで下さい(%c-%c) ('='初期オプション設定): ", "Choose a realm (%c-%c) ('=' for options): "), I2A(0), I2A(n - 1));
1162
1163     while (true) {
1164         display_realm_cursor(os, n, TERM_WHITE);
1165         display_realm_cursor(cs, n, TERM_YELLOW);
1166         put_str(buf, 10, 10);
1167         os = cs;
1168
1169         char c = inkey();
1170         cs = interpret_realm_select_key(cs, n, c);
1171
1172         if (c == 'S') {
1173             return 255;
1174         }
1175
1176         if (c == ' ' || c == '\r' || c == '\n') {
1177             if (cs == n) {
1178                 display_realm_cursor(cs, n, TERM_WHITE);
1179                 cs = randint0(n - 1);
1180             }
1181             break;
1182         }
1183
1184         if (c == '*') {
1185             display_realm_cursor(cs, n, TERM_WHITE);
1186             cs = randint0(n - 1);
1187             break;
1188         }
1189
1190         k = islower(c) ? A2I(c) : -1;
1191         if (k >= 0 && k < n) {
1192             display_realm_cursor(cs, n, TERM_WHITE);
1193             cs = k;
1194             break;
1195         }
1196
1197         k = isupper(c) ? (26 + c - 'A') : -1;
1198         if (k >= 26 && k < n) {
1199             display_realm_cursor(cs, n, TERM_WHITE);
1200             cs = k;
1201             break;
1202         }
1203
1204         if (c == '=') {
1205             screen_save();
1206             do_cmd_options_aux(player_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
1207             screen_load();
1208         } else if (c != '2' && c != '4' && c != '6' && c != '8') {
1209             bell();
1210         }
1211     }
1212
1213     display_realm_cursor(cs, n, TERM_YELLOW);
1214     return cs + 1;
1215 }
1216
1217 /*!
1218  * @brief 領域選択
1219  * @param player_ptr プレイヤー情報への参照ポインタ
1220  * @return 領域番号
1221  */
1222 byte select_element_realm(PlayerType *player_ptr)
1223 {
1224     clear_from(10);
1225
1226     int realm_max = enum2i(ElementRealmType::MAX);
1227     int realm_idx = 1;
1228     int row = 16;
1229     while (1) {
1230         put_str(
1231             _("注意:元素系統の選択によりあなたが習得する呪文のタイプが決まります。", "Note: The system of element will determine which spells you can learn."),
1232             23, 5);
1233
1234         for (int i = 0; i < realm_max; i++) {
1235             display_realm_cursor(i, realm_max - 1, TERM_WHITE);
1236         }
1237
1238         realm_idx = get_element_realm(player_ptr, realm_idx - 1, realm_max - 1);
1239         if (realm_idx == 255) {
1240             break;
1241         }
1242
1243         auto realm = i2enum<ElementRealmType>(realm_idx);
1244         display_wrap_around(element_texts.at(realm), 74, row, 3);
1245
1246         if (get_check_strict(player_ptr, _("よろしいですか?", "Are you sure? "), CHECK_DEFAULT_Y)) {
1247             break;
1248         }
1249
1250         clear_from(row);
1251     }
1252
1253     clear_from(10);
1254     return (byte)realm_idx;
1255 }
1256
1257 /*!
1258  * @brief クラスパワー情報を追加
1259  * @param player_ptr プレイヤー情報への参照ポインタ
1260  * @param rc_ptr レイシャルパワー情報への参照ポインタ
1261  */
1262 void switch_element_racial(PlayerType *player_ptr, rc_type *rc_ptr)
1263 {
1264     auto plev = player_ptr->lev;
1265     auto realm = i2enum<ElementRealmType>(player_ptr->element);
1266     rpi_type rpi;
1267     switch (realm) {
1268     case ElementRealmType::FIRE:
1269         rpi = rpi_type(_("ライト・エリア", "Light area"));
1270         rpi.text = _("光源が照らしている範囲か部屋全体を永久に明るくする。", "Lights up nearby area and the inside of a room permanently.");
1271         rpi.min_level = 3;
1272         rpi.cost = 5;
1273         rpi.stat = A_WIS;
1274         rpi.fail = 10;
1275         rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1276         break;
1277     case ElementRealmType::ICE:
1278         rpi = rpi_type(_("周辺フリーズ", "Sleep monsters"));
1279         rpi.info = format("%s%d", KWD_POWER, 20 + plev * 3 / 2);
1280         rpi.text = _("視界内の全てのモンスターを眠らせる。抵抗されると無効。", "Attempts to put all monsters in sight to sleep.");
1281         rpi.min_level = 10;
1282         rpi.cost = 15;
1283         rpi.stat = A_WIS;
1284         rpi.fail = 25;
1285         rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1286         break;
1287     case ElementRealmType::SKY:
1288         rpi = rpi_type(_("魔力充填", "Recharging"));
1289         rpi.info = format("%s%d", KWD_POWER, 120);
1290         rpi.text = _("杖/魔法棒の充填回数を増やすか、充填中のロッドの充填時間を減らす。", "Recharges staffs, wands or rods.");
1291         rpi.min_level = 20;
1292         rpi.cost = 15;
1293         rpi.stat = A_WIS;
1294         rpi.fail = 25;
1295         rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1296         break;
1297     case ElementRealmType::SEA:
1298         rpi = rpi_type(_("岩石溶解", "Stone to mud"));
1299         rpi.text = _("壁を溶かして床にする。", "Turns one rock square to mud.");
1300         rpi.min_level = 5;
1301         rpi.cost = 5;
1302         rpi.stat = A_WIS;
1303         rpi.fail = 10;
1304         rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1305         break;
1306     case ElementRealmType::DARKNESS:
1307         rpi = rpi_type(_("闇の扉", "Door to darkness"));
1308         rpi.info = format("%s%d", KWD_SPHERE, 15 + plev / 2);
1309         rpi.min_level = 5;
1310         rpi.cost = 5 + plev / 7;
1311         rpi.stat = A_WIS;
1312         rpi.fail = 20;
1313         rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1314         break;
1315     case ElementRealmType::CHAOS:
1316         rpi = rpi_type(_("現実変容", "Alter reality"));
1317         rpi.text = _("現在の階を再構成する。", "Recreates current dungeon level.");
1318         rpi.min_level = 35;
1319         rpi.cost = 30;
1320         rpi.stat = A_WIS;
1321         rpi.fail = 40;
1322         rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1323         break;
1324     case ElementRealmType::EARTH:
1325         rpi = rpi_type(_("地震", "Earthquake"));
1326         rpi.info = format("%s%d", KWD_SPHERE, 10);
1327         rpi.text = _("周囲のダンジョンを揺らし、壁と床をランダムに入れ変える。", "Shakes dungeon structure, and results in random swapping of floors and walls.");
1328         rpi.min_level = 25;
1329         rpi.cost = 15;
1330         rpi.stat = A_WIS;
1331         rpi.fail = 20;
1332         rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1333         break;
1334     case ElementRealmType::DEATH:
1335         rpi = rpi_type(_("増殖阻止", "Sterilization"));
1336         rpi.text = _("この階の増殖するモンスターが増殖できなくなる。", "Prevents any breeders on current level from breeding.");
1337         rpi.min_level = 5;
1338         rpi.cost = 5;
1339         rpi.stat = A_WIS;
1340         rpi.fail = 20;
1341         rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1342         break;
1343     default:
1344         break;
1345     }
1346 }
1347
1348 /*!
1349  * @todo 宣言だけ。後日適切な場所に移動
1350  */
1351 static bool door_to_darkness(PlayerType *player_ptr, POSITION dist);
1352
1353 /*!
1354  * @brief クラスパワーを実行
1355  * @param player_ptr プレイヤー情報への参照ポインタ
1356  * @return 実行したらTRUE、しなかったらFALSE
1357  */
1358 bool switch_element_execution(PlayerType *player_ptr)
1359 {
1360     auto realm = i2enum<ElementRealmType>(player_ptr->element);
1361     PLAYER_LEVEL plev = player_ptr->lev;
1362     DIRECTION dir;
1363
1364     switch (realm) {
1365     case ElementRealmType::FIRE:
1366         (void)lite_area(player_ptr, damroll(2, plev / 2), plev / 10);
1367         break;
1368     case ElementRealmType::ICE:
1369         (void)project(player_ptr, 0, 5, player_ptr->y, player_ptr->x, 1, AttributeType::COLD, PROJECT_ITEM);
1370         (void)project_all_los(player_ptr, AttributeType::OLD_SLEEP, 20 + plev * 3 / 2);
1371         break;
1372     case ElementRealmType::SKY:
1373         (void)recharge(player_ptr, 120);
1374         break;
1375     case ElementRealmType::SEA:
1376         if (!get_aim_dir(player_ptr, &dir)) {
1377             return false;
1378         }
1379         (void)wall_to_mud(player_ptr, dir, plev * 3 / 2);
1380         break;
1381     case ElementRealmType::DARKNESS:
1382         return door_to_darkness(player_ptr, 15 + plev / 2);
1383         break;
1384     case ElementRealmType::CHAOS:
1385         reserve_alter_reality(player_ptr, randint0(21) + 15);
1386         break;
1387     case ElementRealmType::EARTH:
1388         (void)earthquake(player_ptr, player_ptr->y, player_ptr->x, 10, 0);
1389         break;
1390     case ElementRealmType::DEATH:
1391         if (player_ptr->current_floor_ptr->num_repro <= MAX_REPRODUCTION) {
1392             player_ptr->current_floor_ptr->num_repro += MAX_REPRODUCTION;
1393         }
1394         break;
1395     default:
1396         return false;
1397     }
1398
1399     return true;
1400 }
1401
1402 /*!
1403  * @brief 指定したマスが暗いかどうか
1404  * @param f_ptr 階の情報への参照ポインタ
1405  * @param y 指定のy座標
1406  * @param x 指定のx座標
1407  * @return 暗いならTRUE、そうでないならFALSE
1408  */
1409 static bool is_target_grid_dark(FloorType *f_ptr, POSITION y, POSITION x)
1410 {
1411     if (any_bits(f_ptr->grid_array[y][x].info, CAVE_MNLT)) {
1412         return false;
1413     }
1414
1415     bool is_dark = false;
1416     bool is_lite = any_bits(f_ptr->grid_array[y][x].info, CAVE_GLOW | CAVE_LITE);
1417
1418     for (int dx = x - 2; dx <= x + 2; dx++) {
1419         for (int dy = y - 2; dy <= y + 2; dy++) {
1420             if (dx == x && dy == y) {
1421                 continue;
1422             }
1423             if (!in_bounds(f_ptr, dy, dx)) {
1424                 continue;
1425             }
1426
1427             MONSTER_IDX m_idx = f_ptr->grid_array[dy][dx].m_idx;
1428             if (!m_idx) {
1429                 continue;
1430             }
1431
1432             POSITION d = distance(dy, dx, y, x);
1433             auto *r_ptr = &monraces_info[f_ptr->m_list[m_idx].r_idx];
1434             if (d <= 1 && r_ptr->brightness_flags.has_any_of({ MonsterBrightnessType::HAS_LITE_1, MonsterBrightnessType::SELF_LITE_1 })) {
1435                 return false;
1436             }
1437             if (d <= 2 && r_ptr->brightness_flags.has_any_of({ MonsterBrightnessType::HAS_LITE_2, MonsterBrightnessType::SELF_LITE_2 })) {
1438                 return false;
1439             }
1440             if (d <= 1 && r_ptr->brightness_flags.has_any_of({ MonsterBrightnessType::HAS_DARK_1, MonsterBrightnessType::SELF_DARK_1 })) {
1441                 is_dark = true;
1442             }
1443             if (d <= 2 && r_ptr->brightness_flags.has_any_of({ MonsterBrightnessType::HAS_DARK_2, MonsterBrightnessType::SELF_DARK_2 })) {
1444                 is_dark = true;
1445             }
1446         }
1447     }
1448
1449     return !is_lite || is_dark;
1450 }
1451
1452 /*!
1453  * @breif 暗いところ限定での次元の扉
1454  * @param player_ptr プレイヤー情報への参照ポインタ
1455  */
1456 static bool door_to_darkness(PlayerType *player_ptr, POSITION dist)
1457 {
1458     POSITION y = player_ptr->y;
1459     POSITION x = player_ptr->x;
1460     FloorType *f_ptr;
1461
1462     for (int i = 0; i < 3; i++) {
1463         if (!tgt_pt(player_ptr, &x, &y)) {
1464             return false;
1465         }
1466
1467         f_ptr = player_ptr->current_floor_ptr;
1468
1469         if (distance(y, x, player_ptr->y, player_ptr->x) > dist) {
1470             msg_print(_("遠すぎる!", "That is too far!"));
1471             continue;
1472         }
1473
1474         if (!is_cave_empty_bold(player_ptr, y, x) || f_ptr->grid_array[y][x].is_icky()) {
1475             msg_print(_("そこには移動できない。", "Can not teleport to there."));
1476             continue;
1477         }
1478
1479         break;
1480     }
1481
1482     bool flag = cave_player_teleportable_bold(player_ptr, y, x, TELEPORT_SPONTANEOUS) && is_target_grid_dark(f_ptr, y, x);
1483     if (flag) {
1484         teleport_player_to(player_ptr, y, x, TELEPORT_SPONTANEOUS);
1485     } else {
1486         msg_print(_("闇の扉は開かなかった!", "The door to darkness does not open!"));
1487     }
1488     return true;
1489 }