OSDN Git Service

65153255e35e7d38224699075e600df4a960724e
[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 "core.h"
11 #include "util.h"
12 #include "term.h"
13
14 #include "player-status.h"
15 #include "cmd-basic.h"
16 #include "snipe.h"
17 #include "monsterrace.h"
18
19 #define MAX_SNIPE_POWERS 16
20
21 /*! スナイパー技能情報のtypedef */
22 typedef struct snipe_power snipe_power;
23
24 /*! スナイパー技能情報の構造体 */
25 struct snipe_power
26 {
27         PLAYER_LEVEL min_lev;
28         MANA_POINT mana_cost;
29         concptr name;
30 };
31
32 /*! スナイパー技能の解説メッセージ */
33 static concptr const snipe_tips[MAX_SNIPE_POWERS] =
34 {
35 #ifdef JP
36         "精神を集中する。射撃の威力、精度が上がり、高度な射撃術が使用できるようになる。",
37         "光る矢を放つ。光に弱いモンスターに威力を発揮する。",
38         "射撃を行った後、短距離の瞬間移動を行う。",
39         "軌道上の罠をすべて無効にする低空飛行の矢を放つ。",
40         "火炎属性の矢を放つ。",
41         "壁を粉砕する矢を放つ。岩でできたモンスターと無生物のモンスターに威力を発揮する。",
42         "冷気属性の矢を放つ。",
43         "敵を突き飛ばす矢を放つ。",
44         "複数の敵を貫通する矢を放つ。",
45         "善良なモンスターに威力を発揮する矢を放つ。",
46         "邪悪なモンスターに威力を発揮する矢を放つ。",
47         "当たると爆発する矢を放つ。",
48         "2回射撃を行う。",
49         "電撃属性の矢を放つ。",
50         "敵の急所にめがけて矢を放つ。成功すると敵を一撃死させる。失敗すると1ダメージ。",
51         "全てのモンスターに高威力を発揮する矢を放つ。反動による副次効果を受ける。",
52 #else
53         "Concentrate your mind fot shooting.",
54         "Shot an allow with brightness.",
55         "Blink after shooting.",
56         "Shot an allow able to shatter traps.",
57         "Deals extra damege of fire.",
58         "Shot an allow able to shatter rocks.",
59         "Deals extra damege of ice.",
60         "An allow rushes away a target.",
61         "An allow pierces some monsters.",
62         "Deals more damage to good monsters.",
63         "Deals more damage to evil monsters.",
64         "An allow explodes when it hits a monster.",
65         "Shot allows twice.",
66         "Deals extra damege of lightning.",
67         "Deals quick death or 1 damage.",
68         "Deals great damage to all monsters, and some side effects to you.",
69 #endif
70 };
71
72 /*! スナイパー技能テーブル */
73 static snipe_power const snipe_powers[MAX_SNIPE_POWERS] =
74 {
75         /* Level gained,  cost,  name */
76 #ifdef JP
77         {  1,  0,  "精神集中" },
78         {  2,  1,  "フラッシュアロー" },
79         {  3,  1,  "シュート&アウェイ" },
80         {  5,  1,  "解除の矢" },
81         {  8,  2,  "火炎の矢" },
82         { 10,  2,  "岩砕き" },
83         { 13,  2,  "冷気の矢" },
84         { 18,  2,  "烈風弾"},
85         { 22,  3,  "貫通弾" },
86         { 25,  4,  "邪念弾"},
87         { 26,  4,  "破魔矢" },
88         { 30,  3,  "爆発の矢"},
89         { 32,  4,  "ダブルショット" },
90         { 36,  3,  "プラズマボルト" },
91         { 40,  3,  "ニードルショット" },
92         { 48,  7,  "セイントスターアロー" },
93 #else
94         {  1,  0,  "Concentration" },
95         {  2,  1,  "Flush Arrow" },
96         {  3,  1,  "Shoot & Away" },
97         {  5,  1,  "Disarm Shot" },
98         {  8,  2,  "Fire Shot" },
99         { 10,  2,  "Shatter Arrow" },
100         { 13,  2,  "Ice Shot" },
101         { 18,  2,  "Rushing Arrow"},
102         { 22,  3,  "Piercing Shot" },
103         { 25,  4,  "Evil Shot"},
104         { 26,  4,  "Holy Shot" },
105         { 30,  3,  "Missile"},
106         { 32,  4,  "Double Shot" },
107         { 36,  3,  "Plasma Bolt" },
108         { 40,  3,  "Needle Shot" },
109         { 48,  7,  "Saint Stars Arrow" },
110 #endif
111 };
112
113 /*! 
114  * @brief スナイパーの集中度加算
115  * @return 常にTRUEを返す
116  */
117 static bool snipe_concentrate(void)
118 {
119         if ((int)p_ptr->concent < (2 + (p_ptr->lev + 5) / 10)) p_ptr->concent++;
120
121         msg_format(_("集中した。(集中度 %d)", "You concentrate deeply. (lvl %d)"), p_ptr->concent);
122         reset_concent = FALSE;
123
124         p_ptr->update |= (PU_BONUS | PU_MONSTERS);
125         p_ptr->redraw |= (PR_STATUS);
126         return (TRUE);
127 }
128
129 /*! 
130  * @brief スナイパーの集中度リセット
131  * @param msg TRUEならばメッセージを表示する
132  * @return なし
133  */
134 void reset_concentration(bool msg)
135 {
136         if (msg)
137         {
138                 msg_print(_("集中力が途切れてしまった。", "Stop concentrating."));
139         }
140
141         p_ptr->concent = 0;
142         reset_concent = FALSE;
143
144         p_ptr->update |= (PU_BONUS | PU_MONSTERS);
145         p_ptr->redraw |= (PR_STATUS);
146 }
147
148 /*! 
149  * @brief スナイパーの集中度によるダメージボーナスを加算する
150  * @param tdam 算出中のダメージ
151  * @return 集中度修正を加えたダメージ
152  */
153 int boost_concentration_damage(int tdam)
154 {
155         tdam *= (10 + p_ptr->concent);
156         tdam /= 10;
157
158         return (tdam);
159 }
160
161 /*! 
162  * @brief スナイパーの技能リストを表示する
163  * @return なし
164  */
165 void display_snipe_list(void)
166 {
167         int i;
168         TERM_LEN y = 1;
169         TERM_LEN x = 1;
170         PLAYER_LEVEL plev = p_ptr->lev;
171         snipe_power     spell;
172         char            psi_desc[80];
173
174         /* Display a list of spells */
175         prt("", y, x);
176         put_str(_("名前", "Name"), y, x + 5);
177         put_str(_("Lv   MP", "Lv Mana"), y, x + 35);
178
179         for (i = 0; i < MAX_SNIPE_POWERS; i++)
180         {
181                 /* Access the available spell */
182                 spell = snipe_powers[i];
183                 if (spell.min_lev > plev) continue;
184                 if (spell.mana_cost > (int)p_ptr->concent) continue;
185
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         TERM_LEN y = 1;
218         TERM_LEN 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         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                                 redraw = TRUE;
287                                 if (!only_browse) screen_save();
288
289                                 /* Display a list of spells */
290                                 prt("", y, x);
291                                 put_str(_("名前", "Name"), y, x + 5);
292                                 if (only_browse) put_str(_("Lv   集中度", "Lv Pow"), y, x + 35);
293
294                                 /* Dump the spells */
295                                 for (i = 0; i < MAX_SNIPE_POWERS; i++)
296                                 {
297                                         Term_erase(x, y + i + 1, 255);
298
299                                         /* Access the spell */
300                                         spell = snipe_powers[i];
301                                         if (spell.min_lev > plev) continue;
302                                         if (!only_browse && (spell.mana_cost > (int)p_ptr->concent)) continue;
303
304                                         /* Dump the spell --(-- */
305                                         if (only_browse)
306                                                 sprintf(psi_desc, "  %c) %-30s%2d %4d",
307                                                         I2A(i), spell.name,     spell.min_lev, spell.mana_cost);
308                                         else
309                                                 sprintf(psi_desc, "  %c) %-30s", I2A(i), spell.name);
310                                         prt(psi_desc, y + i + 1, x);
311                                 }
312
313                                 /* Clear the bottom line */
314                                 prt("", y + i + 1, x);
315                         }
316
317                         /* Hide the list */
318                         else
319                         {
320                                 /* Hide list */
321                                 redraw = FALSE;
322                                 if (!only_browse) screen_load();
323                         }
324
325                         /* Redo asking */
326                         continue;
327                 }
328
329                 /* Note verify */
330                 ask = isupper(choice);
331
332                 /* Lowercase */
333                 if (ask) choice = (char)tolower(choice);
334
335                 /* Extract request */
336                 i = (islower(choice) ? A2I(choice) : -1);
337
338                 /* Totally Illegal */
339                 if ((i < 0) || (i > num) || 
340                         (!only_browse &&(snipe_powers[i].mana_cost > (int)p_ptr->concent)))
341                 {
342                         bell();
343                         continue;
344                 }
345
346                 /* Save the spell index */
347                 spell = snipe_powers[i];
348
349                 /* Verify it */
350                 if (ask)
351                 {
352                         char tmp_val[160];
353
354                         /* Prompt */
355                         (void) strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), snipe_powers[i].name);
356
357                         /* Belay that order */
358                         if (!get_check(tmp_val)) continue;
359                 }
360
361                 /* Stop the loop */
362                 flag = TRUE;
363         }
364         if (redraw && !only_browse) screen_load();
365
366         p_ptr->window |= (PW_SPELL);
367         handle_stuff();
368
369         /* Abort if needed */
370         if (!flag) return (FALSE);
371
372         /* Save the choice */
373         (*sn) = i;
374
375         repeat_push(*sn);
376
377         /* Success */
378         return (TRUE);
379 }
380
381 /*!
382  * @brief スナイバー技能のスレイ倍率計算を行う /
383  * Calcurate magnification of snipe technics
384  * @param mult スナイバー技能のスレイ効果以前に算出している多要素の倍率(/10倍)
385  * @param m_ptr 目標となるモンスターの構造体参照ポインタ
386  * @return スレイの倍率(/10倍)
387  */
388 MULTIPLY tot_dam_aux_snipe(MULTIPLY mult, monster_type *m_ptr, SPELL_IDX snipe_type)
389 {
390         monster_race *r_ptr = &r_info[m_ptr->r_idx];
391         bool seen = is_seen(m_ptr);
392
393         switch (snipe_type)
394         {
395         case SP_LITE:
396                 if (r_ptr->flags3 & (RF3_HURT_LITE))
397                 {
398                         MULTIPLY n = 20 + p_ptr->concent;
399                         if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
400                         if (mult < n) mult = n;
401                 }
402                 break;
403         case SP_FIRE:
404                 if (r_ptr->flagsr & RFR_IM_FIRE)
405                 {
406                         if (seen) r_ptr->r_flagsr |= RFR_IM_FIRE;
407                 }
408                 else
409                 {
410                         MULTIPLY n = 15 + (p_ptr->concent * 3);
411                         if (mult < n) mult = n;
412                 }
413                 break;
414         case SP_COLD:
415                 if (r_ptr->flagsr & RFR_IM_COLD)
416                 {
417                         if (seen) r_ptr->r_flagsr |= RFR_IM_COLD;
418                 }
419                 else
420                 {
421                         MULTIPLY n = 15 + (p_ptr->concent * 3);
422                         if (mult < n) mult = n;
423                 }
424                 break;
425         case SP_ELEC:
426                 if (r_ptr->flagsr & RFR_IM_ELEC)
427                 {
428                         if (seen) r_ptr->r_flagsr |= RFR_IM_ELEC;
429                 }
430                 else
431                 {
432                         MULTIPLY n = 18 + (p_ptr->concent * 4);
433                         if (mult < n) mult = n;
434                 }
435                 break;
436         case SP_KILL_WALL:
437                 if (r_ptr->flags3 & RF3_HURT_ROCK)
438                 {
439                         MULTIPLY n = 15 + (p_ptr->concent * 2);
440                         if (seen) r_ptr->r_flags3 |= RF3_HURT_ROCK;
441                         if (mult < n) mult = n;
442                 }
443                 else if (r_ptr->flags3 & RF3_NONLIVING)
444                 {
445                         MULTIPLY n = 15 + (p_ptr->concent * 2);
446                         if (seen) r_ptr->r_flags3 |= RF3_NONLIVING;
447                         if (mult < n) mult = n;
448                 }
449                 break;
450         case SP_EVILNESS:
451                 if (r_ptr->flags3 & RF3_GOOD)
452                 {
453                         MULTIPLY n = 15 + (p_ptr->concent * 4);
454                         if (seen) r_ptr->r_flags3 |= RF3_GOOD;
455                         if (mult < n) mult = n;
456                 }
457                 break;
458         case SP_HOLYNESS:
459                 if (r_ptr->flags3 & RF3_EVIL)
460                 {
461                         MULTIPLY n = 12 + (p_ptr->concent * 3);
462                         if (seen) r_ptr->r_flags3 |= RF3_EVIL;
463                         if (r_ptr->flags3 & (RF3_HURT_LITE))
464                         {
465                                 n += (p_ptr->concent * 3);
466                                 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
467                         }
468                         if (mult < n) mult = n;
469                 }
470                 break;
471         case SP_FINAL:
472                 if (mult < 50) mult = 50;
473                 break;
474         }
475
476         return (mult);
477 }
478
479
480 /*!
481  * @brief スナイパー技能の発動 /
482  * do_cmd_cast calls this function if the player's class is 'snipe'.
483  * @param spell 発動する特殊技能のID
484  * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
485  */
486 static bool cast_sniper_spell(int spell)
487 {
488         object_type *o_ptr = &p_ptr->inventory_list[INVEN_BOW];
489         SPELL_IDX snipe_type = SP_NONE;
490
491         if (o_ptr->tval != TV_BOW)
492         {
493                 msg_print(_("弓を装備していない!", "You wield no bow!"));
494                 return (FALSE);
495         }
496
497         /* spell code */
498         switch (spell)
499         {
500         case 0: /* Concentration */
501                 if (!snipe_concentrate()) return (FALSE);
502                 take_turn(p_ptr, 100);
503                 return (TRUE);
504         case 1: snipe_type = SP_LITE; break;
505         case 2: snipe_type = SP_AWAY; break;
506         case 3: snipe_type = SP_KILL_TRAP; break;
507         case 4: snipe_type = SP_FIRE; break;
508         case 5: snipe_type = SP_KILL_WALL; break;
509         case 6: snipe_type = SP_COLD; break;
510         case 7: snipe_type = SP_RUSH; break;
511         case 8: snipe_type = SP_PIERCE; break;
512         case 9: snipe_type = SP_EVILNESS; break;
513         case 10: snipe_type = SP_HOLYNESS; break;
514         case 11: snipe_type = SP_EXPLODE; break;
515         case 12: snipe_type = SP_DOUBLE; break;
516         case 13: snipe_type = SP_ELEC; break;
517         case 14: snipe_type = SP_NEEDLE; break;
518         case 15: snipe_type = SP_FINAL; break;
519         default:
520                 msg_print(_("なに?", "Zap?"));
521         }
522
523         command_cmd = 'f';
524         do_cmd_fire(snipe_type);
525
526         return (p_ptr->is_fired);
527 }
528
529 /*!
530  * @brief スナイパー技能コマンドのメインルーチン /
531  * @return なし
532  */
533 void do_cmd_snipe(void)
534 {
535         COMMAND_CODE n = 0;
536         bool cast;
537
538         if(cmd_limit_confused(p_ptr)) return;
539         if(cmd_limit_image(p_ptr)) return;
540         if(cmd_limit_stun(p_ptr)) return;
541
542         if (!get_snipe_power(&n, FALSE)) return;
543
544         sound(SOUND_SHOOT);
545         cast = cast_sniper_spell(n);
546
547         if (!cast) return;
548         p_ptr->redraw |= (PR_HP | PR_MANA);
549         p_ptr->window |= (PW_PLAYER);
550         p_ptr->window |= (PW_SPELL);
551 }
552
553 /*!
554  * @brief スナイパー技能コマンドの表示 /
555  * @return なし
556  */
557 void do_cmd_snipe_browse(void)
558 {
559         COMMAND_CODE n = 0;
560         int j, line;
561         char temp[62 * 4];
562
563         screen_save();
564
565         while(1)
566         {
567                 if (!get_snipe_power(&n, TRUE))
568                 {
569                         screen_load();
570                         return;
571                 }
572
573                 /* Clear lines, position cursor  (really should use strlen here) */
574                 Term_erase(12, 22, 255);
575                 Term_erase(12, 21, 255);
576                 Term_erase(12, 20, 255);
577                 Term_erase(12, 19, 255);
578                 Term_erase(12, 18, 255);
579
580                 roff_to_buf(snipe_tips[n], 62, temp, sizeof(temp));
581                 for(j = 0, line = 19; temp[j]; j += (1 + strlen(&temp[j])))
582                 {
583                         prt(&temp[j], line, 15);
584                         line++;
585                 }
586         }
587 }