OSDN Git Service

[Refactor] #2713 PlayerType::dungeon_idx をFloorType::dungeon_idx へ吸収合併した
[hengbandforosx/hengbandosx.git] / src / realm / realm-hissatsu.cpp
1 #include "realm/realm-hissatsu.h"
2 #include "artifact/fixed-art-types.h"
3 #include "cmd-action/cmd-attack.h"
4 #include "cmd-action/cmd-spell.h"
5 #include "cmd-item/cmd-throw.h"
6 #include "combat/combat-options-type.h"
7 #include "core/asking-player.h"
8 #include "core/player-redraw-types.h"
9 #include "core/stuff-handler.h"
10 #include "dungeon/dungeon-flag-types.h"
11 #include "effect/attribute-types.h"
12 #include "effect/effect-characteristics.h"
13 #include "effect/effect-processor.h"
14 #include "effect/spells-effect-util.h"
15 #include "floor/cave.h"
16 #include "floor/geometry.h"
17 #include "grid/feature-flag-types.h"
18 #include "grid/feature.h"
19 #include "grid/grid.h"
20 #include "inventory/inventory-slot-types.h"
21 #include "io/input-key-acceptor.h"
22 #include "io/input-key-requester.h"
23 #include "mind/mind-ninja.h"
24 #include "monster-race/monster-race-hook.h"
25 #include "monster-race/monster-race.h"
26 #include "monster-race/race-brightness-mask.h"
27 #include "monster-race/race-flags7.h"
28 #include "monster/monster-describer.h"
29 #include "monster/monster-info.h"
30 #include "monster/monster-update.h"
31 #include "object-enchant/tr-types.h"
32 #include "object/object-flags.h"
33 #include "player-info/equipment-info.h"
34 #include "player/player-damage.h"
35 #include "player/player-move.h"
36 #include "spell-kind/earthquake.h"
37 #include "spell-kind/spells-detection.h"
38 #include "spell-kind/spells-launcher.h"
39 #include "spell-kind/spells-perception.h"
40 #include "spell-kind/spells-sight.h"
41 #include "spell-kind/spells-teleport.h"
42 #include "spell/technic-info-table.h"
43 #include "status/bad-status-setter.h"
44 #include "system/dungeon-info.h"
45 #include "system/floor-type-definition.h"
46 #include "system/grid-type-definition.h"
47 #include "system/item-entity.h"
48 #include "system/monster-entity.h"
49 #include "system/monster-race-info.h"
50 #include "system/player-type-definition.h"
51 #include "system/redrawing-flags-updater.h"
52 #include "target/grid-selector.h"
53 #include "target/projection-path-calculator.h"
54 #include "target/target-getter.h"
55 #include "term/screen-processor.h"
56 #include "timed-effect/player-cut.h"
57 #include "timed-effect/timed-effects.h"
58 #include "util/bit-flags-calculator.h"
59 #include "view/display-messages.h"
60 #include "world/world.h"
61
62 /*!
63  * @brief 剣術の各処理を行う
64  * @param player_ptr プレイヤーへの参照ポインタ
65  * @param spell 剣術ID
66  * @param mode 処理内容 (SpellProcessType::NAME / SPELL_DESC / SpellProcessType::CAST)
67  * @return SpellProcessType::NAME / SPELL_DESC 時には文字列を返す。SpellProcessType::CAST時は std::nullopt を返す。
68  */
69 std::optional<std::string> do_hissatsu_spell(PlayerType *player_ptr, SPELL_IDX spell, SpellProcessType mode)
70 {
71     bool name = mode == SpellProcessType::NAME;
72     bool desc = mode == SpellProcessType::DESCRIPTION;
73     bool cast = mode == SpellProcessType::CAST;
74
75     DIRECTION dir;
76     PLAYER_LEVEL plev = player_ptr->lev;
77
78     switch (spell) {
79     case 0:
80         if (name) {
81             return _("飛飯綱", "Tobi-Izuna");
82         }
83         if (desc) {
84             return _("2マス離れたところにいるモンスターを攻撃する。", "Attacks a monster two squares away.");
85         }
86
87         if (cast) {
88             project_length = 2;
89             if (!get_aim_dir(player_ptr, &dir)) {
90                 return std::nullopt;
91             }
92
93             project_hook(player_ptr, AttributeType::ATTACK, dir, HISSATSU_2, PROJECT_STOP | PROJECT_KILL);
94         }
95         break;
96
97     case 1:
98         if (name) {
99             return _("五月雨斬り", "3-Way Attack");
100         }
101         if (desc) {
102             return _("3方向に対して攻撃する。", "Attacks in 3 directions at one time.");
103         }
104
105         if (cast) {
106             DIRECTION cdir;
107             POSITION y, x;
108
109             if (!get_direction(player_ptr, &dir, false, false)) {
110                 return std::nullopt;
111             }
112             if (dir == 5) {
113                 return std::nullopt;
114             }
115
116             for (cdir = 0; cdir < 8; cdir++) {
117                 if (cdd[cdir] == dir) {
118                     break;
119                 }
120             }
121
122             if (cdir == 8) {
123                 return std::nullopt;
124             }
125
126             y = player_ptr->y + ddy_cdd[cdir];
127             x = player_ptr->x + ddx_cdd[cdir];
128             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
129                 do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
130             } else {
131                 msg_print(_("攻撃は空を切った。", "You attack the empty air."));
132             }
133
134             y = player_ptr->y + ddy_cdd[(cdir + 7) % 8];
135             x = player_ptr->x + ddx_cdd[(cdir + 7) % 8];
136             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
137                 do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
138             } else {
139                 msg_print(_("攻撃は空を切った。", "You attack the empty air."));
140             }
141
142             y = player_ptr->y + ddy_cdd[(cdir + 1) % 8];
143             x = player_ptr->x + ddx_cdd[(cdir + 1) % 8];
144             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
145                 do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
146             } else {
147                 msg_print(_("攻撃は空を切った。", "You attack the empty air."));
148             }
149         }
150         break;
151
152     case 2:
153         if (name) {
154             return _("ブーメラン", "Boomerang");
155         }
156         if (desc) {
157             return _(
158                 "武器を手元に戻ってくるように投げる。戻ってこないこともある。", "Throws current weapon. It'll return to your hand unless the action failed.");
159         }
160
161         if (cast) {
162             if (!ThrowCommand(player_ptr).do_cmd_throw(1, true, -1)) {
163                 return std::nullopt;
164             }
165         }
166         break;
167
168     case 3:
169         if (name) {
170             return _("焔霊", "Burning Strike");
171         }
172         if (desc) {
173             return _("火炎耐性のないモンスターに大ダメージを与える。", "Attacks a monster with more damage unless it has resistance to fire.");
174         }
175
176         if (cast) {
177             POSITION y, x;
178
179             if (!get_direction(player_ptr, &dir, false, false)) {
180                 return std::nullopt;
181             }
182             if (dir == 5) {
183                 return std::nullopt;
184             }
185
186             y = player_ptr->y + ddy[dir];
187             x = player_ptr->x + ddx[dir];
188
189             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
190                 do_cmd_attack(player_ptr, y, x, HISSATSU_FIRE);
191             } else {
192                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
193                 return std::nullopt;
194             }
195         }
196         break;
197
198     case 4:
199         if (name) {
200             return _("殺気感知", "Detect Ferocity");
201         }
202         if (desc) {
203             return _("近くの思考することができるモンスターを感知する。", "Detects all monsters except the mindless in your vicinity.");
204         }
205
206         if (cast) {
207             detect_monsters_mind(player_ptr, DETECT_RAD_DEFAULT);
208         }
209         break;
210
211     case 5:
212         if (name) {
213             return _("みね打ち", "Strike to Stun");
214         }
215         if (desc) {
216             return _("相手にダメージを与えないが、朦朧とさせる。", "Attempts to stun a monster next to you.");
217         }
218
219         if (cast) {
220             POSITION y, x;
221
222             if (!get_direction(player_ptr, &dir, false, false)) {
223                 return std::nullopt;
224             }
225             if (dir == 5) {
226                 return std::nullopt;
227             }
228
229             y = player_ptr->y + ddy[dir];
230             x = player_ptr->x + ddx[dir];
231
232             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
233                 do_cmd_attack(player_ptr, y, x, HISSATSU_MINEUCHI);
234             } else {
235                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
236                 return std::nullopt;
237             }
238         }
239         break;
240
241     case 6:
242         if (name) {
243             return _("カウンター", "Counter");
244         }
245         if (desc) {
246             return _("相手に攻撃されたときに反撃する。反撃するたびにMPを消費。",
247                 "Prepares to counterattack. When attacked by a monster, strikes back using SP each time.");
248         }
249
250         if (cast) {
251             if (player_ptr->riding) {
252                 msg_print(_("乗馬中には無理だ。", "You cannot do it when riding."));
253                 return std::nullopt;
254             }
255             msg_print(_("相手の攻撃に対して身構えた。", "You prepare to counterattack."));
256             player_ptr->counter = true;
257         }
258         break;
259
260     case 7:
261         if (name) {
262             return _("払い抜け", "Harainuke");
263         }
264         if (desc) {
265             return _("攻撃した後、反対側に抜ける。",
266                 "In one action, attacks a monster with your weapons normally and then moves to the space beyond the monster if that space is not blocked.");
267         }
268
269         if (cast) {
270             POSITION y, x;
271
272             if (player_ptr->riding) {
273                 msg_print(_("乗馬中には無理だ。", "You cannot do it when riding."));
274                 return std::nullopt;
275             }
276
277             if (!get_direction(player_ptr, &dir, false, false)) {
278                 return std::nullopt;
279             }
280
281             if (dir == 5) {
282                 return std::nullopt;
283             }
284             y = player_ptr->y + ddy[dir];
285             x = player_ptr->x + ddx[dir];
286
287             const auto *floor_ptr = player_ptr->current_floor_ptr;
288             const auto &grid = floor_ptr->grid_array[y][x];
289             if (!grid.m_idx) {
290                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
291                 return std::nullopt;
292             }
293
294             do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
295             if (!player_can_enter(player_ptr, grid.feat, 0) || is_trap(player_ptr, grid.feat)) {
296                 break;
297             }
298
299             y += ddy[dir];
300             x += ddx[dir];
301
302             if (player_can_enter(player_ptr, grid.feat, 0) && !is_trap(player_ptr, grid.feat) && !grid.m_idx) {
303                 msg_print(nullptr);
304                 (void)move_player_effect(player_ptr, y, x, MPE_FORGET_FLOW | MPE_HANDLE_STUFF | MPE_DONT_PICKUP);
305             }
306         }
307         break;
308
309     case 8:
310         if (name) {
311             return _("サーペンツタン", "Serpent's Tongue");
312         }
313         if (desc) {
314             return _("毒耐性のないモンスターに大ダメージを与える。", "Attacks a monster with more damage unless it has resistance to poison.");
315         }
316
317         if (cast) {
318             POSITION y, x;
319
320             if (!get_direction(player_ptr, &dir, false, false)) {
321                 return std::nullopt;
322             }
323             if (dir == 5) {
324                 return std::nullopt;
325             }
326
327             y = player_ptr->y + ddy[dir];
328             x = player_ptr->x + ddx[dir];
329
330             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
331                 do_cmd_attack(player_ptr, y, x, HISSATSU_POISON);
332             } else {
333                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
334                 return std::nullopt;
335             }
336         }
337         break;
338
339     case 9:
340         if (name) {
341             return _("斬魔剣弐の太刀", "Zammaken");
342         }
343         if (desc) {
344             return _("生命のない邪悪なモンスターに大ダメージを与えるが、他のモンスターには全く効果がない。",
345                 "Attacks an evil unliving monster with great damage. Has no effect on other monsters.");
346         }
347
348         if (cast) {
349             POSITION y, x;
350
351             if (!get_direction(player_ptr, &dir, false, false)) {
352                 return std::nullopt;
353             }
354             if (dir == 5) {
355                 return std::nullopt;
356             }
357
358             y = player_ptr->y + ddy[dir];
359             x = player_ptr->x + ddx[dir];
360
361             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
362                 do_cmd_attack(player_ptr, y, x, HISSATSU_ZANMA);
363             } else {
364                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
365                 return std::nullopt;
366             }
367         }
368         break;
369
370     case 10:
371         if (name) {
372             return _("裂風剣", "Wind Blast");
373         }
374         if (desc) {
375             return _("攻撃した相手を後方へ吹き飛ばす。", "Attacks an adjacent monster and blows it away.");
376         }
377
378         if (cast) {
379             POSITION y, x;
380
381             if (!get_direction(player_ptr, &dir, false, false)) {
382                 return std::nullopt;
383             }
384             if (dir == 5) {
385                 return std::nullopt;
386             }
387
388             y = player_ptr->y + ddy[dir];
389             x = player_ptr->x + ddx[dir];
390
391             const auto &floor = *player_ptr->current_floor_ptr;
392             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
393                 do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
394             } else {
395                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
396                 return std::nullopt;
397             }
398             if (dungeons_info[floor.dungeon_idx].flags.has(DungeonFeatureType::NO_MELEE)) {
399                 return "";
400             }
401             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
402                 int i;
403                 POSITION ty = y, tx = x;
404                 POSITION oy = y, ox = x;
405                 MONSTER_IDX m_idx = player_ptr->current_floor_ptr->grid_array[y][x].m_idx;
406                 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
407                 const auto m_name = monster_desc(player_ptr, m_ptr, 0);
408
409                 for (i = 0; i < 5; i++) {
410                     y += ddy[dir];
411                     x += ddx[dir];
412                     if (is_cave_empty_bold(player_ptr, y, x)) {
413                         ty = y;
414                         tx = x;
415                     } else {
416                         break;
417                     }
418                 }
419                 if ((ty != oy) || (tx != ox)) {
420                     msg_format(_("%sを吹き飛ばした!", "You blow %s away!"), m_name.data());
421                     player_ptr->current_floor_ptr->grid_array[oy][ox].m_idx = 0;
422                     player_ptr->current_floor_ptr->grid_array[ty][tx].m_idx = m_idx;
423                     m_ptr->fy = ty;
424                     m_ptr->fx = tx;
425
426                     update_monster(player_ptr, m_idx, true);
427                     lite_spot(player_ptr, oy, ox);
428                     lite_spot(player_ptr, ty, tx);
429
430                     if (monraces_info[m_ptr->r_idx].brightness_flags.has_any_of(ld_mask)) {
431                         RedrawingFlagsUpdater::get_instance().set_flag(StatusRedrawingFlag::MONSTER_LITE);
432                     }
433                 }
434             }
435         }
436         break;
437
438     case 11:
439         if (name) {
440             return _("刀匠の目利き", "Judge");
441         }
442         if (desc) {
443             return _("武器・防具を1つ識別する。レベル45以上で武器・防具の能力を完全に知ることができる。",
444                 "Identifies a weapon or armor. *Identifies* the item at level 45.");
445         }
446
447         if (cast) {
448             if (plev > 44) {
449                 if (!identify_fully(player_ptr, true)) {
450                     return std::nullopt;
451                 }
452             } else {
453                 if (!ident_spell(player_ptr, true)) {
454                     return std::nullopt;
455                 }
456             }
457         }
458         break;
459
460     case 12:
461         if (name) {
462             return _("破岩斬", "Rock Smash");
463         }
464         if (desc) {
465             return _("岩を壊し、岩石系のモンスターに大ダメージを与える。", "Breaks rock or greatly damages a monster made of rocks.");
466         }
467
468         if (cast) {
469             POSITION y, x;
470
471             if (!get_direction(player_ptr, &dir, false, false)) {
472                 return std::nullopt;
473             }
474             if (dir == 5) {
475                 return std::nullopt;
476             }
477
478             y = player_ptr->y + ddy[dir];
479             x = player_ptr->x + ddx[dir];
480
481             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
482                 do_cmd_attack(player_ptr, y, x, HISSATSU_HAGAN);
483             }
484
485             if (!cave_has_flag_bold(player_ptr->current_floor_ptr, y, x, TerrainCharacteristics::HURT_ROCK)) {
486                 break;
487             }
488
489             /* Destroy the feature */
490             cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::HURT_ROCK);
491             RedrawingFlagsUpdater::get_instance().set_flag(StatusRedrawingFlag::FLOW);
492         }
493         break;
494
495     case 13:
496         if (name) {
497             return _("乱れ雪月花", "Midare-Setsugekka");
498         }
499         if (desc) {
500             return _("攻撃回数が増え、冷気耐性のないモンスターに大ダメージを与える。",
501                 "Attacks a monster with an increased number of attacks and more damage unless it has resistance to cold.");
502         }
503
504         if (cast) {
505             POSITION y, x;
506
507             if (!get_direction(player_ptr, &dir, false, false)) {
508                 return std::nullopt;
509             }
510             if (dir == 5) {
511                 return std::nullopt;
512             }
513
514             y = player_ptr->y + ddy[dir];
515             x = player_ptr->x + ddx[dir];
516
517             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
518                 do_cmd_attack(player_ptr, y, x, HISSATSU_COLD);
519             } else {
520                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
521                 return std::nullopt;
522             }
523         }
524         break;
525
526     case 14:
527         if (name) {
528             return _("急所突き", "Spot Aiming");
529         }
530         if (desc) {
531             return _("モンスターを一撃で倒す攻撃を繰り出す。失敗すると1点しかダメージを与えられない。",
532                 "Attempts to kill a monster instantly. If that fails, causes only 1HP of damage.");
533         }
534
535         if (cast) {
536             POSITION y, x;
537
538             if (!get_direction(player_ptr, &dir, false, false)) {
539                 return std::nullopt;
540             }
541             if (dir == 5) {
542                 return std::nullopt;
543             }
544
545             y = player_ptr->y + ddy[dir];
546             x = player_ptr->x + ddx[dir];
547
548             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
549                 do_cmd_attack(player_ptr, y, x, HISSATSU_KYUSHO);
550             } else {
551                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
552                 return std::nullopt;
553             }
554         }
555         break;
556
557     case 15:
558         if (name) {
559             return _("魔神斬り", "Majingiri");
560         }
561         if (desc) {
562             return _("会心の一撃で攻撃する。攻撃がかわされやすい。", "Attempts to attack with a critical hit, but this attack is easy to evade for a monster.");
563         }
564
565         if (cast) {
566             POSITION y, x;
567
568             if (!get_direction(player_ptr, &dir, false, false)) {
569                 return std::nullopt;
570             }
571             if (dir == 5) {
572                 return std::nullopt;
573             }
574
575             y = player_ptr->y + ddy[dir];
576             x = player_ptr->x + ddx[dir];
577
578             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
579                 do_cmd_attack(player_ptr, y, x, HISSATSU_MAJIN);
580             } else {
581                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
582                 return std::nullopt;
583             }
584         }
585         break;
586
587     case 16:
588         if (name) {
589             return _("捨て身", "Desperate Attack");
590         }
591         if (desc) {
592             return _("強力な攻撃を繰り出す。次のターンまでの間、食らうダメージが増える。",
593                 "Attacks with all of your power, but all damage you take will be doubled for one turn.");
594         }
595
596         if (cast) {
597             POSITION y, x;
598
599             if (!get_direction(player_ptr, &dir, false, false)) {
600                 return std::nullopt;
601             }
602             if (dir == 5) {
603                 return std::nullopt;
604             }
605
606             y = player_ptr->y + ddy[dir];
607             x = player_ptr->x + ddx[dir];
608
609             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
610                 do_cmd_attack(player_ptr, y, x, HISSATSU_SUTEMI);
611             } else {
612                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
613                 return std::nullopt;
614             }
615             player_ptr->sutemi = true;
616         }
617         break;
618
619     case 17:
620         if (name) {
621             return _("雷撃鷲爪斬", "Lightning Eagle");
622         }
623         if (desc) {
624             return _("電撃耐性のないモンスターに非常に大きいダメージを与える。", "Attacks a monster with more damage unless it has resistance to electricity.");
625         }
626
627         if (cast) {
628             POSITION y, x;
629
630             if (!get_direction(player_ptr, &dir, false, false)) {
631                 return std::nullopt;
632             }
633             if (dir == 5) {
634                 return std::nullopt;
635             }
636
637             y = player_ptr->y + ddy[dir];
638             x = player_ptr->x + ddx[dir];
639
640             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
641                 do_cmd_attack(player_ptr, y, x, HISSATSU_ELEC);
642             } else {
643                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
644                 return std::nullopt;
645             }
646         }
647         break;
648
649     case 18:
650         if (name) {
651             return _("入身", "Rush Attack");
652         }
653         if (desc) {
654             return _("素早く相手に近寄り攻撃する。", "Steps close to a monster and attacks at the same time.");
655         }
656
657         if (cast) {
658             if (!rush_attack(player_ptr, nullptr)) {
659                 return std::nullopt;
660             }
661         }
662         break;
663
664     case 19:
665         if (name) {
666             return _("赤流渦", "Bloody Maelstrom");
667         }
668
669         if (desc) {
670             return _("自分自身も傷を作りつつ、その傷が深いほど大きい威力で全方向の敵を攻撃できる。生きていないモンスターには効果がない。",
671                 "Attacks all adjacent monsters with power corresponding to your cuts. Then increases your cuts. Has no effect on unliving monsters.");
672         }
673
674         if (cast) {
675             POSITION y = 0, x = 0;
676             auto current_cut = player_ptr->effects()->cut()->current();
677             short new_cut = current_cut < 300 ? current_cut + 300 : current_cut * 2;
678             (void)BadStatusSetter(player_ptr).set_cut(new_cut);
679             for (dir = 0; dir < 8; dir++) {
680                 y = player_ptr->y + ddy_ddd[dir];
681                 x = player_ptr->x + ddx_ddd[dir];
682                 auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
683                 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[g_ptr->m_idx];
684                 if ((g_ptr->m_idx == 0) || (!m_ptr->ml && !cave_has_flag_bold(player_ptr->current_floor_ptr, y, x, TerrainCharacteristics::PROJECT))) {
685                     continue;
686                 }
687
688                 if (monster_living(m_ptr->r_idx)) {
689                     do_cmd_attack(player_ptr, y, x, HISSATSU_SEKIRYUKA);
690                     continue;
691                 }
692
693                 const auto m_name = monster_desc(player_ptr, m_ptr, 0);
694                 msg_format(_("%sには効果がない!", "%s is unharmed!"), m_name.data());
695             }
696         }
697
698         break;
699     case 20:
700         if (name) {
701             return _("激震撃", "Earthquake Blow");
702         }
703         if (desc) {
704             return _("地震を起こす。", "Shakes dungeon structure, and results in random swapping of floors and walls.");
705         }
706
707         if (cast) {
708             POSITION y, x;
709
710             if (!get_direction(player_ptr, &dir, false, false)) {
711                 return std::nullopt;
712             }
713             if (dir == 5) {
714                 return std::nullopt;
715             }
716
717             y = player_ptr->y + ddy[dir];
718             x = player_ptr->x + ddx[dir];
719
720             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
721                 do_cmd_attack(player_ptr, y, x, HISSATSU_QUAKE);
722             } else {
723                 earthquake(player_ptr, player_ptr->y, player_ptr->x, 10, 0);
724             }
725         }
726         break;
727
728     case 21:
729         if (name) {
730             return _("地走り", "Crack");
731         }
732         if (desc) {
733             return _("衝撃波のビームを放つ。", "Fires a shock wave as a beam.");
734         }
735
736         if (cast) {
737             int total_damage = 0, basedam, i;
738             ItemEntity *o_ptr;
739             if (!get_aim_dir(player_ptr, &dir)) {
740                 return std::nullopt;
741             }
742             msg_print(_("武器を大きく振り下ろした。", "You swing your weapon downward."));
743             for (i = 0; i < 2; i++) {
744                 int damage;
745
746                 if (!has_melee_weapon(player_ptr, INVEN_MAIN_HAND + i)) {
747                     break;
748                 }
749                 o_ptr = &player_ptr->inventory_list[INVEN_MAIN_HAND + i];
750                 basedam = (o_ptr->dd * (o_ptr->ds + 1)) * 50;
751                 damage = o_ptr->to_d * 100;
752                 auto flags = object_flags(o_ptr);
753
754                 // @todo ヴォーパルの多重定義.
755                 if (o_ptr->is_specific_artifact(FixedArtifactId::VORPAL_BLADE) || o_ptr->is_specific_artifact(FixedArtifactId::CHAINSWORD)) {
756                     /* vorpal blade */
757                     basedam *= 5;
758                     basedam /= 3;
759                 } else if (flags.has(TR_VORPAL)) {
760                     /* vorpal flag only */
761                     basedam *= 11;
762                     basedam /= 9;
763                 }
764                 damage += basedam;
765                 damage *= player_ptr->num_blow[i];
766                 total_damage += damage / 200;
767                 if (i) {
768                     total_damage = total_damage * 7 / 10;
769                 }
770             }
771             fire_beam(player_ptr, AttributeType::FORCE, dir, total_damage);
772         }
773         break;
774
775     case 22:
776         if (name) {
777             return _("気迫の雄叫び", "War Cry");
778         }
779         if (desc) {
780             return _("視界内の全モンスターに対して轟音の攻撃を行う。さらに、近くにいるモンスターを怒らせる。",
781                 "Damages all monsters in sight with sound. Aggravates nearby monsters.");
782         }
783
784         if (cast) {
785             msg_print(_("雄叫びをあげた!", "You roar!"));
786             project_all_los(player_ptr, AttributeType::SOUND, randint1(plev * 3));
787             aggravate_monsters(player_ptr, 0);
788         }
789         break;
790
791     case 23:
792         if (name) {
793             return _("無双三段", "Musou-Sandan");
794         }
795         if (desc) {
796             return _("強力な3段攻撃を繰り出す。", "Attacks with three powerful strikes.");
797         }
798
799         if (cast) {
800             int i;
801
802             if (!get_direction(player_ptr, &dir, false, false)) {
803                 return std::nullopt;
804             }
805             if (dir == 5) {
806                 return std::nullopt;
807             }
808
809             const auto &floor = *player_ptr->current_floor_ptr;
810             for (i = 0; i < 3; i++) {
811                 POSITION y, x;
812                 POSITION ny, nx;
813                 MONSTER_IDX m_idx;
814                 grid_type *g_ptr;
815                 MonsterEntity *m_ptr;
816
817                 y = player_ptr->y + ddy[dir];
818                 x = player_ptr->x + ddx[dir];
819                 g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
820
821                 if (g_ptr->m_idx) {
822                     do_cmd_attack(player_ptr, y, x, HISSATSU_3DAN);
823                 } else {
824                     msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
825                     return std::nullopt;
826                 }
827
828                 if (dungeons_info[floor.dungeon_idx].flags.has(DungeonFeatureType::NO_MELEE)) {
829                     return "";
830                 }
831
832                 /* Monster is dead? */
833                 if (!g_ptr->m_idx) {
834                     break;
835                 }
836
837                 ny = y + ddy[dir];
838                 nx = x + ddx[dir];
839                 m_idx = g_ptr->m_idx;
840                 m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
841
842                 /* Monster cannot move back? */
843                 if (!monster_can_enter(player_ptr, ny, nx, &monraces_info[m_ptr->r_idx], 0)) {
844                     /* -more- */
845                     if (i < 2) {
846                         msg_print(nullptr);
847                     }
848                     continue;
849                 }
850
851                 g_ptr->m_idx = 0;
852                 player_ptr->current_floor_ptr->grid_array[ny][nx].m_idx = m_idx;
853                 m_ptr->fy = ny;
854                 m_ptr->fx = nx;
855
856                 update_monster(player_ptr, m_idx, true);
857
858                 /* Redraw the old spot */
859                 lite_spot(player_ptr, y, x);
860
861                 /* Redraw the new spot */
862                 lite_spot(player_ptr, ny, nx);
863
864                 /* Player can move forward? */
865                 if (player_can_enter(player_ptr, g_ptr->feat, 0)) {
866                     if (!move_player_effect(player_ptr, y, x, MPE_FORGET_FLOW | MPE_HANDLE_STUFF | MPE_DONT_PICKUP)) {
867                         break;
868                     }
869                 } else {
870                     break;
871                 }
872
873                 /* -more- */
874                 if (i < 2) {
875                     msg_print(nullptr);
876                 }
877             }
878         }
879         break;
880
881     case 24:
882         if (name) {
883             return _("吸血鬼の牙", "Vampire's Fang");
884         }
885         if (desc) {
886             return _("攻撃した相手の体力を吸いとり、自分の体力を回復させる。生命を持たないモンスターには通じない。",
887                 "Attacks with vampiric strikes which absorb HP from a monster and heal you. Has no effect on unliving monsters.");
888         }
889
890         if (cast) {
891             POSITION y, x;
892
893             if (!get_direction(player_ptr, &dir, false, false)) {
894                 return std::nullopt;
895             }
896             if (dir == 5) {
897                 return std::nullopt;
898             }
899
900             y = player_ptr->y + ddy[dir];
901             x = player_ptr->x + ddx[dir];
902
903             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
904                 do_cmd_attack(player_ptr, y, x, HISSATSU_DRAIN);
905             } else {
906                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
907                 return std::nullopt;
908             }
909         }
910         break;
911
912     case 25:
913         if (name) {
914             return _("幻惑", "Moon Dazzling");
915         }
916         if (desc) {
917             return _("視界内の起きている全モンスターに朦朧、混乱、眠りを与えようとする。", "Attempts to stun, confuse and put to sleep all waking monsters.");
918         }
919
920         if (cast) {
921             msg_print(_("武器を不規則に揺らした...", "You irregularly wave your weapon..."));
922             project_all_los(player_ptr, AttributeType::ENGETSU, plev * 4);
923         }
924         break;
925
926     case 26:
927         if (name) {
928             return _("百人斬り", "Hundred Slaughter");
929         }
930         if (desc) {
931             return _("連続して入身でモンスターを攻撃する。攻撃するたびにMPを消費。MPがなくなるか、モンスターを倒せなかったら百人斬りは終了する。",
932                 "Performs a series of rush attacks. The series continues as long as the attacked monster dies and you have sufficient SP.");
933         }
934
935         if (cast) {
936             const int mana_cost_per_monster = 8;
937             bool is_new = true;
938             bool mdeath;
939
940             do {
941                 if (!rush_attack(player_ptr, &mdeath)) {
942                     break;
943                 }
944                 if (is_new) {
945                     /* Reserve needed mana point */
946                     player_ptr->csp -= technic_info[REALM_HISSATSU - MIN_TECHNIC][26].smana;
947                     is_new = false;
948                 } else {
949                     player_ptr->csp -= mana_cost_per_monster;
950                 }
951
952                 if (!mdeath) {
953                     break;
954                 }
955                 command_dir = 0;
956
957                 player_ptr->redraw |= PR_MP;
958                 handle_stuff(player_ptr);
959             } while (player_ptr->csp > mana_cost_per_monster);
960
961             if (is_new) {
962                 return std::nullopt;
963             }
964
965             /* Restore reserved mana */
966             player_ptr->csp += technic_info[REALM_HISSATSU - MIN_TECHNIC][26].smana;
967         }
968         break;
969
970     case 27:
971         if (name) {
972             return _("天翔龍閃", "Dragonic Flash");
973         }
974         if (desc) {
975             return _("視界内の場所を指定して、その場所と自分の間にいる全モンスターを攻撃し、その場所に移動する。",
976                 "Runs toward given location while attacking all monsters on the path.");
977         }
978
979         if (cast) {
980             POSITION y, x;
981
982             if (!tgt_pt(player_ptr, &x, &y)) {
983                 return std::nullopt;
984             }
985
986             const auto is_teleportable = cave_player_teleportable_bold(player_ptr, y, x, TELEPORT_SPONTANEOUS);
987             const auto dist = distance(y, x, player_ptr->y, player_ptr->x);
988             if (!is_teleportable || (dist > MAX_PLAYER_SIGHT / 2) || !projectable(player_ptr, player_ptr->y, player_ptr->x, y, x)) {
989                 msg_print(_("失敗!", "You cannot move to that place!"));
990                 break;
991             }
992             if (player_ptr->anti_tele) {
993                 msg_print(_("不思議な力がテレポートを防いだ!", "A mysterious force prevents you from teleporting!"));
994                 break;
995             }
996             project(player_ptr, 0, 0, y, x, HISSATSU_ISSEN, AttributeType::ATTACK, PROJECT_BEAM | PROJECT_KILL);
997             teleport_player_to(player_ptr, y, x, TELEPORT_SPONTANEOUS);
998         }
999         break;
1000
1001     case 28:
1002         if (name) {
1003             return _("二重の剣撃", "Twin Slash");
1004         }
1005         if (desc) {
1006             return _("1ターンで2度攻撃を行う。", "Attack twice in one turn.");
1007         }
1008
1009         if (cast) {
1010             POSITION x, y;
1011
1012             if (!get_rep_dir(player_ptr, &dir, false)) {
1013                 return std::nullopt;
1014             }
1015
1016             y = player_ptr->y + ddy[dir];
1017             x = player_ptr->x + ddx[dir];
1018
1019             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
1020                 do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
1021                 if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
1022                     handle_stuff(player_ptr);
1023                     do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
1024                 }
1025             } else {
1026                 msg_print(_("その方向にはモンスターはいません。", "You don't see any monster in this direction"));
1027                 return std::nullopt;
1028             }
1029         }
1030         break;
1031
1032     case 29:
1033         if (name) {
1034             return _("虎伏絶刀勢", "Kofuku-Zettousei");
1035         }
1036         if (desc) {
1037             return _("強力な攻撃を行い、近くの場所にも効果が及ぶ。", "Performs a powerful attack which even affects nearby monsters.");
1038         }
1039
1040         if (cast) {
1041             int total_damage = 0, basedam, i;
1042             POSITION y, x;
1043             ItemEntity *o_ptr;
1044
1045             if (!get_direction(player_ptr, &dir, false, false)) {
1046                 return std::nullopt;
1047             }
1048             if (dir == 5) {
1049                 return std::nullopt;
1050             }
1051
1052             y = player_ptr->y + ddy[dir];
1053             x = player_ptr->x + ddx[dir];
1054
1055             auto &floor = *player_ptr->current_floor_ptr;
1056             if (dungeons_info[floor.dungeon_idx].flags.has(DungeonFeatureType::NO_MELEE)) {
1057                 msg_print(_("なぜか攻撃することができない。", "Something prevents you from attacking."));
1058                 return "";
1059             }
1060             msg_print(_("武器を大きく振り下ろした。", "You swing your weapon downward."));
1061             for (i = 0; i < 2; i++) {
1062                 int damage;
1063                 if (!has_melee_weapon(player_ptr, INVEN_MAIN_HAND + i)) {
1064                     break;
1065                 }
1066                 o_ptr = &player_ptr->inventory_list[INVEN_MAIN_HAND + i];
1067                 basedam = (o_ptr->dd * (o_ptr->ds + 1)) * 50;
1068                 damage = o_ptr->to_d * 100;
1069                 auto flags = object_flags(o_ptr);
1070
1071                 // @todo ヴォーパルの多重定義.
1072                 if (o_ptr->is_specific_artifact(FixedArtifactId::VORPAL_BLADE) || o_ptr->is_specific_artifact(FixedArtifactId::CHAINSWORD)) {
1073                     /* vorpal blade */
1074                     basedam *= 5;
1075                     basedam /= 3;
1076                 } else if (flags.has(TR_VORPAL)) {
1077                     /* vorpal flag only */
1078                     basedam *= 11;
1079                     basedam /= 9;
1080                 }
1081                 damage += basedam;
1082                 damage += player_ptr->to_d[i] * 100;
1083                 damage *= player_ptr->num_blow[i];
1084                 total_damage += (damage / 100);
1085             }
1086
1087             const auto is_bold = cave_has_flag_bold(&floor, y, x, TerrainCharacteristics::PROJECT);
1088             constexpr auto flags = PROJECT_KILL | PROJECT_JUMP | PROJECT_ITEM;
1089             project(player_ptr, 0, (is_bold ? 5 : 0), y, x, total_damage * 3 / 2, AttributeType::METEOR, flags);
1090         }
1091         break;
1092
1093     case 30:
1094         if (name) {
1095             return _("慶雲鬼忍剣", "Keiun-Kininken");
1096         }
1097         if (desc) {
1098             return _("自分もダメージをくらうが、相手に非常に大きなダメージを与える。アンデッドには特に効果がある。",
1099                 "Attacks a monster with extremely powerful damage, but you also take some damage. Hurts an undead monster greatly.");
1100         }
1101
1102         if (cast) {
1103             POSITION y, x;
1104
1105             if (!get_direction(player_ptr, &dir, false, false)) {
1106                 return std::nullopt;
1107             }
1108             if (dir == 5) {
1109                 return std::nullopt;
1110             }
1111
1112             y = player_ptr->y + ddy[dir];
1113             x = player_ptr->x + ddx[dir];
1114
1115             if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
1116                 do_cmd_attack(player_ptr, y, x, HISSATSU_UNDEAD);
1117             } else {
1118                 msg_print(_("その方向にはモンスターはいません。", "There is no monster."));
1119                 return std::nullopt;
1120             }
1121             take_hit(player_ptr, DAMAGE_NOESCAPE, 100 + randint1(100), _("慶雲鬼忍剣を使った衝撃", "exhaustion on using Keiun-Kininken"));
1122         }
1123         break;
1124
1125     case 31:
1126         if (name) {
1127             return _("切腹", "Harakiri");
1128         }
1129         if (desc) {
1130             return _("「武士道とは、死ぬことと見つけたり。」", "'Bushido, the way of warriors, is found in death'");
1131         }
1132
1133         if (cast) {
1134             int i;
1135             if (!get_check(_("本当に自殺しますか?", "Do you really want to commit suicide? "))) {
1136                 return std::nullopt;
1137             }
1138             /* Special Verification for suicide */
1139             prt(_("確認のため '@' を押して下さい。", "Please verify SUICIDE by typing the '@' sign: "), 0, 0);
1140
1141             flush();
1142             i = inkey();
1143             prt("", 0, 0);
1144             if (i != '@') {
1145                 return std::nullopt;
1146             }
1147             if (w_ptr->total_winner) {
1148                 take_hit(player_ptr, DAMAGE_FORCE, 9999, "Seppuku");
1149                 w_ptr->total_winner = true;
1150             } else {
1151                 msg_print(_("武士道とは、死ぬことと見つけたり。", "The meaning of bushido is found in death."));
1152                 take_hit(player_ptr, DAMAGE_FORCE, 9999, "Seppuku");
1153             }
1154         }
1155         break;
1156     }
1157
1158     return "";
1159 }