OSDN Git Service

Merge pull request #3651 from Hourier/Make-TerrainList
[hengbandforosx/hengbandosx.git] / src / cmd-action / cmd-hissatsu.cpp
1 /*!
2  * @brief 剣術の実装 / Blade arts
3  * @date 2014/01/17
4  * @author
5  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke\n
6  * This software may be copied and distributed for educational, research,\n
7  * and not for profit purposes provided that this copyright and statement\n
8  * are included in all such copies.  Other copyrights may also apply.\n
9  * 2014 Deskull rearranged comment for Doxygen.\n
10  */
11
12 #include "action/action-limited.h"
13 #include "cmd-action/cmd-spell.h"
14 #include "core/asking-player.h"
15 #include "core/stuff-handler.h"
16 #include "core/window-redrawer.h"
17 #include "floor/floor-object.h"
18 #include "game-option/disturbance-options.h"
19 #include "game-option/text-display-options.h"
20 #include "inventory/inventory-slot-types.h"
21 #include "io/command-repeater.h"
22 #include "io/input-key-requester.h"
23 #include "main/sound-definitions-table.h"
24 #include "main/sound-of-music.h"
25 #include "monster-race/monster-race-hook.h"
26 #include "object/item-tester-hooker.h"
27 #include "object/item-use-flags.h"
28 #include "player-base/player-class.h"
29 #include "player-info/equipment-info.h"
30 #include "player-info/samurai-data-type.h"
31 #include "player-status/player-energy.h"
32 #include "player/attack-defense-types.h"
33 #include "player/special-defense-types.h"
34 #include "spell/spells-execution.h"
35 #include "spell/technic-info-table.h"
36 #include "status/action-setter.h"
37 #include "system/item-entity.h"
38 #include "system/player-type-definition.h"
39 #include "system/redrawing-flags-updater.h"
40 #include "term/screen-processor.h"
41 #include "term/z-form.h"
42 #include "util/int-char-converter.h"
43 #include "view/display-messages.h"
44
45 #define TECHNIC_HISSATSU (REALM_HISSATSU - MIN_TECHNIC)
46
47 /*!
48  * @brief 使用可能な剣術を選択する /
49  * Allow user to choose a blade arts.
50  * @param sn 選択した特殊技能ID、キャンセルの場合-1、不正な選択の場合-2を返す
51  * @return 発動可能な魔法を選択した場合TRUE、キャンセル処理か不正な選択が行われた場合FALSEを返す。
52  * @details
53  * If a valid spell is chosen, saves it in '*sn' and returns TRUE\n
54  * If the user hits escape, returns FALSE, and set '*sn' to -1\n
55  * If there are no legal choices, returns FALSE, and sets '*sn' to -2\n
56  *\n
57  * The "prompt" should be "cast", "recite", or "study"\n
58  * The "known" should be TRUE for cast/pray, FALSE for study\n
59  *\n
60  * nb: This function has a (trivial) display bug which will be obvious\n
61  * when you run it. It's probably easy to fix but I haven't tried,\n
62  * sorry.\n
63  */
64 static int get_hissatsu_power(PlayerType *player_ptr, SPELL_IDX *sn)
65 {
66     int j = 0;
67     int num = 0;
68     POSITION y = 1;
69     POSITION x = 15;
70     PLAYER_LEVEL plev = player_ptr->lev;
71     char choice;
72     concptr p = _("必殺剣", "special attack");
73     COMMAND_CODE code;
74     magic_type spell;
75     int menu_line = (use_menu ? 1 : 0);
76
77     /* Assume cancelled */
78     *sn = (-1);
79
80     /* Get the spell, if available */
81     if (repeat_pull(&code)) {
82         *sn = (SPELL_IDX)code;
83         /* Verify the spell */
84         if (technic_info[TECHNIC_HISSATSU][*sn].slevel <= plev) {
85             /* Success */
86             return true;
87         }
88     }
89
90     auto flag = false;
91     auto redraw = false;
92
93     int i;
94     int selections[32]{};
95     for (i = 0; i < 32; i++) {
96         if (technic_info[TECHNIC_HISSATSU][i].slevel <= PY_MAX_LEVEL) {
97             selections[num] = i;
98             num++;
99         }
100     }
101
102     constexpr auto fmt = _("(%s^ %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%s^s %c-%c, *=List, ESC=exit) Use which %s? ");
103     const auto prompt = format(fmt, p, I2A(0), "abcdefghijklmnopqrstuvwxyz012345"[num - 1], p);
104
105     if (use_menu) {
106         screen_save();
107     }
108     choice = always_show_list ? ESCAPE : 1;
109
110     while (!flag) {
111         if (choice == ESCAPE) {
112             choice = ' ';
113         } else {
114             const auto new_choice = input_command(prompt);
115             if (!new_choice.has_value()) {
116                 break;
117             }
118
119             choice = new_choice.value();
120         }
121
122         auto should_redraw_cursor = true;
123         if (use_menu && choice != ' ') {
124             switch (choice) {
125             case '0': {
126                 screen_load();
127                 return false;
128             }
129
130             case '8':
131             case 'k':
132             case 'K': {
133                 do {
134                     menu_line += 31;
135                     if (menu_line > 32) {
136                         menu_line -= 32;
137                     }
138                 } while (!(player_ptr->spell_learned1 & (1UL << (menu_line - 1))));
139                 break;
140             }
141
142             case '2':
143             case 'j':
144             case 'J': {
145                 do {
146                     menu_line++;
147                     if (menu_line > 32) {
148                         menu_line -= 32;
149                     }
150                 } while (!(player_ptr->spell_learned1 & (1UL << (menu_line - 1))));
151                 break;
152             }
153
154             case '4':
155             case 'h':
156             case 'H':
157             case '6':
158             case 'l':
159             case 'L': {
160                 bool reverse = false;
161                 if ((choice == '4') || (choice == 'h') || (choice == 'H')) {
162                     reverse = true;
163                 }
164                 if (menu_line > 16) {
165                     menu_line -= 16;
166                     reverse = true;
167                 } else {
168                     menu_line += 16;
169                 }
170                 while (!(player_ptr->spell_learned1 & (1UL << (menu_line - 1)))) {
171                     if (reverse) {
172                         menu_line--;
173                         if (menu_line < 2) {
174                             reverse = false;
175                         }
176                     } else {
177                         menu_line++;
178                         if (menu_line > 31) {
179                             reverse = true;
180                         }
181                     }
182                 }
183                 break;
184             }
185
186             case 'x':
187             case 'X':
188             case '\r':
189             case '\n': {
190                 i = menu_line - 1;
191                 should_redraw_cursor = false;
192                 break;
193             }
194             }
195         }
196         /* Request redraw */
197         if ((choice == ' ') || (choice == '*') || (choice == '?') || (use_menu && should_redraw_cursor)) {
198             /* Show the list */
199             if (!redraw || use_menu) {
200                 int line;
201                 redraw = true;
202                 if (!use_menu) {
203                     screen_save();
204                 }
205
206                 /* Display a list of spells */
207                 prt("", y, x);
208                 put_str(_("名前              Lv  MP      名前              Lv  MP ", "name              Lv  SP      name              Lv  SP "), y, x + 5);
209                 prt("", y + 1, x);
210                 /* Dump the spells */
211                 for (i = 0, line = 0; i < 32; i++) {
212                     spell = technic_info[TECHNIC_HISSATSU][i];
213
214                     if (spell.slevel > PY_MAX_LEVEL) {
215                         continue;
216                     }
217                     line++;
218                     if (!(player_ptr->spell_learned1 >> i)) {
219                         break;
220                     }
221
222                     /* Access the spell */
223                     if (spell.slevel > plev) {
224                         continue;
225                     }
226                     if (!(player_ptr->spell_learned1 & (1UL << i))) {
227                         continue;
228                     }
229                     std::string psi_desc;
230                     if (use_menu) {
231                         if (i == (menu_line - 1)) {
232                             psi_desc = _("  》", "  > ");
233                         } else {
234                             psi_desc = "    ";
235                         }
236
237                     } else {
238                         char letter;
239                         if (line <= 26) {
240                             letter = I2A(line - 1);
241                         } else {
242                             letter = '0' + line - 27;
243                         }
244                         psi_desc = format("  %c)", letter);
245                     }
246
247                     /* Dump the spell --(-- */
248                     const auto spell_name = exe_spell(player_ptr, REALM_HISSATSU, i, SpellProcessType::NAME);
249                     psi_desc.append(format(" %-18s%2d %3d", spell_name->data(), spell.slevel, spell.smana));
250                     prt(psi_desc, y + (line % 17) + (line >= 17), x + (line / 17) * 30);
251                     prt("", y + (line % 17) + (line >= 17) + 1, x + (line / 17) * 30);
252                 }
253             }
254
255             /* Hide the list */
256             else {
257                 /* Hide list */
258                 redraw = false;
259                 screen_load();
260             }
261
262             /* Redo asking */
263             continue;
264         }
265
266         if (!use_menu) {
267             if (isalpha(choice)) {
268                 i = A2I(choice);
269             } else {
270                 i = choice - '0' + 26;
271             }
272         }
273
274         /* Totally Illegal */
275         if ((i < 0) || (i >= 32) || !(player_ptr->spell_learned1 & (1U << selections[i]))) {
276             bell();
277             continue;
278         }
279
280         j = selections[i];
281
282         /* Stop the loop */
283         flag = true;
284     }
285     if (redraw) {
286         screen_load();
287     }
288
289     RedrawingFlagsUpdater::get_instance().set_flag(SubWindowRedrawingFlag::SPELL);
290     handle_stuff(player_ptr);
291
292     /* Abort if needed */
293     if (!flag) {
294         return false;
295     }
296
297     /* Save the choice */
298     (*sn) = j;
299
300     repeat_push((COMMAND_CODE)j);
301
302     /* Success */
303     return true;
304 }
305
306 /*!
307  * @brief 剣術コマンドのメインルーチン
308  */
309 void do_cmd_hissatsu(PlayerType *player_ptr)
310 {
311     SPELL_IDX n = 0;
312     magic_type spell;
313
314     if (cmd_limit_confused(player_ptr)) {
315         return;
316     }
317     if (!has_melee_weapon(player_ptr, INVEN_MAIN_HAND) && !has_melee_weapon(player_ptr, INVEN_SUB_HAND)) {
318         if (flush_failure) {
319             flush();
320         }
321         msg_print(_("武器を持たないと必殺技は使えない!", "You need to wield a weapon!"));
322         return;
323     }
324     if (!player_ptr->spell_learned1) {
325         msg_print(_("何も技を知らない。", "You don't know any special attacks."));
326         return;
327     }
328
329     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU, SamuraiStanceType::IAI, SamuraiStanceType::FUUJIN, SamuraiStanceType::KOUKIJIN });
330
331     if (!get_hissatsu_power(player_ptr, &n)) {
332         return;
333     }
334
335     spell = technic_info[TECHNIC_HISSATSU][n];
336
337     /* Verify "dangerous" spells */
338     if (spell.smana > player_ptr->csp) {
339         if (flush_failure) {
340             flush();
341         }
342         /* Warning */
343         msg_print(_("MPが足りません。", "You do not have enough mana to use this power."));
344         msg_print(nullptr);
345         return;
346     }
347
348     sound(SOUND_ZAP);
349
350     if (!exe_spell(player_ptr, REALM_HISSATSU, n, SpellProcessType::CAST)) {
351         return;
352     }
353
354     PlayerEnergy(player_ptr).set_player_turn_energy(100);
355     player_ptr->csp -= spell.smana;
356     if (player_ptr->csp < 0) {
357         player_ptr->csp = 0;
358     }
359
360     auto &rfu = RedrawingFlagsUpdater::get_instance();
361     rfu.set_flag(MainWindowRedrawingFlag::MP);
362     static constexpr auto flags = {
363         SubWindowRedrawingFlag::PLAYER,
364         SubWindowRedrawingFlag::SPELL,
365     };
366     rfu.set_flags(flags);
367 }
368
369 /*!
370  * @brief 剣術コマンドの学習
371  */
372 void do_cmd_gain_hissatsu(PlayerType *player_ptr)
373 {
374     PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU, SamuraiStanceType::KOUKIJIN });
375     if (cmd_limit_blind(player_ptr) || cmd_limit_confused(player_ptr)) {
376         return;
377     }
378
379     if (!(player_ptr->new_spells)) {
380         msg_print(_("新しい必殺技を覚えることはできない!", "You cannot learn any new special attacks!"));
381         return;
382     }
383
384 #ifdef JP
385     msg_format("あと %d 種の必殺技を学べる。", player_ptr->new_spells);
386 #else
387     msg_format("You can learn %d new special attack%s.", player_ptr->new_spells, (player_ptr->new_spells == 1 ? "" : "s"));
388 #endif
389
390     constexpr auto q = _("どの書から学びますか? ", "Study which book? ");
391     constexpr auto s = _("読める書がない。", "You have no books that you can read.");
392     constexpr auto options = USE_INVEN | USE_FLOOR;
393     short i_idx;
394     const auto *o_ptr = choose_object(player_ptr, &i_idx, q, s, options, TvalItemTester(ItemKindType::HISSATSU_BOOK));
395     if (o_ptr == nullptr) {
396         return;
397     }
398
399     const auto sval = o_ptr->bi_key.sval().value();
400     auto gain = false;
401     for (auto i = sval * 8; i < sval * 8 + 8; i++) {
402         if (player_ptr->spell_learned1 & (1UL << i)) {
403             continue;
404         }
405
406         if (technic_info[TECHNIC_HISSATSU][i].slevel > player_ptr->lev) {
407             continue;
408         }
409
410         player_ptr->spell_learned1 |= (1UL << i);
411         player_ptr->spell_worked1 |= (1UL << i);
412         const auto spell_name = exe_spell(player_ptr, REALM_HISSATSU, i, SpellProcessType::NAME);
413         msg_format(_("%sの技を覚えた。", "You have learned the special attack of %s."), spell_name->data());
414         int j;
415         for (j = 0; j < 64; j++) {
416             /* Stop at the first empty space */
417             if (player_ptr->spell_order[j] == 99) {
418                 break;
419             }
420         }
421
422         player_ptr->spell_order[j] = i;
423         gain = true;
424     }
425
426     if (!gain) {
427         msg_print(_("何も覚えられなかった。", "You were not able to learn any special attacks."));
428     } else {
429         PlayerEnergy(player_ptr).set_player_turn_energy(100);
430     }
431
432     RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::SPELLS);
433 }