OSDN Git Service

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