5 #include "mind-elementalist.h"
6 #include "action/action-limited.h"
7 #include "cmd-action/cmd-spell.h"
8 #include "cmd-action/cmd-mind.h"
9 #include "cmd-io/cmd-gameoption.h"
10 #include "core/asking-player.h"
11 #include "core/hp-mp-processor.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 "game-option/disturbance-options.h"
22 #include "game-option/input-options.h"
23 #include "game-option/text-display-options.h"
24 #include "io/command-repeater.h"
25 #include "io/input-key-acceptor.h"
26 #include "io/input-key-requester.h"
27 #include "main/sound-definitions-table.h"
28 #include "main/sound-of-music.h"
29 #include "mind/mind-explanations-table.h"
30 #include "monster/monster-describer.h"
31 #include "monster-race/monster-race.h"
32 #include "monster-race/race-flags3.h"
33 #include "monster-race/race-flags-resistance.h"
34 #include "mind/mind-mindcrafter.h"
35 #include "player/player-status-table.h"
36 #include "player-info/avatar.h"
37 #include "player-status/player-status-base.h"
38 #include "racial/racial-util.h"
39 #include "spell-kind/earthquake.h"
40 #include "spell-kind/spells-charm.h"
41 #include "spell-kind/spells-detection.h"
42 #include "spell-kind/spells-genocide.h"
43 #include "spell-kind/spells-launcher.h"
44 #include "spell-kind/spells-lite.h"
45 #include "spell-kind/magic-item-recharger.h"
46 #include "spell-kind/spells-beam.h"
47 #include "spell-kind/spells-sight.h"
48 #include "spell-kind/spells-world.h"
49 #include "status/bad-status-setter.h"
50 #include "status/base-status.h"
51 #include "system/game-option-types.h"
52 #include "util/bit-flags-calculator.h"
53 #include "util/buffer-shaper.h"
54 #include "util/int-char-converter.h"
55 #include "target/target-getter.h"
56 #include "term/screen-processor.h"
57 #include "term/term-color-types.h"
58 #include "view/display-messages.h"
61 #include <unordered_map>
66 enum class ElementSpells {
91 std::string_view title; //!< 領域名
92 std::array<spells_type, 3> type; //!< 属性タイプリスト
93 std::array<std::string_view, 3> name; //!< 属性名リスト
99 struct element_power {
100 int elem; //!< 使用属性番号
101 mind_type info; //!< 難易度構造体
104 using element_type_list = const std::unordered_map<ElementRealm, element_type>;
105 using element_power_list = const std::unordered_map<ElementSpells, element_power>;
106 using element_tip_list = const std::unordered_map<ElementSpells, std::string_view>;
107 using element_text_list = const std::unordered_map<ElementRealm, std::string_view>;
113 static element_type_list element_types = {
115 ElementRealm::FIRE, {
117 { GF_FIRE, GF_PLASMA, GF_HOLY_FIRE },
118 { _("火炎", "Fire"), _("プラズマ", "Plasma"), _("聖気", "Holy Fire") },
124 { GF_COLD, GF_INERTIAL, GF_TIME },
125 { _("冷気", "Ice"), _("遅鈍", "Inertial"), _("時間逆転", "Time Stream") },
131 { GF_ELEC, GF_LITE, GF_MANA },
132 { _("電撃", "Lightning"), _("光", "Light"), _("魔力", "Mana") },
138 { GF_ACID, GF_WATER, GF_DISINTEGRATE },
139 { _("酸", "Acid"), _("水", "Water"), _("分解", "Disintegrate") },
143 ElementRealm::DARKNESS, {
145 { GF_DARK, GF_NETHER, GF_HELL_FIRE },
146 { _("暗黒", "Darkness"), _("地獄", "Nether"), _("業火", "Hell Fire") },
150 ElementRealm::CHAOS, {
152 { GF_CONFUSION, GF_NEXUS, GF_CHAOS },
153 { _("混乱", "Confusion"), _("因果混乱", "Nexus"), _("カオス", "Chaos") },
157 ElementRealm::EARTH, {
159 { GF_SHARDS, GF_FORCE, GF_METEOR },
160 { _("破片", "Shard"), _("フォース", "Force"), _("隕石", "Meteo") },
164 ElementRealm::DEATH, {
166 { GF_POIS, GF_HYPODYNAMIA, GF_DISENCHANT },
167 { _("毒", "Poison"), _("吸血", "Drain Life"), _("劣化", "Disenchant") },
175 static element_power_list element_powers = {
176 { ElementSpells::BOLT_1ST, { 0, { 1, 1, 15, _("%sの矢", "%s Bolt") }}},
177 { ElementSpells::MON_DETECT, { 0, { 2, 1, 20, _("モンスター感知", "Detect Monsters") }}},
178 { ElementSpells::PERCEPT, { 0, { 5, 5, 50, _("擬似鑑定", "Psychometry") }}},
179 { ElementSpells::CURE, { 0, { 6, 5, 35, _("傷の治癒", "Cure Wounds") }}},
180 { ElementSpells::BOLT_2ND, { 1, { 8, 6, 35, _("%sの矢", "%s Bolt") }}},
181 { ElementSpells::MAG_DETECT, { 0, { 10, 8, 60, _("魔法感知", "Detect Magical Objs") }}},
182 { ElementSpells::BEAM_1ST, { 0, { 15, 10, 45, _("%sの鞭", "%s Whip") }}},
183 { ElementSpells::BALL_1ST, { 0, { 18, 15, 70, _("%sの球", "%s Ball") }}},
184 { ElementSpells::BREATH_2ND, { 1, { 21, 20, 70, _("%sのブレス", "Breath of %s") }}},
185 { ElementSpells::ANNIHILATE, { 0, { 24, 20, 75, _("モンスター消滅", "Annihilation") }}},
186 { ElementSpells::BOLT_3RD, { 2, { 25, 15, 60, _("%sの矢", "%s Bolt") }}},
187 { ElementSpells::WAVE_1ST, { 0, { 28, 30, 75, _("元素の波動", "Elemental Wave") }}},
188 { ElementSpells::BALL_2ND, { 1, { 28, 22, 75, _("%sの球", "%s Ball") }}},
189 { ElementSpells::BURST_1ST, { 0, { 33, 35, 75, _("精気乱射", "%s Blast") }}},
190 { ElementSpells::STORM_2ND, { 1, { 35, 30, 75, _("%sの嵐", "%s Storm") }}},
191 { ElementSpells::BREATH_1ST, { 0, { 42, 48, 75, _("%sのブレス", "Breath of %s") }}},
192 { ElementSpells::STORM_3ND, { 2, { 45, 60, 80, _("%sの嵐", "%s Storm") }}},
198 static element_tip_list element_tips = {
199 { ElementSpells::BOLT_1ST,
200 _("弱い%sの矢を放つ。", "Fire a weak bolt of %s.") },
201 { ElementSpells::MON_DETECT,
202 _("近くの全てのモンスターを感知する。", "Detects monsters.") },
203 { ElementSpells::PERCEPT,
204 _("アイテムの雰囲気を知る。", "Gives feeling of an item.") },
205 { ElementSpells::CURE,
206 _("怪我と体力を少し回復させる。", "Heals HP and wounds a bit.") },
207 { ElementSpells::BOLT_2ND,
208 _("%sの矢を放つ。", "Fire a bolt of %s.") },
209 { ElementSpells::MAG_DETECT,
210 _("近くの魔法のアイテムを感知する。", "Detects magic devices.") },
211 { ElementSpells::BEAM_1ST,
212 _("高威力で射程が短い%sのビームを放つ。", "Fire a short and strong beam of %s.") },
213 { ElementSpells::BALL_1ST,
214 _("%sの球を放つ。", "Fire a ball of %s.") },
215 { ElementSpells::BREATH_2ND,
216 _("%sのブレスを吐く。", "Fire a breath of %s.") },
217 { ElementSpells::ANNIHILATE,
218 _("%s耐性のないモンスターを1体抹殺する。", "Erase a monster unless it resists %s.") },
219 { ElementSpells::BOLT_3RD,
220 _("%sの矢を放つ。", "Fire a bolt of %s.") },
221 { ElementSpells::WAVE_1ST,
222 _("視界内の全ての敵に%sによるダメージを与える。", "Inflict all monsters with %s damage.") },
223 { ElementSpells::BALL_2ND,
224 _("%sの球を放つ。", "Fire a ball of %s.") },
225 { ElementSpells::BURST_1ST,
226 _("ランダムな方向に%sの矢を放つ。", "Fire some bolts of %s toward random direction.") },
227 { ElementSpells::STORM_2ND,
228 _("%sの巨大な球を放つ。", "Fire a large ball of %s.") },
229 { ElementSpells::BREATH_1ST,
230 _("%sのブレスを吐く。", "Fire a breath of %s.") },
231 { ElementSpells::STORM_3ND,
232 _("%sの巨大な球を放つ。", "Fire a large ball of %s.") },
236 * @brief 元素魔法選択時説明文定義
238 static element_text_list element_texts = {
239 { ElementRealm::FIRE,
240 _("炎系統は巨大なエネルギーで灼熱を生み出し、全ての敵を燃やし尽くそうとします。",
241 "Great energy of Fire system will be able to burn out all of your enemies.")},
243 _("氷系統の魔法はその冷たさで敵の動きを奪い尽くし、魂すらも止めてしまうでしょう。",
244 "Ice system will be freeze your enemies even their souls.")},
246 _("空系統は大いなる天空のエネルギーを駆使して敵の全てを撃滅できます。",
247 "Sky system can terminate all of your enemies powerfully with enery of the great sky.")},
249 _("海系統はその敵の全てを溶かし、大いなる海へと返してしまいます。",
250 "Sea system melts all of your enemies and returns them to the great oscean.")},
251 { ElementRealm::DARKNESS,
252 _("闇系統は恐るべき力を常闇から引き出し、敵を地獄へと叩き落とすでしょう。",
253 "Dark system buries all of your enemies by scary power from dark hell.")},
254 { ElementRealm::CHAOS,
255 _("混沌系統は敵の意識も条理も捻じ曲げ、その存在をあの世に送ってしまいます。",
256 "Chaos system twists and wraps your enemies even their souls and kill all of them resultly.")},
257 { ElementRealm::EARTH,
258 _("地系統は偉大なる大地の力を呼び出して、数多の敵のことごとくを粉砕しようとします。",
259 "Earth system smash all of your enemies massively using its huge powers.")},
260 { ElementRealm::DEATH,
261 _("瘴気系統は全ての生ける者にとって途轍もない毒です。",
262 "Death system is doomed poison for all of living your ememies.")},
269 * @param realm_idx 領域番号
272 concptr get_element_title(int realm_idx)
274 auto realm = static_cast<ElementRealm>(realm_idx);
275 return element_types.at(realm).title.data();
279 * @brief 元素魔法領域の属性リストを返す
280 * @param realm_idx 領域番号
281 * @return 領域で使用できる属性リスト
283 static std::array<spells_type, 3> get_element_types(int realm_idx)
285 auto realm = static_cast<ElementRealm>(realm_idx);
286 return element_types.at(realm).type;
290 * @brief 元素魔法領域のn番目の属性を返す
291 * @param realm_idx 領域番号
295 spells_type get_element_type(int realm_idx, int n)
297 return get_element_types(realm_idx)[n];
301 * @brief 元素魔法領域の属性名リストを返す
302 * @param realm_idx 領域番号
303 * @return 領域で使用できる属性の名称リスト
305 static std::array<std::string_view, 3> get_element_names(int realm_idx)
307 auto realm = static_cast<ElementRealm>(realm_idx);
308 return element_types.at(realm).name;
312 * @brief 元素魔法領域のn番目の属性名を返す
313 * @param realm_idx 領域番号
317 concptr get_element_name(int realm_idx, int n)
319 return get_element_names(realm_idx)[n].data();
324 * @param caster_ptr プレイヤー情報への参照ポインタ
325 * @param spell_idx 呪文番号
328 static concptr get_element_tip(player_type *caster_ptr, int spell_idx)
330 auto realm = static_cast<ElementRealm>(caster_ptr->realm1);
331 auto spell = static_cast<ElementSpells>(spell_idx);
332 auto elem = element_powers.at(spell).elem;
333 return format(element_tips.at(spell).data(), element_types.at(realm).name[elem].data());
338 * @param caster_ptr プレイヤー情報への参照ポインタ
339 * @param spell_idx 呪文番号
342 static int get_elemental_elem(player_type *caster_ptr, int spell_idx)
345 auto spell = static_cast<ElementSpells>(spell_idx);
346 return element_powers.at(spell).elem;
350 * @brief 元素魔法呪文の難易度データを取得
351 * @param caster_ptr プレイヤー情報への参照ポインタ
352 * @param spell_idx 呪文番号
355 static mind_type get_elemental_info(player_type *caster_ptr, int spell_idx)
358 auto spell = static_cast<ElementSpells>(spell_idx);
359 return element_powers.at(spell).info;
363 * @brief 元素魔法呪文の効果表示文字列を取得
364 * @param caster_ptr プレイヤー情報への参照ポインタ
365 * @param spell_idx 呪文番号
369 void get_element_effect_info(player_type *caster_ptr, int spell_idx, char *p)
371 PLAYER_LEVEL plev = caster_ptr->lev;
372 auto spell = static_cast<ElementSpells>(spell_idx);
376 case ElementSpells::BOLT_1ST:
377 sprintf(p, " %s%dd%d", KWD_DAM, 3 + ((plev - 1) / 5), 4);
379 case ElementSpells::CURE:
380 sprintf(p, " %s%dd%d", KWD_HEAL, 2, 8);
382 case ElementSpells::BOLT_2ND:
383 sprintf(p, " %s%dd%d", KWD_DAM, 8 + ((plev - 5) / 4), 8);
385 case ElementSpells::BEAM_1ST:
386 sprintf(p, " %s%d", KWD_DAM, (50 + plev * 2));
388 case ElementSpells::BALL_1ST:
389 sprintf(p, " %s%d", KWD_DAM, 55 + plev);
391 case ElementSpells::BREATH_2ND:
392 dam = p_ptr->chp / 2;
393 sprintf(p, " %s%d", KWD_DAM, (dam > 120) ? 120 : dam);
395 case ElementSpells::ANNIHILATE:
396 sprintf(p, " %s%d", _("効力:", "pow "), 50 + plev);
398 case ElementSpells::BOLT_3RD:
399 sprintf(p, " %s%dd%d", KWD_DAM, 12 + ((plev - 5) / 4), 8);
401 case ElementSpells::WAVE_1ST:
402 sprintf(p, " %sd%d", KWD_DAM, plev * 4);
404 case ElementSpells::BALL_2ND:
405 sprintf(p, " %s%d", KWD_DAM, 75 + plev);
407 case ElementSpells::BURST_1ST:
408 sprintf(p, " %s%dd%d", KWD_DAM, 6 + plev / 8, 8);
410 case ElementSpells::STORM_2ND:
411 sprintf(p, " %s%d", KWD_DAM, 120 + plev * 2);
413 case ElementSpells::BREATH_1ST:
414 sprintf(p, " %s%d", KWD_DAM, p_ptr->chp * 2 / 3);
416 case ElementSpells::STORM_3ND:
417 sprintf(p, " %s%d", KWD_DAM, 300 + plev * 5);
427 * @param caster_ptr プレイヤー情報への参照ポインタ
428 * @param spell_idx 呪文番号
429 * @return 実行したらTRUE、キャンセルならFALSE
431 static bool cast_element_spell(player_type *caster_ptr, SPELL_IDX spell_idx)
433 auto spell = static_cast<ElementSpells>(spell_idx);
434 auto types = get_element_types(caster_ptr->realm1);
436 PLAYER_LEVEL plev = caster_ptr->lev;
442 case ElementSpells::BOLT_1ST:
443 if (!get_aim_dir(caster_ptr, &dir))
445 dam = damroll(3 + ((plev - 1) / 5), 4);
446 (void)fire_bolt(caster_ptr, types[0], dir, dam);
448 case ElementSpells::MON_DETECT:
449 (void)detect_monsters_normal(caster_ptr, DETECT_RAD_DEFAULT);
450 (void)detect_monsters_invis(caster_ptr, DETECT_RAD_DEFAULT);
452 case ElementSpells::PERCEPT:
453 return psychometry(caster_ptr);
454 case ElementSpells::CURE:
455 (void)hp_player(caster_ptr, damroll(2, 8));
456 (void)set_cut(caster_ptr, caster_ptr->cut - 10);
458 case ElementSpells::BOLT_2ND:
459 if (!get_aim_dir(caster_ptr, &dir))
461 dam = damroll(8 + ((plev - 5) / 4), 8);
462 if (fire_bolt_or_beam(caster_ptr, plev, types[1], dir, dam)) {
463 if (types[1] == GF_HYPODYNAMIA) {
464 (void)hp_player(caster_ptr, dam / 2);
468 case ElementSpells::MAG_DETECT:
469 (void)detect_objects_magic(caster_ptr, DETECT_RAD_DEFAULT);
471 case ElementSpells::BEAM_1ST:
473 if (!get_aim_dir(caster_ptr, &dir))
475 (void)fire_beam(caster_ptr, types[0], dir, 50 + plev * 2);
478 case ElementSpells::BALL_1ST:
479 if (!get_aim_dir(caster_ptr, &dir))
482 (void)fire_ball(caster_ptr, types[0], dir, dam, 2);
484 case ElementSpells::BREATH_2ND:
485 if (!get_aim_dir(caster_ptr, &dir))
487 dam = caster_ptr->chp / 2;
488 if (fire_breath(caster_ptr, types[1], dir, dam, 3)) {
489 if (types[1] == GF_HYPODYNAMIA) {
490 (void)hp_player(caster_ptr, dam / 2);
494 case ElementSpells::ANNIHILATE:
495 if (!get_aim_dir(caster_ptr, &dir))
497 fire_ball_hide(caster_ptr, GF_E_GENOCIDE, dir, plev + 50, 0);
499 case ElementSpells::BOLT_3RD:
500 if (!get_aim_dir(caster_ptr, &dir))
502 dam = damroll(12 + ((plev - 5) / 4), 8);
503 fire_bolt_or_beam(caster_ptr, plev, types[2], dir, dam);
505 case ElementSpells::WAVE_1ST:
506 dam = randint1(plev * 4);
507 project_all_los(caster_ptr, types[0], dam);
509 case ElementSpells::BALL_2ND:
510 if (!get_aim_dir(caster_ptr, &dir))
513 if (fire_ball(caster_ptr, types[1], dir, dam, 2)) {
514 if (types[1] == GF_HYPODYNAMIA) {
515 (void)hp_player(caster_ptr, dam / 2);
519 case ElementSpells::BURST_1ST:
523 for (int k = 0; k < num; k++) {
526 scatter(caster_ptr, &y, &x, caster_ptr->y, caster_ptr->x, 4, PROJECT_NONE);
527 if (!player_bold(caster_ptr, y, x))
530 project(caster_ptr, 0, 0, y, x, damroll(6 + plev / 8, 10), types[0], (PROJECT_BEAM | PROJECT_THRU | PROJECT_GRID | PROJECT_KILL), -1);
533 case ElementSpells::STORM_2ND:
534 if (!get_aim_dir(caster_ptr, &dir))
536 dam = 125 + plev * 2;
537 if (fire_ball(caster_ptr, types[1], dir, dam, 4)) {
538 if (types[1] == GF_HYPODYNAMIA) {
539 (void)hp_player(caster_ptr, dam / 2);
543 case ElementSpells::BREATH_1ST:
544 if (!get_aim_dir(caster_ptr, &dir))
546 dam = caster_ptr->chp * 2 / 3;
547 (void)fire_breath(caster_ptr, types[0], dir, dam, 3);
549 case ElementSpells::STORM_3ND:
550 if (!get_aim_dir(caster_ptr, &dir))
552 dam = 300 + plev * 5;
553 (void)fire_ball(caster_ptr, types[2], dir, dam, 5);
563 * @brief 元素魔法呪文の失敗率を計算
564 * @param caster_ptr プレイヤー情報への参照ポインタ
565 * @param spell_idx 呪文番号
568 static PERCENTAGE decide_element_chance(player_type *caster_ptr, mind_type spell)
570 PERCENTAGE chance = spell.fail;
572 chance -= 3 * (caster_ptr->lev - spell.min_lev);
573 chance += caster_ptr->to_m_chance;
574 chance -= 3 * (adj_mag_stat[caster_ptr->stat_index[A_WIS]] - 1);
576 PERCENTAGE minfail = adj_mag_fail[caster_ptr->stat_index[A_WIS]];
577 if (chance < minfail)
580 if (caster_ptr->stun > 50)
582 else if (caster_ptr->stun)
585 if (heavy_armor(caster_ptr))
588 if (caster_ptr->icky_wield[0])
591 if (caster_ptr->icky_wield[1])
601 * @brief 元素魔法呪文の消費MPを計算
602 * @param caster_ptr プレイヤー情報への参照ポインタ
603 * @param spell_idx 呪文番号
606 static MANA_POINT decide_element_mana_cost(player_type *caster_ptr, mind_type spell)
609 return spell.mana_cost;
613 * @brief 元素魔法呪文を選択して取得
614 * @param caster_ptr プレイヤー情報への参照ポインタ
616 * @param only_browse 閲覧モードかどうか
617 * @return 選んだらTRUE、選ばなかったらFALSE
619 bool get_element_power(player_type *caster_ptr, SPELL_IDX *sn, bool only_browse)
625 PLAYER_LEVEL plev = caster_ptr->lev;
632 int menu_line = (use_menu ? 1 : 0);
635 if (repeat_pull(&code)) {
636 *sn = (SPELL_IDX)code;
637 if (get_elemental_info(caster_ptr, *sn).min_lev <= plev)
641 concptr p = _("元素魔法", "magic");
645 for (i = 0; i < static_cast<SPELL_IDX>(ElementSpells::MAX); i++) {
646 if (get_elemental_info(caster_ptr, i).min_lev <= plev)
651 (void)strnfmt(out_val, 78,
652 _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
653 p, I2A(0), I2A(num - 1), p);
655 (void)strnfmt(out_val, 78,
656 _("(%^s %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
657 p, I2A(0), I2A(num - 1), p);
659 if (use_menu && !only_browse)
664 choice = (always_show_list || use_menu) ? ESCAPE : 1;
666 if (choice == ESCAPE)
668 else if (!get_com(out_val, &choice, TRUE))
671 if (use_menu && choice != ' ') {
680 menu_line += (num - 1);
700 int spell_max = static_cast<int>(ElementSpells::MAX);
701 if ((choice == ' ') || (choice == '*') || (choice == '?') || (use_menu && ask)) {
702 if (!redraw || use_menu) {
706 if (!only_browse && !use_menu)
710 put_str(_("名前", "Name"), y, x + 5);
711 put_str(_("Lv MP 失率 効果", "Lv MP Fail Info"), y, x + 35);
712 for (i = 0; i < spell_max; i++) {
713 elem = get_elemental_elem(caster_ptr, i);
714 spell = get_elemental_info(caster_ptr, i);
716 if (spell.min_lev > plev)
719 PERCENTAGE chance = decide_element_chance(caster_ptr, spell);
720 int mana_cost = decide_element_mana_cost(caster_ptr, spell);
721 get_element_effect_info(caster_ptr, i, comment);
724 if (i == (menu_line - 1))
725 strcpy(desc, _(" 》 ", " > "));
729 sprintf(desc, " %c) ", I2A(i));
731 concptr s = get_element_name(caster_ptr->realm1, elem);
732 sprintf(name, spell.name, s);
734 format("%-30s%2d %4d %3d%%%s", name, spell.min_lev, mana_cost, chance, comment));
735 prt(desc, y + i + 1, x);
738 prt("", y + i + 1, x);
739 } else if (!only_browse) {
748 ask = isupper(choice);
750 choice = (char)tolower(choice);
752 i = (islower(choice) ? A2I(choice) : -1);
755 if ((i < 0) || (i >= num)) {
760 spell = get_elemental_info(caster_ptr, i);
763 (void)strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), spell.name);
764 if (!get_check(tmp_val))
771 if (redraw && !only_browse)
774 set_bits(caster_ptr->window_flags, PW_SPELL);
775 handle_stuff(caster_ptr);
780 repeat_push((COMMAND_CODE)i);
785 * @brief 元素魔法呪文をMPがなくても挑戦するか確認する
786 * @param caster_ptr プレイヤー情報への参照ポインタ
787 * @param mana_cost 消費MP
788 * @return 詠唱するならTRUE、しないならFALSE
790 static bool check_element_mp_sufficiency(player_type *caster_ptr, int mana_cost)
792 if (mana_cost <= caster_ptr->csp)
795 msg_print(_("MPが足りません。", "You do not have enough mana to use this power."));
799 return get_check(_("それでも挑戦しますか? ", "Attempt it anyway? "));
803 * @brief 元素魔法呪文の詠唱を試み、成功なら詠唱し、失敗ならファンブルする
804 * @param caster_ptr プレイヤー情報への参照ポインタ
805 * @param spell_idx 呪文番号
807 * @return 詠唱して実行したらTRUE、されなかったらFALSE
809 static bool try_cast_element_spell(player_type *caster_ptr, SPELL_IDX spell_idx, PERCENTAGE chance)
811 if (randint0(100) >= chance) {
813 return cast_element_spell(caster_ptr, spell_idx);
819 msg_format(_("魔力の集中に失敗した!", "You failed to concentrate hard enough for Mana!"));
822 if (randint1(100) < chance / 2) {
823 int plev = caster_ptr->lev;
824 msg_print(_("元素の力が制御できない氾流となって解放された!",
825 "Elemental power unleashes its power in an uncontrollable storm!"));
826 project(caster_ptr, PROJECT_WHO_UNCTRL_POWER, 2 + plev / 10, caster_ptr->y, caster_ptr->x, plev * 2,
827 get_element_types(caster_ptr->realm1)[0],
828 PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM, -1);
829 caster_ptr->csp = MAX(0, caster_ptr->csp - plev * MAX(1, plev / 10));
836 * @brief 元素魔法コマンドのメインルーチン
837 * @param caster_ptr プレイヤー情報への参照ポインタ
840 void do_cmd_element(player_type *caster_ptr)
843 if (cmd_limit_confused(caster_ptr) || !get_element_power(caster_ptr, &i, FALSE))
846 mind_type spell = get_elemental_info(caster_ptr, i);
847 PERCENTAGE chance = decide_element_chance(caster_ptr, spell);
848 int mana_cost = decide_element_mana_cost(caster_ptr, spell);
850 if (!check_element_mp_sufficiency(caster_ptr, mana_cost))
853 if (!try_cast_element_spell(caster_ptr, i, chance))
856 if (mana_cost <= caster_ptr->csp) {
857 caster_ptr->csp -= mana_cost;
859 int oops = mana_cost;
861 caster_ptr->csp_frac = 0;
862 msg_print(_("精神を集中しすぎて気を失ってしまった!", "You faint from the effort!"));
863 (void)set_paralyzed(caster_ptr, caster_ptr->paralyzed + randint1(5 * oops + 1));
864 chg_virtue(caster_ptr, V_KNOWLEDGE, -10);
865 if (randint0(100) < 50) {
866 bool perm = (randint0(100) < 25);
867 msg_print(_("体を悪くしてしまった!", "You have damaged your health!"));
868 (void)dec_stat(caster_ptr, A_CON, 15 + randint1(10), perm);
872 take_turn(caster_ptr, 100);
873 set_bits(caster_ptr->redraw, PR_MANA);
874 set_bits(caster_ptr->window_flags, PW_PLAYER | PW_SPELL);
878 * @brief 現在プレイヤーが使用可能な元素魔法の一覧表示
879 * @param caster_ptr プレイヤー情報への参照ポインタ
882 void do_cmd_element_browse(player_type *caster_ptr)
889 if (!get_element_power(caster_ptr, &n, TRUE)) {
894 term_erase(12, 21, 255);
895 term_erase(12, 20, 255);
896 term_erase(12, 19, 255);
897 term_erase(12, 18, 255);
898 term_erase(12, 17, 255);
899 term_erase(12, 16, 255);
900 shape_buffer(get_element_tip(caster_ptr, n), 62, temp, sizeof(temp));
901 for (int j = 0, line = 17; temp[j]; j += (1 + strlen(&temp[j]))) {
902 prt(&temp[j], line, 15);
906 prt(_("何かキーを押して下さい。", "Hit any key."), 0, 0);
913 * @brief 元素魔法の単体抹殺の効果を発動する
914 * @param caster_ptr プレイヤー情報への参照ポインタ
915 * @param em_ptr 魔法効果情報への参照ポインタ
916 * @return 効果処理を続けるかどうか
918 process_result effect_monster_elemental_genocide(player_type *caster_ptr, effect_monster_type *em_ptr)
920 auto types = get_element_types(caster_ptr->realm1);
923 monster_desc(caster_ptr, m_name, em_ptr->m_ptr, 0);
924 msg_format(_("%sが%sを包み込んだ。", "The %s surrounds %s."), types[0], m_name);
926 auto realm = static_cast<ElementRealm>(caster_ptr->realm1);
928 case ElementRealm::FIRE:
929 if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_IM_FIRE))
930 return PROCESS_CONTINUE;
932 case ElementRealm::ICE:
933 if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_IM_COLD))
934 return PROCESS_CONTINUE;
936 case ElementRealm::SKY:
937 if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_IM_ELEC))
938 return PROCESS_CONTINUE;
940 case ElementRealm::SEA:
941 if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_IM_ACID))
942 return PROCESS_CONTINUE;
944 case ElementRealm::DARKNESS:
945 if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_RES_DARK) || any_bits(em_ptr->r_ptr->r_flags3, RF3_HURT_LITE))
946 return PROCESS_CONTINUE;
948 case ElementRealm::CHAOS:
949 if (any_bits(em_ptr->r_ptr->r_flags3, RF3_NO_CONF))
950 return PROCESS_CONTINUE;
952 case ElementRealm::EARTH:
953 if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_RES_SHAR))
954 return PROCESS_CONTINUE;
956 case ElementRealm::DEATH:
957 if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_IM_POIS))
958 return PROCESS_CONTINUE;
961 return PROCESS_CONTINUE;
965 em_ptr->obvious = TRUE;
967 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"))) {
968 if (em_ptr->seen_msg)
969 msg_format(_("%sは消滅した!", "%^s disappeared!"), em_ptr->m_name);
970 chg_virtue(caster_ptr, V_VITALITY, -1);
974 em_ptr->skipped = TRUE;
975 return PROCESS_CONTINUE;
979 * @brief 元素領域とレベルの条件に見合うかチェックする
980 * @param caster_ptr プレイヤー情報への参照ポインタ
982 * @param lev プレイヤーレベル
983 * @return 見合うならTRUE、そうでなければFALSE
985 * レベルに応じて取得する耐性などの判定に使用する
987 bool has_element_resist(player_type *creature_ptr, ElementRealm realm, PLAYER_LEVEL lev)
989 auto prealm = static_cast<ElementRealm>(creature_ptr->realm1);
990 return (prealm == realm && creature_ptr->lev >= lev);
994 * @brief 領域選択時のカーソル表示(シンボル+領域名)
1000 static void display_realm_cursor(int i, int n, term_color_type color)
1007 name = _("ランダム", "Random");
1010 name = element_types.at(static_cast<ElementRealm>(i + 1)).title.data();
1012 sprintf(cur, "%c) %s", sym, name);
1014 c_put_str(color, cur, 12 + (i / 5), 2 + 15 * (i % 5));
1018 * @brief 領域選択時の移動キー処理
1024 static int interpret_realm_select_key(int cs, int n, char c)
1050 * @param creature_ptr プレイヤー情報への参照ポインタ
1054 static int get_element_realm(player_type *creature_ptr, int is, int n)
1056 int cs = MAX(0, is);
1061 sprintf(buf, _("領域を選んで下さい(%c-%c) ('='初期オプション設定): ", "Choose a realm (%c-%c) ('=' for options): "), I2A(0), I2A(n - 1));
1064 display_realm_cursor(os, n, TERM_WHITE);
1065 display_realm_cursor(cs, n, TERM_YELLOW);
1066 put_str(buf, 10, 10);
1070 cs = interpret_realm_select_key(cs, n, c);
1075 if (c == ' ' || c == '\r' || c == '\n') {
1077 display_realm_cursor(cs, n, TERM_WHITE);
1078 cs = randint0(n - 1);
1079 display_realm_cursor(cs, n, TERM_YELLOW);
1085 display_realm_cursor(cs, n, TERM_WHITE);
1086 cs = randint0(n - 1);
1087 display_realm_cursor(cs, n, TERM_YELLOW);
1091 k = islower(c) ? A2I(c) : -1;
1092 if (k >= 0 && k < n) {
1097 k = isupper(c) ? (26 + c - 'A') : -1;
1098 if (k >= 26 && k < n) {
1105 do_cmd_options_aux(creature_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
1107 } else if (c != '2' && c != '4' && c != '6' && c != '8')
1116 * @param creature_ptr プレイヤー情報への参照ポインタ
1119 byte select_element_realm(player_type *creature_ptr)
1123 int realm_max = static_cast<int>(ElementRealm::MAX);
1128 _("注意:元素系統の選択によりあなたが習得する呪文のタイプが決まります。", "Note: The system of element will determine which spells you can learn."),
1131 for (int i = 0; i < realm_max; i++) {
1132 display_realm_cursor(i, realm_max - 1, TERM_WHITE);
1135 realm_idx = get_element_realm(creature_ptr, realm_idx - 1, realm_max - 1);
1136 if (realm_idx == 255)
1139 auto realm = static_cast<ElementRealm>(realm_idx);
1141 shape_buffer(element_texts.at(realm).data(), 74, temp, sizeof(temp));
1143 for (int i = 0; i < 5; i++) {
1150 if (get_check_strict(creature_ptr, _("よろしいですか?", "Are you sure? "), CHECK_DEFAULT_Y))
1157 return (byte)realm_idx;
1161 * @brief クラスパワー情報を追加
1162 * @param creature_ptr プレイヤー情報への参照ポインタ
1163 * @param rc_ptr レイシャルパワー情報への参照ポインタ
1166 void switch_element_racial(player_type *creature_ptr, rc_type *rc_ptr)
1168 auto realm = static_cast<ElementRealm>(creature_ptr->realm1);
1170 case ElementRealm::FIRE:
1171 strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("ライト・エリア", "Light area"));
1172 rc_ptr->power_desc[rc_ptr->num].min_level = 3;
1173 rc_ptr->power_desc[rc_ptr->num].cost = 5;
1174 rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
1175 rc_ptr->power_desc[rc_ptr->num].fail = 10;
1176 rc_ptr->power_desc[rc_ptr->num++].number = -4;
1178 case ElementRealm::ICE:
1179 strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("フリーズ・モンスター", "Sleep monster"));
1180 rc_ptr->power_desc[rc_ptr->num].min_level = 10;
1181 rc_ptr->power_desc[rc_ptr->num].cost = 10;
1182 rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
1183 rc_ptr->power_desc[rc_ptr->num].fail = 15;
1184 rc_ptr->power_desc[rc_ptr->num++].number = -4;
1186 case ElementRealm::SKY:
1187 strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("魔力充填", "Recharging"));
1188 rc_ptr->power_desc[rc_ptr->num].min_level = 20;
1189 rc_ptr->power_desc[rc_ptr->num].cost = 15;
1190 rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
1191 rc_ptr->power_desc[rc_ptr->num].fail = 25;
1192 rc_ptr->power_desc[rc_ptr->num++].number = -4;
1194 case ElementRealm::SEA:
1195 strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("岩石溶解", "Stone to mud"));
1196 rc_ptr->power_desc[rc_ptr->num].min_level = 5;
1197 rc_ptr->power_desc[rc_ptr->num].cost = 5;
1198 rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
1199 rc_ptr->power_desc[rc_ptr->num].fail = 10;
1200 rc_ptr->power_desc[rc_ptr->num++].number = -4;
1202 case ElementRealm::DARKNESS:
1203 strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("アンデッド従属", "Enslave undead"));
1204 rc_ptr->power_desc[rc_ptr->num].min_level = 10;
1205 rc_ptr->power_desc[rc_ptr->num].cost = 10;
1206 rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
1207 rc_ptr->power_desc[rc_ptr->num].fail = 20;
1208 rc_ptr->power_desc[rc_ptr->num++].number = -4;
1210 case ElementRealm::CHAOS:
1211 strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("現実変容", "Alter reality"));
1212 rc_ptr->power_desc[rc_ptr->num].min_level = 35;
1213 rc_ptr->power_desc[rc_ptr->num].cost = 30;
1214 rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
1215 rc_ptr->power_desc[rc_ptr->num].fail = 40;
1216 rc_ptr->power_desc[rc_ptr->num++].number = -4;
1218 case ElementRealm::EARTH:
1219 strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("地震", "Earthquake"));
1220 rc_ptr->power_desc[rc_ptr->num].min_level = 25;
1221 rc_ptr->power_desc[rc_ptr->num].cost = 15;
1222 rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
1223 rc_ptr->power_desc[rc_ptr->num].fail = 20;
1224 rc_ptr->power_desc[rc_ptr->num++].number = -4;
1226 case ElementRealm::DEATH:
1227 strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("害虫駆除", "Pesticide"));
1228 rc_ptr->power_desc[rc_ptr->num].min_level = 5;
1229 rc_ptr->power_desc[rc_ptr->num].cost = 3;
1230 rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
1231 rc_ptr->power_desc[rc_ptr->num].fail = 20;
1232 rc_ptr->power_desc[rc_ptr->num++].number = -4;
1241 * @param creature_ptr プレイヤー情報への参照ポインタ
1242 * @return 実行したらTRUE、しなかったらFALSE
1244 bool switch_element_execution(player_type *creature_ptr)
1246 auto realm = static_cast<ElementRealm>(creature_ptr->realm1);
1247 PLAYER_LEVEL plev = creature_ptr->lev;
1251 case ElementRealm::FIRE:
1252 (void)lite_area(creature_ptr, damroll(2, plev / 2), plev / 10);
1254 case ElementRealm::ICE:
1255 if (!get_aim_dir(creature_ptr, &dir))
1257 (void)project_hook(creature_ptr, GF_OLD_SLEEP, dir, (plev * 2), PROJECT_STOP | PROJECT_KILL | PROJECT_REFLECTABLE);
1259 case ElementRealm::SKY:
1260 (void)recharge(creature_ptr, 120);
1262 case ElementRealm::SEA:
1263 if (!get_aim_dir(creature_ptr, &dir))
1265 (void)wall_to_mud(creature_ptr, dir, plev * 3 / 2);
1267 case ElementRealm::DARKNESS:
1268 if (!get_aim_dir(creature_ptr, &dir))
1270 (void)control_one_undead(creature_ptr, dir, plev * 3 / 2);
1272 case ElementRealm::CHAOS:
1273 reserve_alter_reality(creature_ptr, randint0(21) + 15);
1275 case ElementRealm::EARTH:
1276 (void)earthquake(creature_ptr, creature_ptr->y, creature_ptr->x, 10, 0);
1278 case ElementRealm::DEATH:
1279 (void)dispel_monsters(creature_ptr, plev / 2);