OSDN Git Service

[Refactor] #37353 混乱時のコマンド制限処理を cmd_limit_confused() に統合。 / Integrate command limitat...
[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         concptr name;
22 };
23
24 /*! スナイパー技能の解説メッセージ */
25 static concptr 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         p_ptr->update |= (PU_BONUS | PU_MONSTERS);
117         p_ptr->redraw |= (PR_STATUS);
118         return (TRUE);
119 }
120
121 /*! 
122  * @brief スナイパーの集中度リセット
123  * @param msg TRUEならばメッセージを表示する
124  * @return なし
125  */
126 void reset_concentration(bool msg)
127 {
128         if (msg)
129         {
130                 msg_print(_("集中力が途切れてしまった。", "Stop concentrating."));
131         }
132
133         p_ptr->concent = 0;
134         reset_concent = FALSE;
135
136         p_ptr->update |= (PU_BONUS | PU_MONSTERS);
137         p_ptr->redraw |= (PR_STATUS);
138 }
139
140 /*! 
141  * @brief スナイパーの集中度によるダメージボーナスを加算する
142  * @param tdam 算出中のダメージ
143  * @return 集中度修正を加えたダメージ
144  */
145 int boost_concentration_damage(int tdam)
146 {
147         tdam *= (10 + p_ptr->concent);
148         tdam /= 10;
149
150         return (tdam);
151 }
152
153 /*! 
154  * @brief スナイパーの技能リストを表示する
155  * @return なし
156  */
157 void display_snipe_list(void)
158 {
159         int             i;
160         int             y = 1;
161         int             x = 1;
162         PLAYER_LEVEL plev = p_ptr->lev;
163         snipe_power     spell;
164         char            psi_desc[80];
165
166         /* Display a list of spells */
167         prt("", y, x);
168 #ifdef JP
169         put_str("名前", y, x + 5);
170         put_str("Lv   MP", y, x + 35);
171 #else
172         put_str("Name", y, x + 5);
173         put_str("Lv Mana", y, x + 35);
174 #endif
175
176         /* Dump the spells */
177         for (i = 0; i < MAX_SNIPE_POWERS; i++)
178         {
179                 /* Access the available spell */
180                 spell = snipe_powers[i];
181                 if (spell.min_lev > plev) continue;
182                 if (spell.mana_cost > (int)p_ptr->concent) continue;
183
184                 /* Dump the spell */
185                 sprintf(psi_desc, "  %c) %-30s%2d %4d",
186                         I2A(i), spell.name, spell.min_lev, spell.mana_cost);
187
188                 Term_putstr(x, y + i + 1, -1, TERM_WHITE, psi_desc);
189         }
190         return;
191 }
192
193
194 /*! 
195  * @brief スナイパー技能を選択する
196  * @param sn 選択した特殊技能ID、キャンセルの場合-1、不正な選択の場合-2を返す
197  * @param only_browse 一覧を見るだけの場合TRUEを返す
198  * @return 発動可能な魔法を選択した場合TRUE、キャンセル処理か不正な選択が行われた場合FALSEを返す。
199  * Allow user to choose a mindcrafter power.\n
200  *\n
201  * If a valid spell is chosen, saves it in '*sn' and returns TRUE\n
202  * If the user hits escape, returns FALSE, and set '*sn' to -1\n
203  * If there are no legal choices, returns FALSE, and sets '*sn' to -2\n
204  *\n
205  * The "prompt" should be "cast", "recite", or "study"\n
206  * The "known" should be TRUE for cast/pray, FALSE for study\n
207  *\n
208  * nb: This function has a (trivial) display bug which will be obvious\n
209  * when you run it. It's probably easy to fix but I haven't tried,\n
210  * sorry.\n
211  */
212 static int get_snipe_power(COMMAND_CODE *sn, bool only_browse)
213 {
214         COMMAND_CODE i;
215         int             num = 0;
216         int             y = 1;
217         int             x = 20;
218         PLAYER_LEVEL plev = p_ptr->lev;
219         int             ask;
220         char            choice;
221         char            out_val[160];
222         concptr            p = _("射撃術", "power");
223         snipe_power     spell;
224         bool            flag, redraw;
225
226         repeat_push(*sn);
227
228         /* Assume cancelled */
229         *sn = (-1);
230
231         /* Repeat previous command */
232         /* Get the spell, if available */
233         if (repeat_pull(sn))
234         {
235                 /* Verify the spell */
236                 if ((snipe_powers[*sn].min_lev <= plev) && (snipe_powers[*sn].mana_cost <= (int)p_ptr->concent))
237                 {
238                         /* Success */
239                         return (TRUE);
240                 }
241         }
242
243         /* Nothing chosen yet */
244         flag = FALSE;
245
246         /* No redraw yet */
247         redraw = FALSE;
248
249         for (i = 0; i < MAX_SNIPE_POWERS; i++)
250         {
251                 if ((snipe_powers[i].min_lev <= plev) &&
252                         ((only_browse) || (snipe_powers[i].mana_cost <= (int)p_ptr->concent)))
253                 {
254                         num = i;
255                 }
256         }
257
258         /* Build a prompt (accept all spells) */
259         if (only_browse)
260         {
261                 (void)strnfmt(out_val, 78, 
262                                         _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
263                                         p, I2A(0), I2A(num), p);
264         }
265         else
266         {
267                 (void)strnfmt(out_val, 78, 
268                                         _("(%^s %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
269                                         p, I2A(0), I2A(num), p);
270         }
271
272         /* Get a spell from the user */
273         choice = always_show_list ? ESCAPE : 1;
274         while (!flag)
275         {
276                 if(choice == ESCAPE) choice = ' ';
277                 else if( !get_com(out_val, &choice, FALSE) )break; 
278
279                 /* Request redraw */
280                 if ((choice == ' ') || (choice == '*') || (choice == '?'))
281                 {
282                         /* Show the list */
283                         if (!redraw)
284                         {
285                                 char psi_desc[80];
286
287                                 /* Show list */
288                                 redraw = TRUE;
289                                 if (!only_browse) screen_save();
290
291                                 /* Display a list of spells */
292                                 prt("", y, x);
293 #ifdef JP
294                                 put_str("名前", y, x + 5);
295                                 if (only_browse) put_str("Lv   集中度", y, x + 35);
296 #else
297                                 put_str("Name", y, x + 5);
298                                 if (only_browse) put_str("Lv Pow", y, x + 35);
299 #endif
300
301                                 /* Dump the spells */
302                                 for (i = 0; i < MAX_SNIPE_POWERS; i++)
303                                 {
304                                         Term_erase(x, y + i + 1, 255);
305
306                                         /* Access the spell */
307                                         spell = snipe_powers[i];
308                                         if (spell.min_lev > plev) continue;
309                                         if (!only_browse && (spell.mana_cost > (int)p_ptr->concent)) continue;
310
311                                         /* Dump the spell --(-- */
312                                         if (only_browse)
313                                                 sprintf(psi_desc, "  %c) %-30s%2d %4d",
314                                                         I2A(i), spell.name,     spell.min_lev, spell.mana_cost);
315                                         else
316                                                 sprintf(psi_desc, "  %c) %-30s", I2A(i), spell.name);
317                                         prt(psi_desc, y + i + 1, x);
318                                 }
319
320                                 /* Clear the bottom line */
321                                 prt("", y + i + 1, x);
322                         }
323
324                         /* Hide the list */
325                         else
326                         {
327                                 /* Hide list */
328                                 redraw = FALSE;
329                                 if (!only_browse) screen_load();
330                         }
331
332                         /* Redo asking */
333                         continue;
334                 }
335
336                 /* Note verify */
337                 ask = isupper(choice);
338
339                 /* Lowercase */
340                 if (ask) choice = (char)tolower(choice);
341
342                 /* Extract request */
343                 i = (islower(choice) ? A2I(choice) : -1);
344
345                 /* Totally Illegal */
346                 if ((i < 0) || (i > num) || 
347                         (!only_browse &&(snipe_powers[i].mana_cost > (int)p_ptr->concent)))
348                 {
349                         bell();
350                         continue;
351                 }
352
353                 /* Save the spell index */
354                 spell = snipe_powers[i];
355
356                 /* Verify it */
357                 if (ask)
358                 {
359                         char tmp_val[160];
360
361                         /* Prompt */
362                         (void) strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), snipe_powers[i].name);
363
364                         /* Belay that order */
365                         if (!get_check(tmp_val)) continue;
366                 }
367
368                 /* Stop the loop */
369                 flag = TRUE;
370         }
371         if (redraw && !only_browse) screen_load();
372
373         p_ptr->window |= (PW_SPELL);
374         handle_stuff();
375
376         /* Abort if needed */
377         if (!flag) return (FALSE);
378
379         /* Save the choice */
380         (*sn) = i;
381
382         repeat_push(*sn);
383
384         /* Success */
385         return (TRUE);
386 }
387
388 /*!
389  * @brief スナイバー技能のスレイ倍率計算を行う /
390  * Calcurate magnification of snipe technics
391  * @param mult スナイバー技能のスレイ効果以前に算出している多要素の倍率(/10倍)
392  * @param m_ptr 目標となるモンスターの構造体参照ポインタ
393  * @return スレイの倍率(/10倍)
394  */
395 int tot_dam_aux_snipe(int mult, monster_type *m_ptr)
396 {
397         monster_race *r_ptr = &r_info[m_ptr->r_idx];
398         bool seen = is_seen(m_ptr);
399
400         switch (snipe_type)
401         {
402         case SP_LITE:
403                 if (r_ptr->flags3 & (RF3_HURT_LITE))
404                 {
405                         int n = 20 + p_ptr->concent;
406                         if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
407                         if (mult < n) mult = n;
408                 }
409                 break;
410         case SP_FIRE:
411                 if (r_ptr->flagsr & RFR_IM_FIRE)
412                 {
413                         if (seen) r_ptr->r_flagsr |= RFR_IM_FIRE;
414                 }
415                 else
416                 {
417                         int n = 15 + (p_ptr->concent * 3);
418                         if (mult < n) mult = n;
419                 }
420                 break;
421         case SP_COLD:
422                 if (r_ptr->flagsr & RFR_IM_COLD)
423                 {
424                         if (seen) r_ptr->r_flagsr |= RFR_IM_COLD;
425                 }
426                 else
427                 {
428                         int n = 15 + (p_ptr->concent * 3);
429                         if (mult < n) mult = n;
430                 }
431                 break;
432         case SP_ELEC:
433                 if (r_ptr->flagsr & RFR_IM_ELEC)
434                 {
435                         if (seen) r_ptr->r_flagsr |= RFR_IM_ELEC;
436                 }
437                 else
438                 {
439                         int n = 18 + (p_ptr->concent * 4);
440                         if (mult < n) mult = n;
441                 }
442                 break;
443         case SP_KILL_WALL:
444                 if (r_ptr->flags3 & RF3_HURT_ROCK)
445                 {
446                         int n = 15 + (p_ptr->concent * 2);
447                         if (seen) r_ptr->r_flags3 |= RF3_HURT_ROCK;
448                         if (mult < n) mult = n;
449                 }
450                 else if (r_ptr->flags3 & RF3_NONLIVING)
451                 {
452                         int n = 15 + (p_ptr->concent * 2);
453                         if (seen) r_ptr->r_flags3 |= RF3_NONLIVING;
454                         if (mult < n) mult = n;
455                 }
456                 break;
457         case SP_EVILNESS:
458                 if (r_ptr->flags3 & RF3_GOOD)
459                 {
460                         int n = 15 + (p_ptr->concent * 4);
461                         if (seen) r_ptr->r_flags3 |= RF3_GOOD;
462                         if (mult < n) mult = n;
463                 }
464                 break;
465         case SP_HOLYNESS:
466                 if (r_ptr->flags3 & RF3_EVIL)
467                 {
468                         int n = 12 + (p_ptr->concent * 3);
469                         if (seen) r_ptr->r_flags3 |= RF3_EVIL;
470                         if (r_ptr->flags3 & (RF3_HURT_LITE))
471                         {
472                                 n += (p_ptr->concent * 3);
473                                 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
474                         }
475                         if (mult < n) mult = n;
476                 }
477                 break;
478         case SP_FINAL:
479                 if (mult < 50) mult = 50;
480                 break;
481         }
482
483         return (mult);
484 }
485
486
487 /*!
488  * @brief スナイパー技能の発動 /
489  * do_cmd_cast calls this function if the player's class is 'snipe'.
490  * @param spell 発動する特殊技能のID
491  * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
492  */
493 static bool cast_sniper_spell(int spell)
494 {
495         object_type *o_ptr = &inventory[INVEN_BOW];
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                 p_ptr->energy_use = 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();
531         snipe_type = 0;
532
533         return (is_fired);
534 }
535
536 /*!
537  * @brief スナイパー技能コマンドのメインルーチン /
538  * @return なし
539  */
540 void do_cmd_snipe(void)
541 {
542         COMMAND_CODE n = 0;
543         bool            cast;
544
545         if (cmd_limit_confused(p_ptr)) return;
546
547         /* not if hullucinated */
548         if (p_ptr->image)
549         {
550                 msg_print(_("幻覚が見えて集中できない!", "You are too hallucinated!"));
551                 return;
552         }
553
554         /* not if stuned */
555         if (p_ptr->stun)
556         {
557                 msg_print(_("頭が朦朧としていて集中できない!", "You are too stuned!"));
558                 return;
559         }
560
561         /* get power */
562         if (!get_snipe_power(&n, FALSE)) return;
563
564         sound(SOUND_SHOOT);
565
566         /* Cast the spell */
567         cast = cast_sniper_spell(n);
568
569         if (!cast) return;
570         p_ptr->redraw |= (PR_HP | PR_MANA);
571
572         p_ptr->window |= (PW_PLAYER);
573         p_ptr->window |= (PW_SPELL);
574 }
575
576 /*!
577  * @brief スナイパー技能コマンドの表示 /
578  * @return なし
579  */
580 void do_cmd_snipe_browse(void)
581 {
582         COMMAND_CODE n = 0;
583         int j, line;
584         char temp[62 * 4];
585
586         screen_save();
587
588         while(1)
589         {
590                 /* get power */
591                 if (!get_snipe_power(&n, TRUE))
592                 {
593                         screen_load();
594                         return;
595                 }
596
597                 /* Clear lines, position cursor  (really should use strlen here) */
598                 Term_erase(12, 22, 255);
599                 Term_erase(12, 21, 255);
600                 Term_erase(12, 20, 255);
601                 Term_erase(12, 19, 255);
602                 Term_erase(12, 18, 255);
603
604                 roff_to_buf(snipe_tips[n], 62, temp, sizeof(temp));
605                 for(j = 0, line = 19; temp[j]; j += (1 + strlen(&temp[j])))
606                 {
607                         prt(&temp[j], line, 15);
608                         line++;
609                 }
610         }
611 }