OSDN Git Service

6e90e80f505d8bf831ba1acff649c6c64e6bedb3
[hengbandforosx/hengbandosx.git] / src / cmd-item / cmd-magiceat.cpp
1 /*!
2  * @brief プレイヤーのアイテムに関するコマンドの実装2 / Spell/Prayer commands
3  * @date 2014/01/27
4  * @author
5  * <pre>
6  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
7  * This software may be copied and distributed for educational, research,
8  * and not for profit purposes provided that this copyright and statement
9  * are included in all such copies.  Other copyrights may also apply.
10  * 2014 Deskull rearranged comment for Doxygen.\n
11  * </pre>
12  * @details
13  * <pre>
14  * This file includes code for eating food, drinking potions,
15  * reading scrolls, aiming wands, using staffs, zapping rods,
16  * and activating artifacts.
17  *
18  * In all cases, if the player becomes "aware" of the item's use
19  * by testing it, mark it as "aware" and reward some experience
20  * based on the object's level, always rounding up.  If the player
21  * remains "unaware", mark that object "kind" as "tried".
22  *
23  * This code now correctly handles the unstacking of wands, staffs,
24  * and rods.  Note the overly paranoid warning about potential pack
25  * overflow, which allows the player to use and drop a stacked item.
26  *
27  * In all "unstacking" scenarios, the "used" object is "carried" as if
28  * the player had just picked it up.  In particular, this means that if
29  * the use of an item induces pack overflow, that item will be dropped.
30  *
31  * For simplicity, these routines induce a full "pack reorganization"
32  * which not only combines similar items, but also reorganizes various
33  * items to obey the current "sorting" method.  This may require about
34  * 400 item comparisons, but only occasionally.
35  *
36  * There may be a BIG problem with any "effect" that can cause "changes"
37  * to the inventory.  For example, a "scroll of recharging" can cause
38  * a wand/staff to "disappear", moving the inventory up.  Luckily, the
39  * scrolls all appear BEFORE the staffs/wands, so this is not a problem.
40  * But, for example, a "staff of recharging" could cause MAJOR problems.
41  * In such a case, it will be best to either (1) "postpone" the effect
42  * until the end of the function, or (2) "change" the effect, say, into
43  * giving a staff "negative" charges, or "turning a staff into a stick".
44  * It seems as though a "rod of recharging" might in fact cause problems.
45  * The basic problem is that the act of recharging (and destroying) an
46  * item causes the inducer of that action to "move", causing "o_ptr" to
47  * no longer point at the correct item, with horrifying results.
48  *
49  * Note that food/potions/scrolls no longer use bit-flags for effects,
50  * but instead use the "sval" (which is also used to sort the objects).
51  * </pre>
52  */
53
54 #include "cmd-item/cmd-magiceat.h"
55 #include "action/action-limited.h"
56 #include "cmd-item/cmd-usestaff.h"
57 #include "cmd-item/cmd-zaprod.h"
58 #include "cmd-item/cmd-zapwand.h"
59 #include "core/asking-player.h"
60 #include "game-option/disturbance-options.h"
61 #include "game-option/text-display-options.h"
62 #include "io/command-repeater.h"
63 #include "io/input-key-acceptor.h"
64 #include "io/input-key-requester.h"
65 #include "main/sound-definitions-table.h"
66 #include "main/sound-of-music.h"
67 #include "object/object-kind-hook.h"
68 #include "object/object-kind.h"
69 #include "player-info/avatar.h"
70 #include "player-status/player-energy.h"
71 #include "player/player-class.h"
72 #include "player/player-status-table.h"
73 #include "spell/spell-info.h"
74 #include "sv-definition/sv-other-types.h"
75 #include "sv-definition/sv-rod-types.h"
76 #include "system/player-type-definition.h"
77 #include "target/target-getter.h"
78 #include "term/screen-processor.h"
79 #include "term/term-color-types.h"
80 #include "util/buffer-shaper.h"
81 #include "util/int-char-converter.h"
82 #include "view/display-messages.h"
83
84 /*!
85  * @brief 魔道具術師の取り込んだ魔力一覧から選択/閲覧する /
86  * @param only_browse 閲覧するだけならばTRUE
87  * @return 選択した魔力のID、キャンセルならば-1を返す
88  */
89 static OBJECT_SUBTYPE_VALUE select_magic_eater(player_type *creature_ptr, bool only_browse)
90 {
91     OBJECT_SUBTYPE_VALUE ext = 0;
92     char choice;
93     bool flag, request_list;
94     tval_type tval = TV_NONE;
95     int ask = TRUE;
96     OBJECT_SUBTYPE_VALUE i = 0;
97     char out_val[160];
98
99     int menu_line = (use_menu ? 1 : 0);
100
101     COMMAND_CODE sn;
102     if (repeat_pull(&sn)) {
103         /* Verify the spell */
104         if (sn >= EATER_EXT * 2
105             && !(creature_ptr->magic_num1[sn] > k_info[lookup_kind(TV_ROD, sn - EATER_EXT * 2)].pval * (creature_ptr->magic_num2[sn] - 1) * EATER_ROD_CHARGE))
106             return sn;
107         else if (sn < EATER_EXT * 2 && !(creature_ptr->magic_num1[sn] < EATER_CHARGE))
108             return sn;
109     }
110
111     for (i = 0; i < MAX_SPELLS; i++) {
112         if (creature_ptr->magic_num2[i])
113             break;
114     }
115
116     if (i == MAX_SPELLS) {
117         msg_print(_("魔法を覚えていない!", "You don't have any magic!"));
118         return -1;
119     }
120
121     if (use_menu) {
122         screen_save();
123
124         while (!tval) {
125 #ifdef JP
126             prt(format(" %s 杖", (menu_line == 1) ? "》" : "  "), 2, 14);
127             prt(format(" %s 魔法棒", (menu_line == 2) ? "》" : "  "), 3, 14);
128             prt(format(" %s ロッド", (menu_line == 3) ? "》" : "  "), 4, 14);
129 #else
130             prt(format(" %s staff", (menu_line == 1) ? "> " : "  "), 2, 14);
131             prt(format(" %s wand", (menu_line == 2) ? "> " : "  "), 3, 14);
132             prt(format(" %s rod", (menu_line == 3) ? "> " : "  "), 4, 14);
133 #endif
134
135             if (only_browse)
136                 prt(_("どの種類の魔法を見ますか?", "Which type of magic do you browse?"), 0, 0);
137             else
138                 prt(_("どの種類の魔法を使いますか?", "Which type of magic do you use?"), 0, 0);
139
140             choice = inkey();
141             switch (choice) {
142             case ESCAPE:
143             case 'z':
144             case 'Z':
145                 screen_load();
146                 return -1;
147             case '2':
148             case 'j':
149             case 'J':
150                 menu_line++;
151                 break;
152             case '8':
153             case 'k':
154             case 'K':
155                 menu_line += 2;
156                 break;
157             case '\r':
158             case 'x':
159             case 'X':
160                 ext = (menu_line - 1) * EATER_EXT;
161                 if (menu_line == 1)
162                     tval = TV_STAFF;
163                 else if (menu_line == 2)
164                     tval = TV_WAND;
165                 else
166                     tval = TV_ROD;
167                 break;
168             }
169             if (menu_line > 3)
170                 menu_line -= 3;
171         }
172         screen_load();
173     } else {
174         while (TRUE) {
175             if (!get_com(_("[A] 杖, [B] 魔法棒, [C] ロッド:", "[A] staff, [B] wand, [C] rod:"), &choice, TRUE)) {
176                 return -1;
177             }
178             if (choice == 'A' || choice == 'a') {
179                 ext = 0;
180                 tval = TV_STAFF;
181                 break;
182             }
183             if (choice == 'B' || choice == 'b') {
184                 ext = EATER_EXT;
185                 tval = TV_WAND;
186                 break;
187             }
188             if (choice == 'C' || choice == 'c') {
189                 ext = EATER_EXT * 2;
190                 tval = TV_ROD;
191                 break;
192             }
193         }
194     }
195     for (i = ext; i < ext + EATER_EXT; i++) {
196         if (creature_ptr->magic_num2[i]) {
197             if (use_menu)
198                 menu_line = i - ext + 1;
199             break;
200         }
201     }
202     if (i == ext + EATER_EXT) {
203         msg_print(_("その種類の魔法は覚えていない!", "You don't have that type of magic!"));
204         return -1;
205     }
206
207     /* Nothing chosen yet */
208     flag = FALSE;
209
210     if (only_browse)
211         strnfmt(out_val, 78, _("('*'で一覧, ESCで中断) どの魔力を見ますか?", "(*=List, ESC=exit) Browse which power? "));
212     else
213         strnfmt(out_val, 78, _("('*'で一覧, ESCで中断) どの魔力を使いますか?", "(*=List, ESC=exit) Use which power? "));
214     screen_save();
215
216     request_list = always_show_list;
217
218     while (!flag) {
219         /* Show the list */
220         if (request_list || use_menu) {
221             byte y, x = 0;
222             OBJECT_SUBTYPE_VALUE ctr;
223             PERCENTAGE chance;
224             KIND_OBJECT_IDX k_idx;
225             char dummy[80];
226             POSITION x1, y1;
227             DEPTH level;
228             byte col;
229
230             strcpy(dummy, "");
231
232             for (y = 1; y < 20; y++)
233                 prt("", y, x);
234
235             y = 1;
236
237             /* Print header(s) */
238 #ifdef JP
239             prt(format("                           %s 失率                           %s 失率", (tval == TV_ROD ? "  状態  " : "使用回数"),
240                     (tval == TV_ROD ? "  状態  " : "使用回数")),
241                 y++, x);
242 #else
243             prt(format("                           %s Fail                           %s Fail", (tval == TV_ROD ? "  Stat  " : " Charges"),
244                     (tval == TV_ROD ? "  Stat  " : " Charges")),
245                 y++, x);
246 #endif
247
248             /* Print list */
249             for (ctr = 0; ctr < EATER_EXT; ctr++) {
250                 if (!creature_ptr->magic_num2[ctr + ext])
251                     continue;
252
253                 k_idx = lookup_kind(tval, ctr);
254
255                 if (use_menu) {
256                     if (ctr == (menu_line - 1))
257                         strcpy(dummy, _("》", "> "));
258                     else
259                         strcpy(dummy, "  ");
260                 }
261                 /* letter/number for power selection */
262                 else {
263                     char letter;
264                     if (ctr < 26)
265                         letter = I2A(ctr);
266                     else
267                         letter = '0' + ctr - 26;
268                     sprintf(dummy, "%c)", letter);
269                 }
270                 x1 = ((ctr < EATER_EXT / 2) ? x : x + 40);
271                 y1 = ((ctr < EATER_EXT / 2) ? y + ctr : y + ctr - EATER_EXT / 2);
272                 level = (tval == TV_ROD ? k_info[k_idx].level * 5 / 6 - 5 : k_info[k_idx].level);
273                 chance = level * 4 / 5 + 20;
274                 chance -= 3 * (adj_mag_stat[creature_ptr->stat_index[mp_ptr->spell_stat]] - 1);
275                 level /= 2;
276                 if (creature_ptr->lev > level) {
277                     chance -= 3 * (creature_ptr->lev - level);
278                 }
279                 chance = mod_spell_chance_1(creature_ptr, chance);
280                 chance = MAX(chance, adj_mag_fail[creature_ptr->stat_index[mp_ptr->spell_stat]]);
281                 /* Stunning makes spells harder */
282                 if (creature_ptr->stun > 50)
283                     chance += 25;
284                 else if (creature_ptr->stun)
285                     chance += 15;
286
287                 if (chance > 95)
288                     chance = 95;
289
290                 chance = mod_spell_chance_2(creature_ptr, chance);
291
292                 col = TERM_WHITE;
293
294                 if (k_idx) {
295                     if (tval == TV_ROD) {
296                         strcat(dummy,
297                             format(_(" %-22.22s 充填:%2d/%2d%3d%%", " %-22.22s   (%2d/%2d) %3d%%"), k_info[k_idx].name.c_str(),
298                                 creature_ptr->magic_num1[ctr + ext] ? (creature_ptr->magic_num1[ctr + ext] - 1) / (EATER_ROD_CHARGE * k_info[k_idx].pval) + 1
299                                                                     : 0,
300                                 creature_ptr->magic_num2[ctr + ext], chance));
301                         if (creature_ptr->magic_num1[ctr + ext] > k_info[k_idx].pval * (creature_ptr->magic_num2[ctr + ext] - 1) * EATER_ROD_CHARGE)
302                             col = TERM_RED;
303                     } else {
304                         strcat(dummy,
305                             format(" %-22.22s    %2d/%2d %3d%%", k_info[k_idx].name.c_str(), (s16b)(creature_ptr->magic_num1[ctr + ext] / EATER_CHARGE),
306                                 creature_ptr->magic_num2[ctr + ext], chance));
307                         if (creature_ptr->magic_num1[ctr + ext] < EATER_CHARGE)
308                             col = TERM_RED;
309                     }
310                 } else
311                     strcpy(dummy, "");
312                 c_prt(col, dummy, y1, x1);
313             }
314         }
315
316         if (!get_com(out_val, &choice, FALSE))
317             break;
318
319         if (use_menu && choice != ' ') {
320             switch (choice) {
321             case '0': {
322                 screen_load();
323                 return 0;
324             }
325
326             case '8':
327             case 'k':
328             case 'K': {
329                 do {
330                     menu_line += EATER_EXT - 1;
331                     if (menu_line > EATER_EXT)
332                         menu_line -= EATER_EXT;
333                 } while (!creature_ptr->magic_num2[menu_line + ext - 1]);
334                 break;
335             }
336
337             case '2':
338             case 'j':
339             case 'J': {
340                 do {
341                     menu_line++;
342                     if (menu_line > EATER_EXT)
343                         menu_line -= EATER_EXT;
344                 } while (!creature_ptr->magic_num2[menu_line + ext - 1]);
345                 break;
346             }
347
348             case '4':
349             case 'h':
350             case 'H':
351             case '6':
352             case 'l':
353             case 'L': {
354                 bool reverse = FALSE;
355                 if ((choice == '4') || (choice == 'h') || (choice == 'H'))
356                     reverse = TRUE;
357                 if (menu_line > EATER_EXT / 2) {
358                     menu_line -= EATER_EXT / 2;
359                     reverse = TRUE;
360                 } else
361                     menu_line += EATER_EXT / 2;
362                 while (!creature_ptr->magic_num2[menu_line + ext - 1]) {
363                     if (reverse) {
364                         menu_line--;
365                         if (menu_line < 2)
366                             reverse = FALSE;
367                     } else {
368                         menu_line++;
369                         if (menu_line > EATER_EXT - 1)
370                             reverse = TRUE;
371                     }
372                 }
373                 break;
374             }
375
376             case 'x':
377             case 'X':
378             case '\r': {
379                 i = menu_line - 1;
380                 ask = FALSE;
381                 break;
382             }
383             }
384         }
385
386         /* Request redraw */
387         if (use_menu && ask)
388             continue;
389
390         /* Request redraw */
391         if (!use_menu && ((choice == ' ') || (choice == '*') || (choice == '?'))) {
392             /* Hide the list */
393             if (request_list) {
394                 /* Hide list */
395                 request_list = FALSE;
396                 screen_load();
397                 screen_save();
398             } else
399                 request_list = TRUE;
400
401             /* Redo asking */
402             continue;
403         }
404
405         if (!use_menu) {
406             if (isalpha(choice)) {
407                 /* Note verify */
408                 ask = (isupper(choice));
409
410                 /* Lowercase */
411                 if (ask)
412                     choice = (char)tolower(choice);
413
414                 /* Extract request */
415                 i = (islower(choice) ? A2I(choice) : -1);
416             } else {
417                 ask = FALSE; /* Can't uppercase digits */
418
419                 i = choice - '0' + 26;
420             }
421         }
422
423         /* Totally Illegal */
424         if ((i < 0) || (i > EATER_EXT) || !creature_ptr->magic_num2[i + ext]) {
425             bell();
426             continue;
427         }
428
429         if (!only_browse) {
430             /* Verify it */
431             if (ask) {
432                 char tmp_val[160];
433
434                 /* Prompt */
435                 (void)strnfmt(tmp_val, 78, _("%sを使いますか? ", "Use %s? "), k_info[lookup_kind(tval, i)].name.c_str());
436
437                 /* Belay that order */
438                 if (!get_check(tmp_val))
439                     continue;
440             }
441             if (tval == TV_ROD) {
442                 if (creature_ptr->magic_num1[ext + i] > k_info[lookup_kind(tval, i)].pval * (creature_ptr->magic_num2[ext + i] - 1) * EATER_ROD_CHARGE) {
443                     msg_print(_("その魔法はまだ充填している最中だ。", "The magic is still charging."));
444                     msg_print(NULL);
445                     if (use_menu)
446                         ask = TRUE;
447                     continue;
448                 }
449             } else {
450                 if (creature_ptr->magic_num1[ext + i] < EATER_CHARGE) {
451                     msg_print(_("その魔法は使用回数が切れている。", "The magic has no charges left."));
452                     msg_print(NULL);
453                     if (use_menu)
454                         ask = TRUE;
455                     continue;
456                 }
457             }
458         }
459
460         /* Browse */
461         else {
462             int line, j;
463             char temp[70 * 20];
464
465             /* Clear lines, position cursor  (really should use strlen here) */
466             term_erase(7, 23, 255);
467             term_erase(7, 22, 255);
468             term_erase(7, 21, 255);
469             term_erase(7, 20, 255);
470
471             shape_buffer(k_info[lookup_kind(tval, i)].text.c_str(), 62, temp, sizeof(temp));
472             for (j = 0, line = 21; temp[j]; j += 1 + strlen(&temp[j])) {
473                 prt(&temp[j], line, 10);
474                 line++;
475             }
476
477             continue;
478         }
479
480         /* Stop the loop */
481         flag = TRUE;
482     }
483     screen_load();
484
485     if (!flag)
486         return -1;
487
488     repeat_push(ext + i);
489     return ext + i;
490 }
491
492 /*!
493  * @brief 取り込んだ魔力を利用するコマンドのメインルーチン /
494  * Use eaten rod, wand or staff
495  * @param only_browse 閲覧するだけならばTRUE
496  * @param powerful 強力発動中の処理ならばTRUE
497  * @return 実際にコマンドを実行したならばTRUEを返す。
498  */
499 bool do_cmd_magic_eater(player_type *creature_ptr, bool only_browse, bool powerful)
500 {
501     tval_type tval;
502     OBJECT_SUBTYPE_VALUE sval;
503     bool use_charge = TRUE;
504
505     if (cmd_limit_confused(creature_ptr))
506         return FALSE;
507
508     auto item = select_magic_eater(creature_ptr, only_browse);
509     PlayerEnergy energy(creature_ptr);
510     if (item == -1) {
511         energy.reset_player_turn();
512         return FALSE;
513     }
514     if (item >= EATER_EXT * 2) {
515         tval = TV_ROD;
516         sval = item - EATER_EXT * 2;
517     } else if (item >= EATER_EXT) {
518         tval = TV_WAND;
519         sval = item - EATER_EXT;
520     } else {
521         tval = TV_STAFF;
522         sval = item;
523     }
524     
525     auto k_idx = lookup_kind(tval, sval);
526     auto level = (tval == TV_ROD ? k_info[k_idx].level * 5 / 6 - 5 : k_info[k_idx].level);
527     auto chance = level * 4 / 5 + 20;
528     chance -= 3 * (adj_mag_stat[creature_ptr->stat_index[mp_ptr->spell_stat]] - 1);
529     level /= 2;
530     if (creature_ptr->lev > level) {
531         chance -= 3 * (creature_ptr->lev - level);
532     }
533     chance = mod_spell_chance_1(creature_ptr, chance);
534     chance = MAX(chance, adj_mag_fail[creature_ptr->stat_index[mp_ptr->spell_stat]]);
535     /* Stunning makes spells harder */
536     if (creature_ptr->stun > 50)
537         chance += 25;
538     else if (creature_ptr->stun)
539         chance += 15;
540
541     if (chance > 95)
542         chance = 95;
543
544     chance = mod_spell_chance_2(creature_ptr, chance);
545
546     if (randint0(100) < chance) {
547         if (flush_failure)
548             flush();
549
550         msg_print(_("呪文をうまく唱えられなかった!", "You failed to get the magic off!"));
551         sound(SOUND_FAIL);
552         if (randint1(100) >= chance)
553             chg_virtue(creature_ptr, V_CHANCE, -1);
554         energy.set_player_turn_energy(100);
555
556         return TRUE;
557     } else {
558         DIRECTION dir = 0;
559
560         if (tval == TV_ROD) {
561             if ((sval >= SV_ROD_MIN_DIRECTION) && (sval != SV_ROD_HAVOC) && (sval != SV_ROD_AGGRAVATE) && (sval != SV_ROD_PESTICIDE))
562                 if (!get_aim_dir(creature_ptr, &dir))
563                     return FALSE;
564             rod_effect(creature_ptr, sval, dir, &use_charge, powerful, TRUE);
565             if (!use_charge)
566                 return FALSE;
567         } else if (tval == TV_WAND) {
568             if (!get_aim_dir(creature_ptr, &dir))
569                 return FALSE;
570             wand_effect(creature_ptr, sval, dir, powerful, TRUE);
571         } else {
572             staff_effect(creature_ptr, sval, &use_charge, powerful, TRUE, TRUE);
573             if (!use_charge)
574                 return FALSE;
575         }
576         if (randint1(100) < chance)
577             chg_virtue(creature_ptr, V_CHANCE, 1);
578     }
579
580     energy.set_player_turn_energy(100);
581     if (tval == TV_ROD)
582         creature_ptr->magic_num1[item] += k_info[k_idx].pval * EATER_ROD_CHARGE;
583     else
584         creature_ptr->magic_num1[item] -= EATER_CHARGE;
585
586     return TRUE;
587 }