OSDN Git Service

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