OSDN Git Service

[Refactor] #37353 snipe_type をグローバル変数から、引数へ置換 / Replace snipe_type from global to...
[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                                 redraw = TRUE;
288                                 if (!only_browse) screen_save();
289
290                                 /* Display a list of spells */
291                                 prt("", y, x);
292 #ifdef JP
293                                 put_str("名前", y, x + 5);
294                                 if (only_browse) put_str("Lv   集中度", y, x + 35);
295 #else
296                                 put_str("Name", y, x + 5);
297                                 if (only_browse) put_str("Lv Pow", y, x + 35);
298 #endif
299
300                                 /* Dump the spells */
301                                 for (i = 0; i < MAX_SNIPE_POWERS; i++)
302                                 {
303                                         Term_erase(x, y + i + 1, 255);
304
305                                         /* Access the spell */
306                                         spell = snipe_powers[i];
307                                         if (spell.min_lev > plev) continue;
308                                         if (!only_browse && (spell.mana_cost > (int)p_ptr->concent)) continue;
309
310                                         /* Dump the spell --(-- */
311                                         if (only_browse)
312                                                 sprintf(psi_desc, "  %c) %-30s%2d %4d",
313                                                         I2A(i), spell.name,     spell.min_lev, spell.mana_cost);
314                                         else
315                                                 sprintf(psi_desc, "  %c) %-30s", I2A(i), spell.name);
316                                         prt(psi_desc, y + i + 1, x);
317                                 }
318
319                                 /* Clear the bottom line */
320                                 prt("", y + i + 1, x);
321                         }
322
323                         /* Hide the list */
324                         else
325                         {
326                                 /* Hide list */
327                                 redraw = FALSE;
328                                 if (!only_browse) screen_load();
329                         }
330
331                         /* Redo asking */
332                         continue;
333                 }
334
335                 /* Note verify */
336                 ask = isupper(choice);
337
338                 /* Lowercase */
339                 if (ask) choice = (char)tolower(choice);
340
341                 /* Extract request */
342                 i = (islower(choice) ? A2I(choice) : -1);
343
344                 /* Totally Illegal */
345                 if ((i < 0) || (i > num) || 
346                         (!only_browse &&(snipe_powers[i].mana_cost > (int)p_ptr->concent)))
347                 {
348                         bell();
349                         continue;
350                 }
351
352                 /* Save the spell index */
353                 spell = snipe_powers[i];
354
355                 /* Verify it */
356                 if (ask)
357                 {
358                         char tmp_val[160];
359
360                         /* Prompt */
361                         (void) strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), snipe_powers[i].name);
362
363                         /* Belay that order */
364                         if (!get_check(tmp_val)) continue;
365                 }
366
367                 /* Stop the loop */
368                 flag = TRUE;
369         }
370         if (redraw && !only_browse) screen_load();
371
372         p_ptr->window |= (PW_SPELL);
373         handle_stuff();
374
375         /* Abort if needed */
376         if (!flag) return (FALSE);
377
378         /* Save the choice */
379         (*sn) = i;
380
381         repeat_push(*sn);
382
383         /* Success */
384         return (TRUE);
385 }
386
387 /*!
388  * @brief スナイバー技能のスレイ倍率計算を行う /
389  * Calcurate magnification of snipe technics
390  * @param mult スナイバー技能のスレイ効果以前に算出している多要素の倍率(/10倍)
391  * @param m_ptr 目標となるモンスターの構造体参照ポインタ
392  * @return スレイの倍率(/10倍)
393  */
394 MULTIPLY tot_dam_aux_snipe(MULTIPLY mult, monster_type *m_ptr, SPELL_IDX snipe_type)
395 {
396         monster_race *r_ptr = &r_info[m_ptr->r_idx];
397         bool seen = is_seen(m_ptr);
398
399         switch (snipe_type)
400         {
401         case SP_LITE:
402                 if (r_ptr->flags3 & (RF3_HURT_LITE))
403                 {
404                         MULTIPLY n = 20 + p_ptr->concent;
405                         if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
406                         if (mult < n) mult = n;
407                 }
408                 break;
409         case SP_FIRE:
410                 if (r_ptr->flagsr & RFR_IM_FIRE)
411                 {
412                         if (seen) r_ptr->r_flagsr |= RFR_IM_FIRE;
413                 }
414                 else
415                 {
416                         MULTIPLY n = 15 + (p_ptr->concent * 3);
417                         if (mult < n) mult = n;
418                 }
419                 break;
420         case SP_COLD:
421                 if (r_ptr->flagsr & RFR_IM_COLD)
422                 {
423                         if (seen) r_ptr->r_flagsr |= RFR_IM_COLD;
424                 }
425                 else
426                 {
427                         MULTIPLY n = 15 + (p_ptr->concent * 3);
428                         if (mult < n) mult = n;
429                 }
430                 break;
431         case SP_ELEC:
432                 if (r_ptr->flagsr & RFR_IM_ELEC)
433                 {
434                         if (seen) r_ptr->r_flagsr |= RFR_IM_ELEC;
435                 }
436                 else
437                 {
438                         MULTIPLY n = 18 + (p_ptr->concent * 4);
439                         if (mult < n) mult = n;
440                 }
441                 break;
442         case SP_KILL_WALL:
443                 if (r_ptr->flags3 & RF3_HURT_ROCK)
444                 {
445                         MULTIPLY n = 15 + (p_ptr->concent * 2);
446                         if (seen) r_ptr->r_flags3 |= RF3_HURT_ROCK;
447                         if (mult < n) mult = n;
448                 }
449                 else if (r_ptr->flags3 & RF3_NONLIVING)
450                 {
451                         MULTIPLY n = 15 + (p_ptr->concent * 2);
452                         if (seen) r_ptr->r_flags3 |= RF3_NONLIVING;
453                         if (mult < n) mult = n;
454                 }
455                 break;
456         case SP_EVILNESS:
457                 if (r_ptr->flags3 & RF3_GOOD)
458                 {
459                         MULTIPLY n = 15 + (p_ptr->concent * 4);
460                         if (seen) r_ptr->r_flags3 |= RF3_GOOD;
461                         if (mult < n) mult = n;
462                 }
463                 break;
464         case SP_HOLYNESS:
465                 if (r_ptr->flags3 & RF3_EVIL)
466                 {
467                         MULTIPLY n = 12 + (p_ptr->concent * 3);
468                         if (seen) r_ptr->r_flags3 |= RF3_EVIL;
469                         if (r_ptr->flags3 & (RF3_HURT_LITE))
470                         {
471                                 n += (p_ptr->concent * 3);
472                                 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
473                         }
474                         if (mult < n) mult = n;
475                 }
476                 break;
477         case SP_FINAL:
478                 if (mult < 50) mult = 50;
479                 break;
480         }
481
482         return (mult);
483 }
484
485
486 /*!
487  * @brief スナイパー技能の発動 /
488  * do_cmd_cast calls this function if the player's class is 'snipe'.
489  * @param spell 発動する特殊技能のID
490  * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
491  */
492 static bool cast_sniper_spell(int spell)
493 {
494         object_type *o_ptr = &inventory[INVEN_BOW];
495         SPELL_IDX snipe_type = SP_NONE;
496
497         if (o_ptr->tval != TV_BOW)
498         {
499                 msg_print(_("弓を装備していない!", "You wield no bow!"));
500                 return (FALSE);
501         }
502
503         /* spell code */
504         switch (spell)
505         {
506         case 0: /* Concentration */
507                 if (!snipe_concentrate()) return (FALSE);
508                 take_turn(p_ptr, 100);
509                 return (TRUE);
510         case 1: snipe_type = SP_LITE; break;
511         case 2: snipe_type = SP_AWAY; break;
512         case 3: snipe_type = SP_KILL_TRAP; break;
513         case 4: snipe_type = SP_FIRE; break;
514         case 5: snipe_type = SP_KILL_WALL; break;
515         case 6: snipe_type = SP_COLD; break;
516         case 7: snipe_type = SP_RUSH; break;
517         case 8: snipe_type = SP_PIERCE; break;
518         case 9: snipe_type = SP_EVILNESS; break;
519         case 10: snipe_type = SP_HOLYNESS; break;
520         case 11: snipe_type = SP_EXPLODE; break;
521         case 12: snipe_type = SP_DOUBLE; break;
522         case 13: snipe_type = SP_ELEC; break;
523         case 14: snipe_type = SP_NEEDLE; break;
524         case 15: snipe_type = SP_FINAL; break;
525         default:
526                 msg_print(_("なに?", "Zap?"));
527         }
528
529         command_cmd = 'f';
530         do_cmd_fire(snipe_type);
531
532         return (is_fired);
533 }
534
535 /*!
536  * @brief スナイパー技能コマンドのメインルーチン /
537  * @return なし
538  */
539 void do_cmd_snipe(void)
540 {
541         COMMAND_CODE n = 0;
542         bool            cast;
543
544         if (cmd_limit_confused(p_ptr)) return;
545
546         /* not if hullucinated */
547         if (p_ptr->image)
548         {
549                 msg_print(_("幻覚が見えて集中できない!", "You are too hallucinated!"));
550                 return;
551         }
552
553         /* not if stuned */
554         if (p_ptr->stun)
555         {
556                 msg_print(_("頭が朦朧としていて集中できない!", "You are too stuned!"));
557                 return;
558         }
559
560         /* get power */
561         if (!get_snipe_power(&n, FALSE)) return;
562
563         sound(SOUND_SHOOT);
564
565         /* Cast the spell */
566         cast = cast_sniper_spell(n);
567
568         if (!cast) return;
569         p_ptr->redraw |= (PR_HP | PR_MANA);
570
571         p_ptr->window |= (PW_PLAYER);
572         p_ptr->window |= (PW_SPELL);
573 }
574
575 /*!
576  * @brief スナイパー技能コマンドの表示 /
577  * @return なし
578  */
579 void do_cmd_snipe_browse(void)
580 {
581         COMMAND_CODE n = 0;
582         int j, line;
583         char temp[62 * 4];
584
585         screen_save();
586
587         while(1)
588         {
589                 /* get power */
590                 if (!get_snipe_power(&n, TRUE))
591                 {
592                         screen_load();
593                         return;
594                 }
595
596                 /* Clear lines, position cursor  (really should use strlen here) */
597                 Term_erase(12, 22, 255);
598                 Term_erase(12, 21, 255);
599                 Term_erase(12, 20, 255);
600                 Term_erase(12, 19, 255);
601                 Term_erase(12, 18, 255);
602
603                 roff_to_buf(snipe_tips[n], 62, temp, sizeof(temp));
604                 for(j = 0, line = 19; temp[j]; j += (1 + strlen(&temp[j])))
605                 {
606                         prt(&temp[j], line, 15);
607                         line++;
608                 }
609         }
610 }