OSDN Git Service

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