OSDN Git Service

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