OSDN Git Service

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