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"
73 #include <unordered_map>
78 enum class ElementSpells {
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; //!< 追加属性タイプ
112 struct element_power {
113 int elem; //!< 使用属性番号
114 mind_type info; //!< 難易度構造体
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>;
126 static element_type_list element_types = {
128 ElementRealm::FIRE, {
130 { GF_FIRE, GF_HELL_FIRE, GF_PLASMA },
131 { _("火炎", "Fire"), _("業火", "Hell Fire"), _("プラズマ", "Plasma") },
138 { GF_COLD, GF_INERTIAL, GF_TIME },
139 { _("冷気", "Ice"), _("遅鈍", "Inertia"), _("時間逆転", "Time Stream") },
140 { { GF_COLD, GF_ICE} },
146 { GF_ELEC, GF_LITE, GF_MANA },
147 { _("電撃", "Lightning"), _("光", "Light"), _("魔力", "Mana") },
154 { GF_ACID, GF_WATER, GF_DISINTEGRATE },
155 { _("酸", "Acid"), _("水", "Water"), _("分解", "Disintegration") },
160 ElementRealm::DARKNESS, {
162 { GF_DARK, GF_NETHER, GF_VOID },
163 { _("暗黒", "Darkness"), _("地獄", "Nether"), _("虚無", "void") },
164 { { GF_DARK, GF_ABYSS } },
168 ElementRealm::CHAOS, {
170 { GF_CONFUSION, GF_CHAOS, GF_NEXUS },
171 { _("混乱", "Confusion"), _("カオス", "Chaos"), _("因果混乱", "Nexus") },
176 ElementRealm::EARTH, {
178 { GF_SHARDS, GF_FORCE, GF_METEOR },
179 { _("破片", "Shards"), _("フォース", "Force"), _("隕石", "Meteor") },
184 ElementRealm::DEATH, {
186 { GF_POIS, GF_HYPODYNAMIA, GF_DISENCHANT },
187 { _("毒", "Poison"), _("吸血", "Drain Life"), _("劣化", "Disenchantment") },
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") }}},
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.") },
257 * @brief 元素魔法選択時説明文定義
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.")},
264 _("氷系統の魔法はその冷たさで敵の動きを奪い尽くし、魂すらも止めてしまうでしょう。",
265 "Ice system will freeze your enemies, even their souls.")},
267 _("空系統は大いなる天空のエネルギーを駆使して敵の全てを撃滅できます。",
268 "Sky system can terminate all of your enemies powerfully with the energy of the great sky.")},
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.")},
290 * @param realm_idx 領域番号
293 concptr get_element_title(int realm_idx)
295 auto realm = static_cast<ElementRealm>(realm_idx);
296 return element_types.at(realm).title.data();
300 * @brief 元素魔法領域の属性リストを返す
301 * @param realm_idx 領域番号
302 * @return 領域で使用できる属性リスト
304 static std::array<spells_type, 3> get_element_types(int realm_idx)
306 auto realm = static_cast<ElementRealm>(realm_idx);
307 return element_types.at(realm).type;
311 * @brief 元素魔法領域のn番目の属性を返す
312 * @param realm_idx 領域番号
316 spells_type get_element_type(int realm_idx, int n)
318 return get_element_types(realm_idx)[n];
322 * @brief 元素魔法領域のn番目の呪文用の属性を返す
323 * @param realm_idx 領域番号
327 static spells_type get_element_spells_type(player_type *caster_ptr, int n)
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);
339 * @brief 元素魔法領域の属性名リストを返す
340 * @param realm_idx 領域番号
341 * @return 領域で使用できる属性の名称リスト
343 static std::array<std::string_view, 3> get_element_names(int realm_idx)
345 auto realm = static_cast<ElementRealm>(realm_idx);
346 return element_types.at(realm).name;
350 * @brief 元素魔法領域のn番目の属性名を返す
351 * @param realm_idx 領域番号
355 concptr get_element_name(int realm_idx, int n)
357 return get_element_names(realm_idx)[n].data();
362 * @param caster_ptr プレイヤー情報への参照ポインタ
363 * @param spell_idx 呪文番号
366 static concptr get_element_tip(player_type *caster_ptr, int spell_idx)
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());
376 * @param caster_ptr プレイヤー情報への参照ポインタ
377 * @param spell_idx 呪文番号
380 static int get_elemental_elem(player_type *caster_ptr, int spell_idx)
383 auto spell = static_cast<ElementSpells>(spell_idx);
384 return element_powers.at(spell).elem;
388 * @brief 元素魔法呪文の難易度データを取得
389 * @param caster_ptr プレイヤー情報への参照ポインタ
390 * @param spell_idx 呪文番号
393 static mind_type get_elemental_info(player_type *caster_ptr, int spell_idx)
396 auto spell = static_cast<ElementSpells>(spell_idx);
397 return element_powers.at(spell).info;
401 * @brief 元素魔法呪文の効果表示文字列を取得
402 * @param caster_ptr プレイヤー情報への参照ポインタ
403 * @param spell_idx 呪文番号
407 void get_element_effect_info(player_type *caster_ptr, int spell_idx, char *p)
409 PLAYER_LEVEL plev = caster_ptr->lev;
410 auto spell = static_cast<ElementSpells>(spell_idx);
414 case ElementSpells::BOLT_1ST:
415 sprintf(p, " %s%dd%d", KWD_DAM, 3 + ((plev - 1) / 5), 4);
417 case ElementSpells::CURE:
418 sprintf(p, " %s%dd%d", KWD_HEAL, 2, 8);
420 case ElementSpells::BOLT_2ND:
421 sprintf(p, " %s%dd%d", KWD_DAM, 8 + ((plev - 5) / 4), 8);
423 case ElementSpells::BALL_3RD:
424 sprintf(p, " %s%d", KWD_DAM, (50 + plev * 2));
426 case ElementSpells::BALL_1ST:
427 sprintf(p, " %s%d", KWD_DAM, 55 + plev);
429 case ElementSpells::BREATH_2ND:
430 dam = p_ptr->chp / 2;
431 sprintf(p, " %s%d", KWD_DAM, (dam > 150) ? 150 : dam);
433 case ElementSpells::ANNIHILATE:
434 sprintf(p, " %s%d", _("効力:", "pow "), 50 + plev);
436 case ElementSpells::BOLT_3RD:
437 sprintf(p, " %s%dd%d", KWD_DAM, 12 + ((plev - 5) / 4), 8);
439 case ElementSpells::WAVE_1ST:
440 sprintf(p, " %s50+d%d", KWD_DAM, plev * 3);
442 case ElementSpells::BALL_2ND:
443 sprintf(p, " %s%d", KWD_DAM, 75 + plev * 3 / 2);
445 case ElementSpells::BURST_1ST:
446 sprintf(p, " %s%dd%d", KWD_DAM, 6 + plev / 8, 7);
448 case ElementSpells::STORM_2ND:
449 sprintf(p, " %s%d", KWD_DAM, 115 + plev * 5 / 2);
451 case ElementSpells::BREATH_1ST:
452 sprintf(p, " %s%d", KWD_DAM, p_ptr->chp * 2 / 3);
454 case ElementSpells::STORM_3ND:
455 sprintf(p, " %s%d", KWD_DAM, 300 + plev * 5);
465 * @param caster_ptr プレイヤー情報への参照ポインタ
466 * @param spell_idx 呪文番号
467 * @return 実行したらTRUE、キャンセルならFALSE
469 static bool cast_element_spell(player_type *caster_ptr, SPELL_IDX spell_idx)
471 auto spell = static_cast<ElementSpells>(spell_idx);
472 auto power = element_powers.at(spell);
475 PLAYER_LEVEL plev = caster_ptr->lev;
481 case ElementSpells::BOLT_1ST:
482 if (!get_aim_dir(caster_ptr, &dir))
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);
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);
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);
498 case ElementSpells::BOLT_2ND:
499 if (!get_aim_dir(caster_ptr, &dir))
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);
509 case ElementSpells::MAG_DETECT:
510 (void)detect_objects_magic(caster_ptr, DETECT_RAD_DEFAULT);
512 case ElementSpells::BALL_3RD:
514 if (!get_aim_dir(caster_ptr, &dir))
516 typ = get_element_spells_type(caster_ptr, power.elem);
518 (void)fire_ball(caster_ptr, typ, dir, dam, 1);
521 case ElementSpells::BALL_1ST:
522 if (!get_aim_dir(caster_ptr, &dir))
525 typ = get_element_spells_type(caster_ptr, power.elem);
526 (void)fire_ball(caster_ptr, typ, dir, dam, 2);
528 case ElementSpells::BREATH_2ND:
529 if (!get_aim_dir(caster_ptr, &dir))
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);
539 case ElementSpells::ANNIHILATE:
540 if (!get_aim_dir(caster_ptr, &dir))
542 fire_ball_hide(caster_ptr, GF_E_GENOCIDE, dir, plev + 50, 0);
544 case ElementSpells::BOLT_3RD:
545 if (!get_aim_dir(caster_ptr, &dir))
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);
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);
556 case ElementSpells::BALL_2ND:
557 if (!get_aim_dir(caster_ptr, &dir))
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);
567 case ElementSpells::BURST_1ST:
571 typ = get_element_spells_type(caster_ptr, power.elem);
572 for (int k = 0; k < num; k++) {
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))
578 if (!player_bold(caster_ptr, y, x))
581 project(caster_ptr, 0, 0, y, x, damroll(6 + plev / 8, 7), typ, (PROJECT_BEAM | PROJECT_THRU | PROJECT_GRID | PROJECT_KILL));
584 case ElementSpells::STORM_2ND:
585 if (!get_aim_dir(caster_ptr, &dir))
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);
595 case ElementSpells::BREATH_1ST:
596 if (!get_aim_dir(caster_ptr, &dir))
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);
602 case ElementSpells::STORM_3ND:
603 if (!get_aim_dir(caster_ptr, &dir))
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);
617 * @brief 元素魔法呪文の失敗率を計算
618 * @param caster_ptr プレイヤー情報への参照ポインタ
619 * @param spell_idx 呪文番号
622 static PERCENTAGE decide_element_chance(player_type *caster_ptr, mind_type spell)
624 PERCENTAGE chance = spell.fail;
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);
630 PERCENTAGE minfail = adj_mag_fail[caster_ptr->stat_index[A_WIS]];
631 if (chance < minfail)
634 if (caster_ptr->stun > 50)
636 else if (caster_ptr->stun)
639 if (heavy_armor(caster_ptr))
642 if (caster_ptr->icky_wield[0])
645 if (caster_ptr->icky_wield[1])
655 * @brief 元素魔法呪文の消費MPを計算
656 * @param caster_ptr プレイヤー情報への参照ポインタ
657 * @param spell_idx 呪文番号
660 static MANA_POINT decide_element_mana_cost(player_type *caster_ptr, mind_type spell)
663 return spell.mana_cost;
667 * @brief 元素魔法呪文を選択して取得
668 * @param caster_ptr プレイヤー情報への参照ポインタ
670 * @param only_browse 閲覧モードかどうか
671 * @return 選んだらTRUE、選ばなかったらFALSE
673 bool get_element_power(player_type *caster_ptr, SPELL_IDX *sn, bool only_browse)
679 PLAYER_LEVEL plev = caster_ptr->lev;
686 int menu_line = (use_menu ? 1 : 0);
689 if (repeat_pull(&code)) {
690 *sn = (SPELL_IDX)code;
691 if (get_elemental_info(caster_ptr, *sn).min_lev <= plev)
695 concptr p = _("元素魔法", "magic");
699 for (i = 0; i < static_cast<SPELL_IDX>(ElementSpells::MAX); i++) {
700 if (get_elemental_info(caster_ptr, i).min_lev <= plev)
705 (void)strnfmt(out_val, 78, _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "), p, I2A(0),
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);
711 if (use_menu && !only_browse)
716 choice = (always_show_list || use_menu) ? ESCAPE : 1;
718 if (choice == ESCAPE)
720 else if (!get_com(out_val, &choice, TRUE))
723 if (use_menu && choice != ' ') {
732 menu_line += (num - 1);
752 int spell_max = static_cast<int>(ElementSpells::MAX);
753 if ((choice == ' ') || (choice == '*') || (choice == '?') || (use_menu && ask)) {
754 if (!redraw || use_menu) {
758 if (!only_browse && !use_menu)
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);
768 if (spell.min_lev > plev)
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);
776 if (i == (menu_line - 1))
777 strcpy(desc, _(" 》 ", " > "));
781 sprintf(desc, " %c) ", I2A(i));
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);
789 prt("", y + i + 1, x);
790 } else if (!only_browse) {
799 ask = isupper(choice);
801 choice = (char)tolower(choice);
803 i = (islower(choice) ? A2I(choice) : -1);
806 if ((i < 0) || (i >= num)) {
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))
825 if (redraw && !only_browse)
828 set_bits(caster_ptr->window_flags, PW_SPELL);
829 handle_stuff(caster_ptr);
834 repeat_push((COMMAND_CODE)i);
839 * @brief 元素魔法呪文をMPがなくても挑戦するか確認する
840 * @param caster_ptr プレイヤー情報への参照ポインタ
841 * @param mana_cost 消費MP
842 * @return 詠唱するならTRUE、しないならFALSE
844 static bool check_element_mp_sufficiency(player_type *caster_ptr, int mana_cost)
846 if (mana_cost <= caster_ptr->csp)
849 msg_print(_("MPが足りません。", "You do not have enough mana to use this power."));
853 return get_check(_("それでも挑戦しますか? ", "Attempt it anyway? "));
857 * @brief 元素魔法呪文の詠唱を試み、成功なら詠唱し、失敗ならファンブルする
858 * @param caster_ptr プレイヤー情報への参照ポインタ
859 * @param spell_idx 呪文番号
861 * @return 詠唱して実行したらTRUE、されなかったらFALSE
863 static bool try_cast_element_spell(player_type *caster_ptr, SPELL_IDX spell_idx, PERCENTAGE chance)
865 if (randint0(100) >= chance) {
867 return cast_element_spell(caster_ptr, spell_idx);
873 msg_format(_("魔力の集中に失敗した!", "You failed to concentrate hard enough for Mana!"));
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)));
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);
894 * @brief 元素魔法コマンドのメインルーチン
895 * @param caster_ptr プレイヤー情報への参照ポインタ
898 void do_cmd_element(player_type *caster_ptr)
901 if (cmd_limit_confused(caster_ptr) || !get_element_power(caster_ptr, &i, FALSE))
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);
908 if (!check_element_mp_sufficiency(caster_ptr, mana_cost))
911 if (!try_cast_element_spell(caster_ptr, i, chance))
914 if (mana_cost <= caster_ptr->csp) {
915 caster_ptr->csp -= mana_cost;
917 int oops = mana_cost;
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);
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);
936 * @brief 現在プレイヤーが使用可能な元素魔法の一覧表示
937 * @param caster_ptr プレイヤー情報への参照ポインタ
940 void do_cmd_element_browse(player_type *caster_ptr)
947 if (!get_element_power(caster_ptr, &n, TRUE)) {
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);
964 prt(_("何かキーを押して下さい。", "Hit any key."), 0, 0);
970 * @brief 元素魔法の単体抹殺が有効か確認する
971 * @param r_ptr モンスター種族への参照ポインタ
973 * @return 効果があるならTRUE、なければFALSE
975 bool is_elemental_genocide_effective(monster_race *r_ptr, spells_type type)
979 if (any_bits(r_ptr->flagsr, RFR_IM_FIRE))
983 if (any_bits(r_ptr->flagsr, RFR_IM_COLD))
987 if (any_bits(r_ptr->flagsr, RFR_IM_ELEC))
991 if (any_bits(r_ptr->flagsr, RFR_IM_ACID))
995 if (any_bits(r_ptr->flagsr, RFR_RES_DARK) || any_bits(r_ptr->r_flags3, RF3_HURT_LITE))
999 if (any_bits(r_ptr->flags3, RF3_NO_CONF))
1003 if (any_bits(r_ptr->flagsr, RFR_RES_SHAR))
1007 if (any_bits(r_ptr->flagsr, RFR_IM_POIS))
1018 * @brief 元素魔法の単体抹殺の効果を発動する
1019 * @param caster_ptr プレイヤー情報への参照ポインタ
1020 * @param em_ptr 魔法効果情報への参照ポインタ
1021 * @return 効果処理を続けるかどうか
1023 process_result effect_monster_elemental_genocide(player_type *caster_ptr, effect_monster_type *em_ptr)
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);
1029 if (em_ptr->seen_msg)
1030 msg_format(_("%sが%sを包み込んだ。", "The %s surrounds %s."), name, em_ptr->m_name);
1033 em_ptr->obvious = TRUE;
1036 if (em_ptr->seen_msg)
1037 msg_format(_("%sには効果がなかった。", "%^s is unaffected."), em_ptr->m_name);
1039 return PROCESS_TRUE;
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);
1046 chg_virtue(caster_ptr, V_VITALITY, -1);
1047 return PROCESS_TRUE;
1050 em_ptr->skipped = TRUE;
1051 return PROCESS_CONTINUE;
1055 * @brief 元素領域とレベルの条件に見合うかチェックする
1056 * @param caster_ptr プレイヤー情報への参照ポインタ
1058 * @param lev プレイヤーレベル
1059 * @return 見合うならTRUE、そうでなければFALSE
1061 * レベルに応じて取得する耐性などの判定に使用する
1063 bool has_element_resist(player_type *creature_ptr, ElementRealm realm, PLAYER_LEVEL lev)
1065 if (creature_ptr->pclass != CLASS_ELEMENTALIST)
1068 auto prealm = static_cast<ElementRealm>(creature_ptr->element);
1069 return (prealm == realm && creature_ptr->lev >= lev);
1073 * @brief 領域選択時のカーソル表示(シンボル+領域名)
1079 static void display_realm_cursor(int i, int n, term_color_type color)
1086 name = _("ランダム", "Random");
1089 name = element_types.at(static_cast<ElementRealm>(i + 1)).title.data();
1091 sprintf(cur, "%c) %s", sym, name);
1093 c_put_str(color, cur, 12 + (i / 5), 2 + 15 * (i % 5));
1097 * @brief 領域選択時の移動キー処理
1103 static int interpret_realm_select_key(int cs, int n, char c)
1129 * @param creature_ptr プレイヤー情報への参照ポインタ
1133 static int get_element_realm(player_type *creature_ptr, int is, int n)
1135 int cs = MAX(0, is);
1140 sprintf(buf, _("領域を選んで下さい(%c-%c) ('='初期オプション設定): ", "Choose a realm (%c-%c) ('=' for options): "), I2A(0), I2A(n - 1));
1143 display_realm_cursor(os, n, TERM_WHITE);
1144 display_realm_cursor(cs, n, TERM_YELLOW);
1145 put_str(buf, 10, 10);
1149 cs = interpret_realm_select_key(cs, n, c);
1154 if (c == ' ' || c == '\r' || c == '\n') {
1156 display_realm_cursor(cs, n, TERM_WHITE);
1157 cs = randint0(n - 1);
1163 display_realm_cursor(cs, n, TERM_WHITE);
1164 cs = randint0(n - 1);
1168 k = islower(c) ? A2I(c) : -1;
1169 if (k >= 0 && k < n) {
1170 display_realm_cursor(cs, n, TERM_WHITE);
1175 k = isupper(c) ? (26 + c - 'A') : -1;
1176 if (k >= 26 && k < n) {
1177 display_realm_cursor(cs, n, TERM_WHITE);
1184 do_cmd_options_aux(creature_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
1186 } else if (c != '2' && c != '4' && c != '6' && c != '8')
1190 display_realm_cursor(cs, n, TERM_YELLOW);
1196 * @param creature_ptr プレイヤー情報への参照ポインタ
1199 byte select_element_realm(player_type *creature_ptr)
1203 int realm_max = static_cast<int>(ElementRealm::MAX);
1208 _("注意:元素系統の選択によりあなたが習得する呪文のタイプが決まります。", "Note: The system of element will determine which spells you can learn."),
1211 for (int i = 0; i < realm_max; i++) {
1212 display_realm_cursor(i, realm_max - 1, TERM_WHITE);
1215 realm_idx = get_element_realm(creature_ptr, realm_idx - 1, realm_max - 1);
1216 if (realm_idx == 255)
1219 auto realm = static_cast<ElementRealm>(realm_idx);
1221 shape_buffer(element_texts.at(realm).data(), 74, temp, sizeof(temp));
1223 for (int i = 0; i < 5; i++) {
1230 if (get_check_strict(creature_ptr, _("よろしいですか?", "Are you sure? "), CHECK_DEFAULT_Y))
1237 return (byte)realm_idx;
1241 * @brief クラスパワー情報を追加
1242 * @param creature_ptr プレイヤー情報への参照ポインタ
1243 * @param rc_ptr レイシャルパワー情報への参照ポインタ
1246 void switch_element_racial(player_type *creature_ptr, rc_type *rc_ptr)
1248 auto plev = creature_ptr->lev;
1249 auto realm = static_cast<ElementRealm>(creature_ptr->element);
1252 case ElementRealm::FIRE:
1253 rpi = rpi_type(_("ライト・エリア", "Light area"));
1254 rpi.text = _("光源が照らしている範囲か部屋全体を永久に明るくする。", "Lights up nearby area and the inside of a room permanently.");
1259 rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
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.");
1269 rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
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.");
1279 rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1281 case ElementRealm::SEA:
1282 rpi = rpi_type(_("岩石溶解", "Stone to mud"));
1283 rpi.text = _("壁を溶かして床にする。", "Turns one rock square to mud.");
1288 rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
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);
1294 rpi.cost = 5 + plev / 7;
1297 rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1299 case ElementRealm::CHAOS:
1300 rpi = rpi_type(_("現実変容", "Alter reality"));
1301 rpi.text = _("現在の階を再構成する。", "Recreates current dungeon level.");
1306 rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1308 case ElementRealm::EARTH:
1309 rpi = rpi_type(_("地震", "Earthquake"));
1310 rpi.info = format("%s%d", KWD_SPHERE, 10);
1312 = _("周囲のダンジョンを揺らし、壁と床をランダムに入れ変える。", "Shakes dungeon structure, and results in random swapping of floors and walls.");
1317 rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1319 case ElementRealm::DEATH:
1320 rpi = rpi_type(_("増殖阻止", "Sterilization"));
1321 rpi.text = _("この階の増殖するモンスターが増殖できなくなる。", "Prevents any breeders on current level from breeding.");
1326 rc_ptr->add_power(rpi, RC_IDX_CLASS_1);
1334 * @todo 宣言だけ。後日適切な場所に移動
1336 static bool door_to_darkness(player_type *caster_ptr, POSITION dist);
1340 * @param creature_ptr プレイヤー情報への参照ポインタ
1341 * @return 実行したらTRUE、しなかったらFALSE
1343 bool switch_element_execution(player_type *creature_ptr)
1345 auto realm = static_cast<ElementRealm>(creature_ptr->element);
1346 PLAYER_LEVEL plev = creature_ptr->lev;
1350 case ElementRealm::FIRE:
1351 (void)lite_area(creature_ptr, damroll(2, plev / 2), plev / 10);
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);
1357 case ElementRealm::SKY:
1358 (void)recharge(creature_ptr, 120);
1360 case ElementRealm::SEA:
1361 if (!get_aim_dir(creature_ptr, &dir))
1363 (void)wall_to_mud(creature_ptr, dir, plev * 3 / 2);
1365 case ElementRealm::DARKNESS:
1366 return door_to_darkness(creature_ptr, 15 + plev / 2);
1368 case ElementRealm::CHAOS:
1369 reserve_alter_reality(creature_ptr, randint0(21) + 15);
1371 case ElementRealm::EARTH:
1372 (void)earthquake(creature_ptr, creature_ptr->y, creature_ptr->x, 10, 0);
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;
1386 * @brief 指定したマスが暗いかどうか
1387 * @param f_ptr 階の情報への参照ポインタ
1390 * @return 暗いならTRUE、そうでないならFALSE
1392 static bool is_target_grid_dark(floor_type *f_ptr, POSITION y, POSITION x)
1394 if (any_bits(f_ptr->grid_array[y][x].info, CAVE_MNLT))
1397 bool is_dark = FALSE;
1398 bool is_lite = any_bits(f_ptr->grid_array[y][x].info, CAVE_GLOW | CAVE_LITE);
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)
1404 if (!in_bounds(f_ptr, dy, dx))
1407 MONSTER_IDX m_idx = f_ptr->grid_array[dy][dx].m_idx;
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))
1415 if (d <= 2 && any_bits(r_ptr->flags7, RF7_HAS_LITE_2 | RF7_SELF_LITE_2))
1417 if (d <= 1 && any_bits(r_ptr->flags7, RF7_HAS_DARK_1 | RF7_SELF_DARK_1))
1419 if (d <= 2 && any_bits(r_ptr->flags7, RF7_HAS_DARK_2 | RF7_SELF_DARK_2))
1423 return !is_lite || is_dark;
1427 * @breif 暗いところ限定での次元の扉
1428 * @param caster_ptr プレイヤー情報への参照ポインタ
1430 static bool door_to_darkness(player_type *caster_ptr, POSITION dist)
1432 POSITION y = caster_ptr->y;
1433 POSITION x = caster_ptr->x;
1436 for (int i = 0; i < 3; i++) {
1437 if (!tgt_pt(caster_ptr, &x, &y))
1440 f_ptr = caster_ptr->current_floor_ptr;
1442 if (distance(y, x, caster_ptr->y, caster_ptr->x) > dist) {
1443 msg_print(_("遠すぎる!", "There is too far!"));
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."));
1455 bool flag = cave_player_teleportable_bold(caster_ptr, y, x, TELEPORT_SPONTANEOUS) && is_target_grid_dark(f_ptr, y, x);
1457 teleport_player_to(caster_ptr, y, x, TELEPORT_SPONTANEOUS);
1459 msg_print(_("闇の扉は開かなかった!", "Door to darkness does not open!"));