OSDN Git Service

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