OSDN Git Service

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