OSDN Git Service

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