OSDN Git Service

Refactor monster_desc() to avoid sprintf(). To work with the refactored monster_desc...
[hengbandforosx/hengbandosx.git] / src / cmd-action / cmd-pet.cpp
1 #include "cmd-action/cmd-pet.h"
2 #include "action/action-limited.h"
3 #include "cmd-action/cmd-attack.h"
4 #include "cmd-io/cmd-dump.h"
5 #include "core/asking-player.h"
6 #include "core/player-redraw-types.h"
7 #include "core/player-update-types.h"
8 #include "core/stuff-handler.h"
9 #include "core/window-redrawer.h"
10 #include "effect/spells-effect-util.h"
11 #include "floor/geometry.h"
12 #include "floor/pattern-walk.h"
13 #include "game-option/input-options.h"
14 #include "game-option/map-screen-options.h"
15 #include "game-option/play-record-options.h"
16 #include "game-option/text-display-options.h"
17 #include "grid/grid.h"
18 #include "inventory/inventory-slot-types.h"
19 #include "io/command-repeater.h"
20 #include "io/cursor.h"
21 #include "io/input-key-acceptor.h"
22 #include "io/input-key-requester.h"
23 #include "io/write-diary.h"
24 #include "main/sound-of-music.h"
25 #include "monster-floor/monster-object.h"
26 #include "monster-floor/monster-remover.h"
27 #include "monster-race/monster-race.h"
28 #include "monster-race/race-flags1.h"
29 #include "monster-race/race-flags7.h"
30 #include "monster/monster-describer.h"
31 #include "monster/monster-description-types.h"
32 #include "monster/monster-info.h"
33 #include "monster/monster-status-setter.h"
34 #include "monster/monster-status.h"
35 #include "monster/smart-learn-types.h"
36 #include "object-hook/hook-weapon.h"
37 #include "pet/pet-util.h"
38 #include "player-base/player-class.h"
39 #include "player-info/class-info.h"
40 #include "player-info/equipment-info.h"
41 #include "player-info/samurai-data-type.h"
42 #include "player-status/player-energy.h"
43 #include "player-status/player-hand-types.h"
44 #include "player/attack-defense-types.h"
45 #include "player/player-damage.h"
46 #include "player/player-move.h"
47 #include "player/player-skill.h"
48 #include "player/player-status-flags.h"
49 #include "player/special-defense-types.h"
50 #include "status/action-setter.h"
51 #include "system/floor-type-definition.h"
52 #include "system/grid-type-definition.h"
53 #include "system/item-entity.h"
54 #include "system/monster-entity.h"
55 #include "system/monster-race-info.h"
56 #include "system/player-type-definition.h"
57 #include "system/terrain-type-definition.h"
58 #include "target/target-checker.h"
59 #include "target/target-getter.h"
60 #include "target/target-setter.h"
61 #include "target/target-types.h"
62 #include "term/screen-processor.h"
63 #include "timed-effect/player-hallucination.h"
64 #include "timed-effect/timed-effects.h"
65 #include "util/bit-flags-calculator.h"
66 #include "util/int-char-converter.h"
67 #include "util/quarks.h"
68 #include "util/sort.h"
69 #include "view/display-messages.h"
70 #include "world/world.h"
71 #include <sstream>
72
73 /*!
74  * @brief ペットを開放するコマンドのメインルーチン
75  */
76 void do_cmd_pet_dismiss(PlayerType *player_ptr)
77 {
78     MonsterEntity *m_ptr;
79     bool all_pets = false;
80     int Dismissed = 0;
81
82     uint16_t dummy_why;
83     bool cu, cv;
84
85     cu = game_term->scr->cu;
86     cv = game_term->scr->cv;
87     game_term->scr->cu = 0;
88     game_term->scr->cv = 1;
89
90     /* Allocate the "who" array */
91     std::vector<MONSTER_IDX> who;
92
93     /* Process the monsters (backwards) */
94     for (MONSTER_IDX pet_ctr = player_ptr->current_floor_ptr->m_max - 1; pet_ctr >= 1; pet_ctr--) {
95         const auto &m_ref = player_ptr->current_floor_ptr->m_list[pet_ctr];
96         if (m_ref.is_pet()) {
97             who.push_back(pet_ctr);
98         }
99     }
100
101     ang_sort(player_ptr, who.data(), &dummy_why, who.size(), ang_sort_comp_pet_dismiss, ang_sort_swap_hook);
102
103     /* Process the monsters (backwards) */
104     for (auto i = 0U; i < who.size(); i++) {
105         auto pet_ctr = who[i];
106         m_ptr = &player_ptr->current_floor_ptr->m_list[pet_ctr];
107
108         bool delete_this = false;
109         bool kakunin = ((pet_ctr == player_ptr->riding) || (m_ptr->nickname));
110         const auto friend_name = monster_desc(player_ptr, m_ptr, MD_ASSUME_VISIBLE);
111
112         if (!all_pets) {
113             /* Hack -- health bar for this monster */
114             health_track(player_ptr, pet_ctr);
115             handle_stuff(player_ptr);
116
117             msg_format(_("%sを放しますか? [Yes/No/Unnamed (%d体)]", "Dismiss %s? [Yes/No/Unnamed (%d remain)]"), friend_name.data(), who.size() - i);
118
119             if (m_ptr->ml) {
120                 move_cursor_relative(m_ptr->fy, m_ptr->fx);
121             }
122
123             while (true) {
124                 char ch = inkey();
125
126                 if (ch == 'Y' || ch == 'y') {
127                     delete_this = true;
128
129                     if (kakunin) {
130                         msg_format(_("本当によろしいですか? (%s) ", "Are you sure? (%s) "), friend_name.data());
131                         ch = inkey();
132                         if (ch != 'Y' && ch != 'y') {
133                             delete_this = false;
134                         }
135                     }
136                     break;
137                 }
138
139                 if (ch == 'U' || ch == 'u') {
140                     all_pets = true;
141                     break;
142                 }
143
144                 if (ch == ESCAPE || ch == 'N' || ch == 'n') {
145                     break;
146                 }
147
148                 bell();
149             }
150         }
151
152         if ((all_pets && !kakunin) || (!all_pets && delete_this)) {
153             if (record_named_pet && m_ptr->nickname) {
154                 const auto m_name = monster_desc(player_ptr, m_ptr, MD_INDEF_VISIBLE);
155                 exe_write_diary(player_ptr, DIARY_NAMED_PET, RECORD_NAMED_PET_DISMISS, m_name.data());
156             }
157
158             if (pet_ctr == player_ptr->riding) {
159                 msg_format(_("%sから降りた。", "You dismount from %s. "), friend_name.data());
160
161                 player_ptr->riding = 0;
162
163                 player_ptr->update |= (PU_MONSTERS);
164                 player_ptr->redraw |= (PR_EXTRA | PR_UHEALTH);
165             }
166
167             /* HACK : Add the line to message buffer */
168             msg_format(_("%s を放した。", "Dismissed %s."), friend_name.data());
169             player_ptr->update |= (PU_BONUS);
170             player_ptr->window_flags |= (PW_MESSAGE);
171
172             delete_monster_idx(player_ptr, pet_ctr);
173             Dismissed++;
174         }
175     }
176
177     game_term->scr->cu = cu;
178     game_term->scr->cv = cv;
179     term_fresh();
180
181 #ifdef JP
182     msg_format("%d 体のペットを放しました。", Dismissed);
183 #else
184     msg_format("You have dismissed %d pet%s.", Dismissed, (Dismissed == 1 ? "" : "s"));
185 #endif
186     if (Dismissed == 0 && all_pets) {
187         msg_print(_("'U'nnamed は、乗馬以外の名前のないペットだけを全て解放します。", "'U'nnamed means all your pets except named pets and your mount."));
188     }
189
190     handle_stuff(player_ptr);
191 }
192
193 /*!
194  * @brief ペットから騎乗/下馬するコマンドのメインルーチン /
195  * @param force 強制的に騎乗/下馬するならばTRUE
196  * @return 騎乗/下馬できたらTRUE
197  */
198 bool do_cmd_riding(PlayerType *player_ptr, bool force)
199 {
200     POSITION x, y;
201     DIRECTION dir = 0;
202     grid_type *g_ptr;
203     MonsterEntity *m_ptr;
204
205     if (!get_direction(player_ptr, &dir, false, false)) {
206         return false;
207     }
208     y = player_ptr->y + ddy[dir];
209     x = player_ptr->x + ddx[dir];
210     g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
211
212     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU });
213
214     if (player_ptr->riding) {
215         /* Skip non-empty grids */
216         if (!can_player_ride_pet(player_ptr, g_ptr, false)) {
217             msg_print(_("そちらには降りられません。", "You cannot go that direction."));
218             return false;
219         }
220
221         if (!pattern_seq(player_ptr, player_ptr->y, player_ptr->x, y, x)) {
222             return false;
223         }
224
225         if (g_ptr->m_idx) {
226             PlayerEnergy(player_ptr).set_player_turn_energy(100);
227
228             msg_print(_("モンスターが立ちふさがっている!", "There is a monster in the way!"));
229
230             do_cmd_attack(player_ptr, y, x, HISSATSU_NONE);
231             return false;
232         }
233
234         player_ptr->riding = 0;
235         player_ptr->pet_extra_flags &= ~(PF_TWO_HANDS);
236         player_ptr->riding_ryoute = player_ptr->old_riding_ryoute = false;
237     } else {
238         if (cmd_limit_confused(player_ptr)) {
239             return false;
240         }
241
242         m_ptr = &player_ptr->current_floor_ptr->m_list[g_ptr->m_idx];
243
244         if (!g_ptr->m_idx || !m_ptr->ml) {
245             msg_print(_("その場所にはモンスターはいません。", "There is no monster here."));
246             return false;
247         }
248         if (!m_ptr->is_pet() && !force) {
249             msg_print(_("そのモンスターはペットではありません。", "That monster is not a pet."));
250             return false;
251         }
252         if (!(monraces_info[m_ptr->r_idx].flags7 & RF7_RIDING)) {
253             msg_print(_("そのモンスターには乗れなさそうだ。", "This monster doesn't seem suitable for riding."));
254             return false;
255         }
256
257         if (!pattern_seq(player_ptr, player_ptr->y, player_ptr->x, y, x)) {
258             return false;
259         }
260
261         if (!can_player_ride_pet(player_ptr, g_ptr, true)) {
262             /* Feature code (applying "mimic" field) */
263             auto *f_ptr = &terrains_info[g_ptr->get_feat_mimic()];
264 #ifdef JP
265             msg_format("そのモンスターは%sの%sにいる。", f_ptr->name.data(),
266                 (f_ptr->flags.has_none_of({ TerrainCharacteristics::MOVE, TerrainCharacteristics::CAN_FLY }) || f_ptr->flags.has_none_of({ TerrainCharacteristics::LOS, TerrainCharacteristics::TREE })) ? "中" : "上");
267 #else
268             msg_format("This monster is %s the %s.",
269                 (f_ptr->flags.has_none_of({ TerrainCharacteristics::MOVE, TerrainCharacteristics::CAN_FLY }) || f_ptr->flags.has_none_of({ TerrainCharacteristics::LOS, TerrainCharacteristics::TREE })) ? "in" : "on", f_ptr->name.data());
270 #endif
271
272             return false;
273         }
274         if (monraces_info[m_ptr->r_idx].level > randint1((player_ptr->skill_exp[PlayerSkillKindType::RIDING] / 50 + player_ptr->lev / 2 + 20))) {
275             msg_print(_("うまく乗れなかった。", "You failed to ride."));
276             PlayerEnergy(player_ptr).set_player_turn_energy(100);
277             return false;
278         }
279
280         if (m_ptr->is_asleep()) {
281             const auto m_name = monster_desc(player_ptr, m_ptr, 0);
282             (void)set_monster_csleep(player_ptr, g_ptr->m_idx, 0);
283             msg_format(_("%sを起こした。", "You have woken %s up."), m_name.data());
284         }
285
286         if (player_ptr->action == ACTION_MONK_STANCE) {
287             set_action(player_ptr, ACTION_NONE);
288         }
289
290         player_ptr->riding = g_ptr->m_idx;
291
292         /* Hack -- remove tracked monster */
293         if (player_ptr->riding == player_ptr->health_who) {
294             health_track(player_ptr, 0);
295         }
296     }
297
298     PlayerEnergy(player_ptr).set_player_turn_energy(100);
299
300     /* Mega-Hack -- Forget the view and lite */
301     player_ptr->update |= (PU_UN_VIEW | PU_UN_LITE);
302     player_ptr->update |= (PU_BONUS);
303     player_ptr->redraw |= (PR_MAP | PR_EXTRA);
304     player_ptr->redraw |= (PR_UHEALTH);
305
306     (void)move_player_effect(player_ptr, y, x, MPE_HANDLE_STUFF | MPE_ENERGY_USE | MPE_DONT_PICKUP | MPE_DONT_SWAP_MON);
307
308     return true;
309 }
310
311 /*!
312  * @brief ペットに名前をつけるコマンドのメインルーチン
313  */
314 static void do_name_pet(PlayerType *player_ptr)
315 {
316     MonsterEntity *m_ptr;
317     char out_val[20];
318     bool old_name = false;
319     bool old_target_pet = target_pet;
320
321     target_pet = true;
322     if (!target_set(player_ptr, TARGET_KILL)) {
323         target_pet = old_target_pet;
324         return;
325     }
326
327     target_pet = old_target_pet;
328
329     if (player_ptr->current_floor_ptr->grid_array[target_row][target_col].m_idx) {
330         m_ptr = &player_ptr->current_floor_ptr->m_list[player_ptr->current_floor_ptr->grid_array[target_row][target_col].m_idx];
331
332         if (!m_ptr->is_pet()) {
333             msg_print(_("そのモンスターはペットではない。", "This monster is not a pet."));
334             return;
335         }
336         if (monraces_info[m_ptr->r_idx].kind_flags.has(MonsterKindType::UNIQUE)) {
337             msg_print(_("そのモンスターの名前は変えられない!", "You cannot change the name of this monster!"));
338             return;
339         }
340
341         msg_format(_("%sに名前をつける。", "Name %s."), monster_desc(player_ptr, m_ptr, 0).data());
342         msg_print(nullptr);
343
344         /* Start with nothing */
345         strcpy(out_val, "");
346
347         /* Use old inscription */
348         if (m_ptr->nickname) {
349             /* Start with the old inscription */
350             strcpy(out_val, quark_str(m_ptr->nickname));
351             old_name = true;
352         }
353
354         /* Get a new inscription (possibly empty) */
355         if (get_string(_("名前: ", "Name: "), out_val, 15)) {
356             if (out_val[0]) {
357                 /* Save the inscription */
358                 m_ptr->nickname = quark_add(out_val);
359                 if (record_named_pet) {
360                     exe_write_diary(player_ptr, DIARY_NAMED_PET, RECORD_NAMED_PET_NAME, monster_desc(player_ptr, m_ptr, MD_INDEF_VISIBLE).data());
361                 }
362             } else {
363                 if (record_named_pet && old_name) {
364                     exe_write_diary(player_ptr, DIARY_NAMED_PET, RECORD_NAMED_PET_UNNAME, monster_desc(player_ptr, m_ptr, MD_INDEF_VISIBLE).data());
365                 }
366                 m_ptr->nickname = 0;
367             }
368         }
369     }
370 }
371
372 /*!
373  * @brief ペットに関するコマンドリストのメインルーチン /
374  * Issue a pet command
375  */
376 void do_cmd_pet(PlayerType *player_ptr)
377 {
378     COMMAND_CODE i = 0;
379     int powers[36]{};
380     std::string power_desc[36];
381     bool flag, redraw;
382     char choice;
383     int pet_ctr;
384     MonsterEntity *m_ptr;
385     auto command_idx = 0;
386     int menu_line = use_menu ? 1 : 0;
387     auto num = 0;
388     if (player_ptr->wild_mode) {
389         return;
390     }
391
392     power_desc[num] = _("ペットを放す", "dismiss pets");
393     powers[num++] = PET_DISMISS;
394
395     auto is_hallucinated = player_ptr->effects()->hallucination()->is_hallucinated();
396     auto taget_of_pet = monraces_info[player_ptr->current_floor_ptr->m_list[player_ptr->pet_t_m_idx].ap_r_idx].name.data();
397     auto target_of_pet_appearance = is_hallucinated ? _("何か奇妙な物", "something strange") : taget_of_pet;
398     auto mes = _("ペットのターゲットを指定 (現在:%s)", "specify a target of pet (now:%s)");
399     auto target_name = player_ptr->pet_t_m_idx > 0 ? target_of_pet_appearance : _("指定なし", "nothing");
400     auto target_ask = format(mes, target_name);
401     power_desc[num] = target_ask;
402     powers[num++] = PET_TARGET;
403     power_desc[num] = _("近くにいろ", "stay close");
404
405     if (player_ptr->pet_follow_distance == PET_CLOSE_DIST) {
406         command_idx = num;
407     }
408     powers[num++] = PET_STAY_CLOSE;
409     power_desc[num] = _("ついて来い", "follow me");
410
411     if (player_ptr->pet_follow_distance == PET_FOLLOW_DIST) {
412         command_idx = num;
413     }
414     powers[num++] = PET_FOLLOW_ME;
415     power_desc[num] = _("敵を見つけて倒せ", "seek and destroy");
416
417     if (player_ptr->pet_follow_distance == PET_DESTROY_DIST) {
418         command_idx = num;
419     }
420     powers[num++] = PET_SEEK_AND_DESTROY;
421     power_desc[num] = _("少し離れていろ", "give me space");
422
423     if (player_ptr->pet_follow_distance == PET_SPACE_DIST) {
424         command_idx = num;
425     }
426     powers[num++] = PET_ALLOW_SPACE;
427     power_desc[num] = _("離れていろ", "stay away");
428
429     if (player_ptr->pet_follow_distance == PET_AWAY_DIST) {
430         command_idx = num;
431     }
432     powers[num++] = PET_STAY_AWAY;
433
434     if (player_ptr->pet_extra_flags & PF_OPEN_DOORS) {
435         power_desc[num] = _("ドアを開ける (現在:ON)", "pets open doors (now On)");
436     } else {
437         power_desc[num] = _("ドアを開ける (現在:OFF)", "pets open doors (now Off)");
438     }
439     powers[num++] = PET_OPEN_DOORS;
440
441     if (player_ptr->pet_extra_flags & PF_PICKUP_ITEMS) {
442         power_desc[num] = _("アイテムを拾う (現在:ON)", "pets pick up items (now On)");
443     } else {
444         power_desc[num] = _("アイテムを拾う (現在:OFF)", "pets pick up items (now Off)");
445     }
446     powers[num++] = PET_TAKE_ITEMS;
447
448     if (player_ptr->pet_extra_flags & PF_TELEPORT) {
449         power_desc[num] = _("テレポート系魔法を使う (現在:ON)", "allow teleport (now On)");
450     } else {
451         power_desc[num] = _("テレポート系魔法を使う (現在:OFF)", "allow teleport (now Off)");
452     }
453     powers[num++] = PET_TELEPORT;
454
455     if (player_ptr->pet_extra_flags & PF_ATTACK_SPELL) {
456         power_desc[num] = _("攻撃魔法を使う (現在:ON)", "allow cast attack spell (now On)");
457     } else {
458         power_desc[num] = _("攻撃魔法を使う (現在:OFF)", "allow cast attack spell (now Off)");
459     }
460     powers[num++] = PET_ATTACK_SPELL;
461
462     if (player_ptr->pet_extra_flags & PF_SUMMON_SPELL) {
463         power_desc[num] = _("召喚魔法を使う (現在:ON)", "allow cast summon spell (now On)");
464     } else {
465         power_desc[num] = _("召喚魔法を使う (現在:OFF)", "allow cast summon spell (now Off)");
466     }
467     powers[num++] = PET_SUMMON_SPELL;
468
469     if (player_ptr->pet_extra_flags & PF_BALL_SPELL) {
470         power_desc[num] = _("プレイヤーを巻き込む範囲魔法を使う (現在:ON)", "allow involve player in area spell (now On)");
471     } else {
472         power_desc[num] = _("プレイヤーを巻き込む範囲魔法を使う (現在:OFF)", "allow involve player in area spell (now Off)");
473     }
474     powers[num++] = PET_BALL_SPELL;
475
476     if (player_ptr->riding) {
477         power_desc[num] = _("ペットから降りる", "get off a pet");
478     } else {
479         power_desc[num] = _("ペットに乗る", "ride a pet");
480     }
481     powers[num++] = PET_RIDING;
482     power_desc[num] = _("ペットに名前をつける", "name pets");
483     powers[num++] = PET_NAME;
484
485     bool empty_main = can_attack_with_main_hand(player_ptr);
486     empty_main &= empty_hands(player_ptr, false) == EMPTY_HAND_SUB;
487     empty_main &= player_ptr->inventory_list[INVEN_MAIN_HAND].allow_two_hands_wielding();
488
489     bool empty_sub = can_attack_with_sub_hand(player_ptr);
490     empty_sub &= empty_hands(player_ptr, false) == EMPTY_HAND_MAIN;
491     empty_sub &= player_ptr->inventory_list[INVEN_SUB_HAND].allow_two_hands_wielding();
492
493     if (player_ptr->riding) {
494         if (empty_main || empty_sub) {
495             if (player_ptr->pet_extra_flags & PF_TWO_HANDS) {
496                 power_desc[num] = _("武器を片手で持つ", "use one hand to control the pet you are riding");
497             } else {
498                 power_desc[num] = _("武器を両手で持つ", "use both hands for a weapon");
499             }
500
501             powers[num++] = PET_TWO_HANDS;
502         } else {
503             switch (player_ptr->pclass) {
504             case PlayerClassType::MONK:
505             case PlayerClassType::FORCETRAINER:
506             case PlayerClassType::BERSERKER:
507                 if (empty_hands(player_ptr, false) == (EMPTY_HAND_MAIN | EMPTY_HAND_SUB)) {
508                     if (player_ptr->pet_extra_flags & PF_TWO_HANDS) {
509                         power_desc[num] = _("片手で格闘する", "use one hand to control the pet you are riding");
510                     } else {
511                         power_desc[num] = _("両手で格闘する", "use both hands for melee");
512                     }
513
514                     powers[num++] = PET_TWO_HANDS;
515                 } else if ((empty_hands(player_ptr, false) != EMPTY_HAND_NONE) && !has_melee_weapon(player_ptr, INVEN_MAIN_HAND) && !has_melee_weapon(player_ptr, INVEN_SUB_HAND)) {
516                     if (player_ptr->pet_extra_flags & PF_TWO_HANDS) {
517                         power_desc[num] = _("格闘を行わない", "use one hand to control the pet you are riding");
518                     } else {
519                         power_desc[num] = _("格闘を行う", "use one hand for melee");
520                     }
521
522                     powers[num++] = PET_TWO_HANDS;
523                 }
524                 break;
525
526             default:
527                 break;
528             }
529         }
530     }
531
532     if (!(repeat_pull(&i) && (i >= 0) && (i < num))) {
533         flag = false;
534         redraw = false;
535
536         std::string prompt;
537         if (use_menu) {
538             screen_save();
539             prompt = _("(コマンド、ESC=終了) コマンドを選んでください:", "(Command, ESC=exit) Choose command from menu.");
540         } else {
541             prompt = format(_("(コマンド %c-%c、'*'=一覧、ESC=終了) コマンドを選んでください:", "(Command %c-%c, *=List, ESC=exit) Select a command: "),
542                 I2A(0), I2A(num - 1));
543         }
544
545         choice = (always_show_list || use_menu) ? ESCAPE : 1;
546
547         /* Get a command from the user */
548         while (!flag) {
549             if (choice == ESCAPE) {
550                 choice = ' ';
551             } else if (!get_com(prompt.data(), &choice, true)) {
552                 break;
553             }
554
555             auto should_redraw_cursor = true;
556             if (use_menu && (choice != ' ')) {
557                 switch (choice) {
558                 case '0':
559                     screen_load();
560                     return;
561
562                 case '8':
563                 case 'k':
564                 case 'K':
565                     menu_line += (num - 1);
566                     break;
567
568                 case '2':
569                 case 'j':
570                 case 'J':
571                     menu_line++;
572                     break;
573
574                 case '4':
575                 case 'h':
576                 case 'H':
577                     menu_line = 1;
578                     break;
579
580                 case '6':
581                 case 'l':
582                 case 'L':
583                     menu_line = num;
584                     break;
585
586                 case 'x':
587                 case 'X':
588                 case '\r':
589                 case '\n':
590                     i = menu_line - 1;
591                     should_redraw_cursor = false;
592                     break;
593                 }
594                 if (menu_line > num) {
595                     menu_line -= num;
596                 }
597             }
598
599             /* Request redraw */
600             if ((choice == ' ') || (choice == '*') || (choice == '?') || (use_menu && should_redraw_cursor)) {
601                 /* Show the list */
602                 if (!redraw || use_menu) {
603                     byte y = 1, x = 0;
604                     redraw = true;
605                     if (!use_menu) {
606                         screen_save();
607                     }
608
609                     prt("", y++, x);
610
611                     /* Print list */
612                     int control;
613                     for (control = 0; control < num; control++) {
614                         /* Letter/number for power selection */
615                         std::stringstream ss;
616                         if (use_menu) {
617                             ss << format("%c%s ", (control == command_idx) ? '*' : ' ', (control == (menu_line - 1)) ? _("》", "> ") : "  ");
618                         } else {
619                             ss << format("%c%c) ", (control == command_idx) ? '*' : ' ', I2A(control));
620                         }
621
622                         ss << power_desc[control];
623                         prt(ss.str().data(), y + control, x);
624                     }
625
626                     prt("", y + std::min(control, 17), x);
627                 }
628
629                 /* Hide the list */
630                 else {
631                     /* Hide list */
632                     redraw = false;
633                     screen_load();
634                 }
635
636                 /* Redo asking */
637                 continue;
638             }
639
640             if (!use_menu) {
641                 i = A2I(choice);
642             }
643
644             /* Totally Illegal */
645             if ((i < 0) || (i >= num)) {
646                 bell();
647                 continue;
648             }
649
650             /* Stop the loop */
651             flag = true;
652         }
653         if (redraw) {
654             screen_load();
655         }
656
657         /* Abort if needed */
658         if (!flag) {
659             PlayerEnergy(player_ptr).reset_player_turn();
660             return;
661         }
662
663         repeat_push(i);
664     }
665     switch (powers[i]) {
666     case PET_DISMISS: /* Dismiss pets */
667     {
668         /* Check pets (backwards) */
669         for (pet_ctr = player_ptr->current_floor_ptr->m_max - 1; pet_ctr >= 1; pet_ctr--) {
670             const auto &m_ref = player_ptr->current_floor_ptr->m_list[pet_ctr];
671             if (m_ref.is_pet()) {
672                 break;
673             }
674         }
675
676         if (!pet_ctr) {
677             msg_print(_("ペットがいない!", "You have no pets!"));
678             break;
679         }
680         do_cmd_pet_dismiss(player_ptr);
681         (void)calculate_upkeep(player_ptr);
682         break;
683     }
684     case PET_TARGET: {
685         project_length = -1;
686         if (!target_set(player_ptr, TARGET_KILL)) {
687             player_ptr->pet_t_m_idx = 0;
688         } else {
689             auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[target_row][target_col];
690             if (g_ptr->m_idx && (player_ptr->current_floor_ptr->m_list[g_ptr->m_idx].ml)) {
691                 player_ptr->pet_t_m_idx = player_ptr->current_floor_ptr->grid_array[target_row][target_col].m_idx;
692                 player_ptr->pet_follow_distance = PET_DESTROY_DIST;
693             } else {
694                 player_ptr->pet_t_m_idx = 0;
695             }
696         }
697         project_length = 0;
698
699         break;
700     }
701     /* Call pets */
702     case PET_STAY_CLOSE: {
703         player_ptr->pet_follow_distance = PET_CLOSE_DIST;
704         player_ptr->pet_t_m_idx = 0;
705         break;
706     }
707     /* "Follow Me" */
708     case PET_FOLLOW_ME: {
709         player_ptr->pet_follow_distance = PET_FOLLOW_DIST;
710         player_ptr->pet_t_m_idx = 0;
711         break;
712     }
713     /* "Seek and destoy" */
714     case PET_SEEK_AND_DESTROY: {
715         player_ptr->pet_follow_distance = PET_DESTROY_DIST;
716         break;
717     }
718     /* "Give me space" */
719     case PET_ALLOW_SPACE: {
720         player_ptr->pet_follow_distance = PET_SPACE_DIST;
721         break;
722     }
723     /* "Stay away" */
724     case PET_STAY_AWAY: {
725         player_ptr->pet_follow_distance = PET_AWAY_DIST;
726         break;
727     }
728     /* flag - allow pets to open doors */
729     case PET_OPEN_DOORS: {
730         if (player_ptr->pet_extra_flags & PF_OPEN_DOORS) {
731             player_ptr->pet_extra_flags &= ~(PF_OPEN_DOORS);
732         } else {
733             player_ptr->pet_extra_flags |= (PF_OPEN_DOORS);
734         }
735         break;
736     }
737     /* flag - allow pets to pickup items */
738     case PET_TAKE_ITEMS: {
739         if (player_ptr->pet_extra_flags & PF_PICKUP_ITEMS) {
740             player_ptr->pet_extra_flags &= ~(PF_PICKUP_ITEMS);
741             for (pet_ctr = player_ptr->current_floor_ptr->m_max - 1; pet_ctr >= 1; pet_ctr--) {
742                 m_ptr = &player_ptr->current_floor_ptr->m_list[pet_ctr];
743                 if (m_ptr->is_pet()) {
744                     monster_drop_carried_objects(player_ptr, m_ptr);
745                 }
746             }
747         } else {
748             player_ptr->pet_extra_flags |= (PF_PICKUP_ITEMS);
749         }
750
751         break;
752     }
753     /* flag - allow pets to teleport */
754     case PET_TELEPORT: {
755         if (player_ptr->pet_extra_flags & PF_TELEPORT) {
756             player_ptr->pet_extra_flags &= ~(PF_TELEPORT);
757         } else {
758             player_ptr->pet_extra_flags |= (PF_TELEPORT);
759         }
760         break;
761     }
762     /* flag - allow pets to cast attack spell */
763     case PET_ATTACK_SPELL: {
764         if (player_ptr->pet_extra_flags & PF_ATTACK_SPELL) {
765             player_ptr->pet_extra_flags &= ~(PF_ATTACK_SPELL);
766         } else {
767             player_ptr->pet_extra_flags |= (PF_ATTACK_SPELL);
768         }
769         break;
770     }
771     /* flag - allow pets to cast attack spell */
772     case PET_SUMMON_SPELL: {
773         if (player_ptr->pet_extra_flags & PF_SUMMON_SPELL) {
774             player_ptr->pet_extra_flags &= ~(PF_SUMMON_SPELL);
775         } else {
776             player_ptr->pet_extra_flags |= (PF_SUMMON_SPELL);
777         }
778         break;
779     }
780     /* flag - allow pets to cast attack spell */
781     case PET_BALL_SPELL: {
782         if (player_ptr->pet_extra_flags & PF_BALL_SPELL) {
783             player_ptr->pet_extra_flags &= ~(PF_BALL_SPELL);
784         } else {
785             player_ptr->pet_extra_flags |= (PF_BALL_SPELL);
786         }
787         break;
788     }
789
790     case PET_RIDING: {
791         (void)do_cmd_riding(player_ptr, false);
792         break;
793     }
794
795     case PET_NAME: {
796         do_name_pet(player_ptr);
797         break;
798     }
799
800     case PET_TWO_HANDS: {
801         if (player_ptr->pet_extra_flags & PF_TWO_HANDS) {
802             player_ptr->pet_extra_flags &= ~(PF_TWO_HANDS);
803         } else {
804             player_ptr->pet_extra_flags |= (PF_TWO_HANDS);
805         }
806         player_ptr->update |= (PU_BONUS);
807         handle_stuff(player_ptr);
808         break;
809     }
810     }
811 }