OSDN Git Service

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