OSDN Git Service

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