OSDN Git Service

[Refactor] #963 Separated take_turn() and free_turn() from player-status.cpp/h to...
[hengbandforosx/hengbandosx.git] / src / mind / mind-sniper.cpp
1 /*!
2  * @brief スナイパー技能の実装 / Sniping
3  * @date 2014/01/18
4  * @author
5  * 2014 Deskull rearranged comment for Doxygen.\n
6  */
7
8 #include "mind/mind-sniper.h"
9 #include "action/action-limited.h"
10 #include "cmd-action/cmd-shoot.h"
11 #include "core/asking-player.h"
12 #include "core/player-redraw-types.h"
13 #include "core/player-update-types.h"
14 #include "core/stuff-handler.h"
15 #include "core/window-redrawer.h"
16 #include "floor/geometry.h"
17 #include "game-option/text-display-options.h"
18 #include "grid/grid.h"
19 #include "inventory/inventory-slot-types.h"
20 #include "io/command-repeater.h"
21 #include "io/input-key-requester.h"
22 #include "main/sound-definitions-table.h"
23 #include "main/sound-of-music.h"
24 #include "mind/snipe-types.h"
25 #include "monster-race/race-flags-resistance.h"
26 #include "monster-race/race-flags3.h"
27 #include "monster-race/monster-race.h"
28 #include "player-status/player-energy.h"
29 #include "system/monster-race-definition.h"
30 #include "system/monster-type-definition.h"
31 #include "system/object-type-definition.h"
32 #include "system/player-type-definition.h"
33 #include "term/screen-processor.h"
34 #include "term/term-color-types.h"
35 #include "util/buffer-shaper.h"
36 #include "util/int-char-converter.h"
37 #include "view/display-messages.h"
38
39 #define MAX_SNIPE_POWERS 16
40
41 /*! スナイパー技能情報の構造体 */
42 typedef struct snipe_power
43 {
44         PLAYER_LEVEL min_lev;
45         MANA_POINT mana_cost;
46         concptr name;
47 } snipe_power;
48
49 /*! スナイパー技能の解説メッセージ */
50 static concptr const snipe_tips[MAX_SNIPE_POWERS] =
51 {
52 #ifdef JP
53         "精神を集中する。射撃の威力、精度が上がり、高度な射撃術が使用できるようになる。",
54         "光る矢を放つ。光に弱いモンスターに威力を発揮する。",
55         "射撃を行った後、短距離の瞬間移動を行う。",
56         "軌道上の罠をすべて無効にする低空飛行の矢を放つ。",
57         "火炎属性の矢を放つ。",
58         "壁を粉砕する矢を放つ。岩でできたモンスターと無生物のモンスターに威力を発揮する。",
59         "冷気属性の矢を放つ。",
60         "敵を突き飛ばす矢を放つ。",
61         "複数の敵を貫通する矢を放つ。",
62         "善良なモンスターに威力を発揮する矢を放つ。",
63         "邪悪なモンスターに威力を発揮する矢を放つ。",
64         "当たると爆発する矢を放つ。",
65         "2回射撃を行う。",
66         "電撃属性の矢を放つ。",
67         "敵の急所にめがけて矢を放つ。成功すると敵を一撃死させる。失敗すると1ダメージ。",
68         "全てのモンスターに高威力を発揮する矢を放つ。反動による副次効果を受ける。",
69 #else
70         "Concentrate your mind for shooting.",
71         "Shoot a glowing arrow effective against light-sensitive foes.",
72         "Blink after shooting.",
73         "Shoot an arrow able to shatter traps.",
74         "Deals extra damage of fire.",
75         "Shoot an arrow able to shatter rocks.",
76         "Deals extra damage of ice.",
77         "Shoot an arrow that pushes away the target.",
78         "Shoot an arrow that does not always stop at the first target on its path."
79         "Deals more damage to good monsters.",
80         "Deals more damage to evil monsters.",
81         "Shoot an arrow that explodes when it hits a monster.",
82         "Shoot two arrows at once.",
83         "Deals extra damage of lightning.",
84         "Deals quick death or 1 damage.",
85         "Deals great damage to all monsters, and some side effects to you.",
86 #endif
87 };
88
89 /*! スナイパー技能テーブル */
90 static snipe_power const snipe_powers[MAX_SNIPE_POWERS] =
91 {
92         /* Level gained,  cost,  name */
93 #ifdef JP
94         {  1,  0,  "精神集中" },
95         {  2,  1,  "フラッシュアロー" },
96         {  3,  1,  "シュート&アウェイ" },
97         {  5,  1,  "解除の矢" },
98         {  8,  2,  "火炎の矢" },
99         { 10,  2,  "岩砕き" },
100         { 13,  2,  "冷気の矢" },
101         { 18,  2,  "烈風弾"},
102         { 22,  3,  "貫通弾" },
103         { 25,  4,  "邪念弾"},
104         { 26,  4,  "破魔矢" },
105         { 30,  3,  "爆発の矢"},
106         { 32,  4,  "ダブルショット" },
107         { 36,  3,  "プラズマボルト" },
108         { 40,  3,  "ニードルショット" },
109         { 48,  7,  "セイントスターアロー" },
110 #else
111         {  1,  0,  "Concentration" },
112         {  2,  1,  "Flash Arrow" },
113         {  3,  1,  "Shoot & Away" },
114         {  5,  1,  "Disarm Shot" },
115         {  8,  2,  "Fire Shot" },
116         { 10,  2,  "Shatter Arrow" },
117         { 13,  2,  "Ice Shot" },
118         { 18,  2,  "Rushing Arrow"},
119         { 22,  3,  "Piercing Shot" },
120         { 25,  4,  "Evil Shot"},
121         { 26,  4,  "Holy Shot" },
122         { 30,  3,  "Missile"},
123         { 32,  4,  "Double Shot" },
124         { 36,  3,  "Plasma Bolt" },
125         { 40,  3,  "Needle Shot" },
126         { 48,  7,  "Saint Stars Arrow" },
127 #endif
128 };
129
130 /*! 
131  * @brief スナイパーの集中度加算
132  * @return 常にTRUEを返す
133  */
134 static bool snipe_concentrate(player_type *creature_ptr)
135 {
136         if ((int)creature_ptr->concent < (2 + (creature_ptr->lev + 5) / 10)) creature_ptr->concent++;
137
138         msg_format(_("集中した。(集中度 %d)", "You concentrate deeply. (lvl %d)"), creature_ptr->concent);
139         creature_ptr->reset_concent = FALSE;
140
141         creature_ptr->update |= (PU_BONUS | PU_MONSTERS);
142         creature_ptr->redraw |= (PR_STATUS);
143         return TRUE;
144 }
145
146 /*! 
147  * @brief スナイパーの集中度リセット
148  * @param msg TRUEならばメッセージを表示する
149  * @return なし
150  */
151 void reset_concentration(player_type *creature_ptr, bool msg)
152 {
153         if (msg)
154         {
155                 msg_print(_("集中力が途切れてしまった。", "Stop concentrating."));
156         }
157
158         creature_ptr->concent = 0;
159         creature_ptr->reset_concent = FALSE;
160
161         creature_ptr->update |= (PU_BONUS | PU_MONSTERS);
162         creature_ptr->redraw |= (PR_STATUS);
163 }
164
165 /*! 
166  * @brief スナイパーの集中度によるダメージボーナスを加算する
167  * @param tdam 算出中のダメージ
168  * @return 集中度修正を加えたダメージ
169  */
170 int boost_concentration_damage(player_type *creature_ptr, int tdam)
171 {
172         tdam *= (10 + creature_ptr->concent);
173         tdam /= 10;
174
175         return (tdam);
176 }
177
178 /*! 
179  * @brief スナイパーの技能リストを表示する
180  * @return なし
181  */
182 void display_snipe_list(player_type *sniper_ptr)
183 {
184         int i;
185         TERM_LEN y = 1;
186         TERM_LEN x = 1;
187         PLAYER_LEVEL plev = sniper_ptr->lev;
188         snipe_power spell;
189         char psi_desc[80];
190
191         /* Display a list of spells */
192         prt("", y, x);
193         put_str(_("名前", "Name"), y, x + 5);
194         put_str(_("Lv   MP", "Lv Mana"), y, x + 35);
195
196         for (i = 0; i < MAX_SNIPE_POWERS; i++)
197         {
198                 /* Access the available spell */
199                 spell = snipe_powers[i];
200                 if (spell.min_lev > plev) continue;
201
202                 sprintf(psi_desc, "  %c) %-30s%2d %4d",
203                         I2A(i), spell.name, spell.min_lev, spell.mana_cost);
204
205                 if (spell.mana_cost > (int)sniper_ptr->concent)
206                         term_putstr(x, y + i + 1, -1, TERM_SLATE, psi_desc);
207                 else
208                         term_putstr(x, y + i + 1, -1, TERM_WHITE, psi_desc);
209         }
210         return;
211 }
212
213
214 /*! 
215  * @brief スナイパー技能を選択する
216  * @param sn 選択した特殊技能ID、キャンセルの場合-1、不正な選択の場合-2を返す
217  * @param only_browse 一覧を見るだけの場合TRUEを返す
218  * @return 発動可能な魔法を選択した場合TRUE、キャンセル処理か不正な選択が行われた場合FALSEを返す。
219  * Allow user to choose a mindcrafter power.\n
220  *\n
221  * If a valid spell is chosen, saves it in '*sn' and returns TRUE\n
222  * If the user hits escape, returns FALSE, and set '*sn' to -1\n
223  * If there are no legal choices, returns FALSE, and sets '*sn' to -2\n
224  *\n
225  * The "prompt" should be "cast", "recite", or "study"\n
226  * The "known" should be TRUE for cast/pray, FALSE for study\n
227  *\n
228  * nb: This function has a (trivial) display bug which will be obvious\n
229  * when you run it. It's probably easy to fix but I haven't tried,\n
230  * sorry.\n
231  */
232 static int get_snipe_power(player_type *sniper_ptr, COMMAND_CODE *sn, bool only_browse)
233 {
234         COMMAND_CODE i;
235         int num = 0;
236         TERM_LEN y = 1;
237         TERM_LEN x = 20;
238         PLAYER_LEVEL plev = sniper_ptr->lev;
239         int ask;
240         char choice;
241         char out_val[160];
242         concptr p = _("射撃術", "power");
243         snipe_power spell;
244         bool flag, redraw;
245
246         repeat_push(*sn);
247
248         /* Assume cancelled */
249         *sn = (-1);
250
251         /* Repeat previous command */
252         /* Get the spell, if available */
253         if (repeat_pull(sn))
254         {
255                 /* Verify the spell */
256                 if ((snipe_powers[*sn].min_lev <= plev) && (snipe_powers[*sn].mana_cost <= (int)sniper_ptr->concent))
257                 {
258                         /* Success */
259                         return TRUE;
260                 }
261         }
262
263         flag = FALSE;
264         redraw = FALSE;
265
266         for (i = 0; i < MAX_SNIPE_POWERS; i++)
267         {
268                 if ((snipe_powers[i].min_lev <= plev) &&
269                         ((only_browse) || (snipe_powers[i].mana_cost <= (int)sniper_ptr->concent)))
270                 {
271                         num = i;
272                 }
273         }
274
275         /* Build a prompt (accept all spells) */
276         if (only_browse)
277         {
278                 (void)strnfmt(out_val, 78, 
279                                         _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
280                                         p, I2A(0), I2A(num), p);
281         }
282         else
283         {
284                 (void)strnfmt(out_val, 78, 
285                                         _("(%^s %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
286                                         p, I2A(0), I2A(num), p);
287         }
288
289         choice = always_show_list ? ESCAPE : 1;
290         while (!flag)
291         {
292                 if(choice == ESCAPE) choice = ' ';
293                 else if( !get_com(out_val, &choice, FALSE) )break; 
294
295                 /* Request redraw */
296                 if ((choice == ' ') || (choice == '*') || (choice == '?'))
297                 {
298                         /* Show the list */
299                         if (!redraw)
300                         {
301                                 char psi_index[6];
302                                 char psi_desc[75];
303                                 redraw = TRUE;
304                                 if (!only_browse) screen_save();
305
306                                 /* Display a list of spells */
307                                 prt("", y, x);
308                                 put_str(_("名前", "Name"), y, x + 5);
309                                 put_str(_("Lv   集中度", "Lv Pow"), y, x + 35);
310
311                                 /* Dump the spells */
312                                 for (i = 0; i < MAX_SNIPE_POWERS; i++)
313                                 {
314                                         term_color_type tcol = TERM_WHITE;
315                                         term_erase(x, y + i + 1, 255);
316
317                                         /* Access the spell */
318                                         spell = snipe_powers[i];
319
320                                         /* Dump the spell --(-- */
321                                         if (spell.min_lev > plev)
322                                                 sprintf(psi_index, "   ) ");
323                                         else
324                                                 sprintf(psi_index, "  %c) ", I2A(i));
325
326                                         sprintf(psi_desc, "%-30s%2d %4d",
327                                                 spell.name,     spell.min_lev, spell.mana_cost);
328
329                                         if (spell.min_lev > plev)
330                                                 tcol = TERM_SLATE;
331                                         else if (spell.mana_cost > (int)sniper_ptr->concent)
332                                                 tcol = TERM_L_BLUE;
333
334                                         term_putstr(x, y + i + 1, -1, tcol, psi_index);
335                                         term_putstr(x + 5, y + i + 1, -1, tcol, psi_desc);
336                                 }
337
338                                 /* Clear the bottom line */
339                                 prt("", y + i + 1, x);
340                         }
341
342                         /* Hide the list */
343                         else
344                         {
345                                 /* Hide list */
346                                 redraw = FALSE;
347                                 if (!only_browse) screen_load();
348                         }
349
350                         /* Redo asking */
351                         continue;
352                 }
353
354                 /* Note verify */
355                 ask = isupper(choice);
356
357                 /* Lowercase */
358                 if (ask) choice = (char)tolower(choice);
359
360                 /* Extract request */
361                 i = (islower(choice) ? A2I(choice) : -1);
362
363                 /* Totally Illegal */
364                 if ((i < 0) || (i > num) || 
365                         (!only_browse &&(snipe_powers[i].mana_cost > (int)sniper_ptr->concent)))
366                 {
367                         bell();
368                         continue;
369                 }
370
371                 /* Save the spell index */
372                 spell = snipe_powers[i];
373
374                 /* Verify it */
375                 if (ask)
376                 {
377                         char tmp_val[160];
378
379                         /* Prompt */
380                         (void) strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), snipe_powers[i].name);
381
382                         /* Belay that order */
383                         if (!get_check(tmp_val)) continue;
384                 }
385
386                 /* Stop the loop */
387                 flag = TRUE;
388         }
389         if (redraw && !only_browse) screen_load();
390
391         sniper_ptr->window_flags |= (PW_SPELL);
392         handle_stuff(sniper_ptr);
393
394         /* Abort if needed */
395         if (!flag) return FALSE;
396
397         /* Save the choice */
398         (*sn) = i;
399
400         repeat_push(*sn);
401
402         /* Success */
403         return TRUE;
404 }
405
406
407 /*!
408  * @brief スナイバー技能のスレイ倍率計算を行う /
409  * Calcurate magnification of snipe technics
410  * @param mult スナイバー技能のスレイ効果以前に算出している多要素の倍率(/10倍)
411  * @param m_ptr 目標となるモンスターの構造体参照ポインタ
412  * @return スレイの倍率(/10倍)
413  */
414 MULTIPLY calc_snipe_damage_with_slay(player_type *sniper_ptr, MULTIPLY mult, monster_type *m_ptr, SPELL_IDX snipe_type)
415 {
416         monster_race *r_ptr = &r_info[m_ptr->r_idx];
417         bool seen = is_seen(sniper_ptr, m_ptr);
418
419         switch (snipe_type)
420         {
421         case SP_LITE:
422                 if (r_ptr->flags3 & (RF3_HURT_LITE))
423                 {
424                         MULTIPLY n = 20 + sniper_ptr->concent;
425                         if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
426                         if (mult < n) mult = n;
427                 }
428                 break;
429         case SP_FIRE:
430                 if (r_ptr->flagsr & RFR_IM_FIRE)
431                 {
432                         if (seen) r_ptr->r_flagsr |= RFR_IM_FIRE;
433                 }
434                 else
435                 {
436                         MULTIPLY n = 15 + (sniper_ptr->concent * 3);
437                         if (mult < n) mult = n;
438                 }
439                 break;
440         case SP_COLD:
441                 if (r_ptr->flagsr & RFR_IM_COLD)
442                 {
443                         if (seen) r_ptr->r_flagsr |= RFR_IM_COLD;
444                 }
445                 else
446                 {
447                         MULTIPLY n = 15 + (sniper_ptr->concent * 3);
448                         if (mult < n) mult = n;
449                 }
450                 break;
451         case SP_ELEC:
452                 if (r_ptr->flagsr & RFR_IM_ELEC)
453                 {
454                         if (seen) r_ptr->r_flagsr |= RFR_IM_ELEC;
455                 }
456                 else
457                 {
458                         MULTIPLY n = 18 + (sniper_ptr->concent * 4);
459                         if (mult < n) mult = n;
460                 }
461                 break;
462         case SP_KILL_WALL:
463                 if (r_ptr->flags3 & RF3_HURT_ROCK)
464                 {
465                         MULTIPLY n = 15 + (sniper_ptr->concent * 2);
466                         if (seen) r_ptr->r_flags3 |= RF3_HURT_ROCK;
467                         if (mult < n) mult = n;
468                 }
469                 else if (r_ptr->flags3 & RF3_NONLIVING)
470                 {
471                         MULTIPLY n = 15 + (sniper_ptr->concent * 2);
472                         if (seen) r_ptr->r_flags3 |= RF3_NONLIVING;
473                         if (mult < n) mult = n;
474                 }
475                 break;
476         case SP_EVILNESS:
477                 if (r_ptr->flags3 & RF3_GOOD)
478                 {
479                         MULTIPLY n = 15 + (sniper_ptr->concent * 4);
480                         if (seen) r_ptr->r_flags3 |= RF3_GOOD;
481                         if (mult < n) mult = n;
482                 }
483                 break;
484         case SP_HOLYNESS:
485                 if (r_ptr->flags3 & RF3_EVIL)
486                 {
487                         MULTIPLY n = 12 + (sniper_ptr->concent * 3);
488                         if (seen) r_ptr->r_flags3 |= RF3_EVIL;
489                         if (r_ptr->flags3 & (RF3_HURT_LITE))
490                         {
491                                 n += (sniper_ptr->concent * 3);
492                                 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
493                         }
494                         if (mult < n) mult = n;
495                 }
496                 break;
497         case SP_FINAL:
498                 if (mult < 50) mult = 50;
499                 break;
500         }
501
502         return (mult);
503 }
504
505
506 /*!
507  * @brief スナイパー技能の発動 /
508  * do_cmd_cast calls this function if the player's class is 'snipe'.
509  * @param spell 発動する特殊技能のID
510  * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
511  */
512 static bool cast_sniper_spell(player_type *sniper_ptr, int spell)
513 {
514         object_type *o_ptr = &sniper_ptr->inventory_list[INVEN_BOW];
515         SPELL_IDX snipe_type = SP_NONE;
516
517         if (o_ptr->tval != TV_BOW)
518         {
519                 msg_print(_("弓を装備していない!", "You wield no bow!"));
520                 return FALSE;
521         }
522
523         /* spell code */
524         switch (spell)
525         {
526         case 0: /* Concentration */
527                 if (!snipe_concentrate(sniper_ptr)) return FALSE;
528                 take_turn(sniper_ptr, 100);
529                 return TRUE;
530         case 1: snipe_type = SP_LITE; break;
531         case 2: snipe_type = SP_AWAY; break;
532         case 3: snipe_type = SP_KILL_TRAP; break;
533         case 4: snipe_type = SP_FIRE; break;
534         case 5: snipe_type = SP_KILL_WALL; break;
535         case 6: snipe_type = SP_COLD; break;
536         case 7: snipe_type = SP_RUSH; break;
537         case 8: snipe_type = SP_PIERCE; break;
538         case 9: snipe_type = SP_EVILNESS; break;
539         case 10: snipe_type = SP_HOLYNESS; break;
540         case 11: snipe_type = SP_EXPLODE; break;
541         case 12: snipe_type = SP_DOUBLE; break;
542         case 13: snipe_type = SP_ELEC; break;
543         case 14: snipe_type = SP_NEEDLE; break;
544         case 15: snipe_type = SP_FINAL; break;
545         default:
546                 msg_print(_("なに?", "Zap?"));
547         }
548
549         command_cmd = 'f';
550         do_cmd_fire(sniper_ptr, snipe_type);
551
552         return (sniper_ptr->is_fired);
553 }
554
555 /*!
556  * @brief スナイパー技能コマンドのメインルーチン /
557  * @return なし
558  */
559 void do_cmd_snipe(player_type *sniper_ptr)
560 {
561         COMMAND_CODE n = 0;
562         bool cast;
563
564         if(cmd_limit_confused(sniper_ptr)) return;
565         if(cmd_limit_image(sniper_ptr)) return;
566         if(cmd_limit_stun(sniper_ptr)) return;
567
568         if (!get_snipe_power(sniper_ptr, &n, FALSE)) return;
569
570         sound(SOUND_SHOOT);
571         cast = cast_sniper_spell(sniper_ptr, n);
572
573         if (!cast) return;
574         sniper_ptr->redraw |= (PR_HP | PR_MANA);
575         sniper_ptr->window_flags |= (PW_PLAYER);
576         sniper_ptr->window_flags |= (PW_SPELL);
577 }
578
579 /*!
580  * @brief スナイパー技能コマンドの表示 /
581  * @return なし
582  */
583 void do_cmd_snipe_browse(player_type *sniper_ptr)
584 {
585         COMMAND_CODE n = 0;
586         int j, line;
587         char temp[62 * 4];
588
589         screen_save();
590
591         while (TRUE)
592         {
593                 if (!get_snipe_power(sniper_ptr, &n, TRUE))
594                 {
595                         screen_load();
596                         return;
597                 }
598
599                 /* Clear lines, position cursor  (really should use strlen here) */
600                 term_erase(12, 22, 255);
601                 term_erase(12, 21, 255);
602                 term_erase(12, 20, 255);
603                 term_erase(12, 19, 255);
604                 term_erase(12, 18, 255);
605
606                 shape_buffer(snipe_tips[n], 62, temp, sizeof(temp));
607                 for(j = 0, line = 19; temp[j]; j += (1 + strlen(&temp[j])))
608                 {
609                         prt(&temp[j], line, 15);
610                         line++;
611                 }
612         }
613 }